Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions frontend/src/components/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(({ className, type,
type={type}
className={cn(
'text-foreground flex h-9 w-full rounded-md border border-border bg-input px-3 py-1 text-sm shadow-xs transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
pyconStyles &&
'bg-pycon-dirty-white text-pycon-dark-blue font-inter text-base md:text-lg h-13 md:h-14 px-5 py-3.5 rounded-2xl border-2 border-transparent focus-visible:border-pycon-teal/40 focus-visible:ring-2 focus-visible:ring-pycon-teal/10 placeholder:text-pycon-lavender/60 placeholder:font-normal placeholder:font-inter shadow-xs',
pyconStyles && 'border-none shadow-none bg-pycon-dirty-white placeholder:text-pycon-lavender text-pycon-violet font-nunito',
className
)}
ref={ref}
Expand Down
5 changes: 2 additions & 3 deletions frontend/src/components/RichContent/RichTextContent.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import type { Content } from '@tiptap/react';
import { describe, expect, it } from 'vitest';
import { sanitizeRichContent, sanitizeRichHtmlContent } from './RichTextContent';
import type { Content } from '@tiptap/react';

describe('RichTextContent XSS protections', () => {
it('sanitizes malicious HTML strings', () => {
const maliciousHtml =
'<p>Safe text</p><img src="x" onerror="alert(1)" /><script>alert("xss")</script><a href="javascript:alert(1)">click</a>';
const maliciousHtml = '<p>Safe text</p><img src="x" onerror="alert(1)" /><script>alert("xss")</script><a href="javascript:alert(1)">click</a>';

const sanitizedHtml = sanitizeRichHtmlContent(maliciousHtml).toLowerCase();

Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/RichContent/RichTextContent.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import DOMPurify from 'dompurify';
import { cn } from '@/utils/classes';
import './RichContent.css';
import Placeholder from '@tiptap/extension-placeholder';
import TextAlign from '@tiptap/extension-text-align';
import { Content, EditorContent, useEditor } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
import DOMPurify from 'dompurify';

interface RichTextContentProps {
content: Content;
Expand Down Expand Up @@ -61,4 +61,4 @@ function RichTextContent({ content, className }: RichTextContentProps) {
return <EditorContent editor={editor} />;
}

export default RichTextContent;
export default RichTextContent;
9 changes: 1 addition & 8 deletions frontend/src/model/evaluations.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
type QuestionType =
| 'text_short'
| 'text_long'
| 'multiple_choice_dropdown'
| 'multiple_choice'
| 'multiple_choice_dropdown'
| 'multiple_answers'
| 'slider'
| 'radio_buttons';
'text_short' | 'text_long' | 'multiple_choice_dropdown' | 'multiple_choice' | 'multiple_choice_dropdown' | 'multiple_answers' | 'slider' | 'radio_buttons';

export interface QuestionConfigItem {
questionType: QuestionType;
Expand Down
33 changes: 16 additions & 17 deletions frontend/src/model/preregistrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,22 @@ import { Registration } from './registrations';

export type AcceptanceStatus = 'PENDING' | 'ACCEPTED' | 'REJECTED';

export interface PreRegistration
extends Omit<
Registration,
| 'certificateClaimed'
| 'amountPaid'
| 'paymentId'
| 'registrationId'
| 'referenceNumber'
| 'gcashPayment'
| 'gcashPaymentUrl'
| 'discountCode'
| 'type'
| 'certificateGenerated'
| 'certificateClaimed'
| 'certificateImgObjectKey'
| 'certificatePdfObjectKey'
> {
export interface PreRegistration extends Omit<
Registration,
| 'certificateClaimed'
| 'amountPaid'
| 'paymentId'
| 'registrationId'
| 'referenceNumber'
| 'gcashPayment'
| 'gcashPaymentUrl'
| 'discountCode'
| 'type'
| 'certificateGenerated'
| 'certificateClaimed'
| 'certificateImgObjectKey'
| 'certificatePdfObjectKey'
> {
type: 'preregistration';
acceptanceStatus: AcceptanceStatus;
preRegistrationId: string;
Expand Down
9 changes: 1 addition & 8 deletions frontend/src/model/pycon/evaluations.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
type QuestionType =
| 'text_short'
| 'text_long'
| 'multiple_choice_dropdown'
| 'multiple_choice'
| 'multiple_choice_dropdown'
| 'multiple_answers'
| 'slider'
| 'radio_buttons';
'text_short' | 'text_long' | 'multiple_choice_dropdown' | 'multiple_choice' | 'multiple_choice_dropdown' | 'multiple_answers' | 'slider' | 'radio_buttons';

export interface QuestionConfigItem {
questionType: QuestionType;
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/model/pycon/registrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ export const mapCreateRegistrationDataForPayment = (registration: RegisterFormVa
export const mapCreateRegistrationValues = (registration: RegisterFormValues, eventId: string, email?: string): CreateRegistration => ({
...mapCreateRegistrationDataForPayment(registration, eventId, email),
amountPaid: roundUpToTwoDecimals(registration.total),
transactionId: registration.transactionId || `price-${registration.total}-${registration.discountCode || 'null'}-${crypto.randomUUID().slice(0, 5).toUpperCase()}`
transactionId:
registration.transactionId || `price-${registration.total}-${registration.discountCode || 'null'}-${crypto.randomUUID().slice(0, 5).toUpperCase()}`
});

export type UpdateRegistration = Omit<Registration, 'registrationId' | 'amountPaid' | 'transactionId' | 'discountCode' | 'createDate' | 'updateDate'>;
Expand Down
20 changes: 9 additions & 11 deletions frontend/src/pages/client/pycon/register/Register.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,28 +90,26 @@ const Register: FC = () => {

return (
<section
className={cn(
'flex flex-col grow items-center px-4 h-full w-full text-pycon-custard font-nunito max-w-6xl mx-auto',
currentStep.id === 'Success' && 'grow-0'
)}
className={cn('flex flex-col grow px-4 h-full w-full text-pycon-custard font-nunito max-w-6xl mx-auto', currentStep.id === 'Success' && 'grow-0')}
>
{currentStep.id === 'Miscellaneous' && <h5 className="!font-inter uppercase opacity-60 font-bold tracking-widest"> Registration </h5>}
<div className="w-full h-full flex flex-col space-y-4 grow">
<FormProvider {...form}>
{showStepper && !shouldBeVertical && (
<div className="w-full my-2">
<Stepper orientation="horizontal" steps={STEPS} currentStep={currentStep} stepsToExclude={[STEP_SUCCESS]} />
</div>
{currentStep.id !== 'EventDetails' && currentStep.id !== 'Success' && (
<>
<h1 className="text-xl">{currentStep.title}</h1>
{currentStep.id === 'Miscellaneous' && (
<p className="text-[#072E4766] !font-inter font-normal text-sm"> A few more things to help us make the event better for you. </p>
)}
</>
)}

{currentStep.id !== 'EventDetails' && currentStep.id !== 'Success' && <h1 className="text-xl">{currentStep.title}</h1>}

<div className="flex flex-col md:flex-row w-full h-full grow">
{showStepper && shouldBeVertical && (
<div className="my-8 h-[700px]">
<Stepper orientation="vertical" steps={STEPS} currentStep={currentStep} stepsToExclude={[STEP_SUCCESS]} />
</div>
)}

<div className={cn('space-y-4 grow', currentStep.id !== 'EventDetails' && currentStep.id !== 'Success' && shouldBeVertical && 'ms-[20vw] p-8')}>
{currentStep.id === 'EventDetails' && <EventDetails event={eventInfo} />}
{currentStep.id === 'BasicInfo' && <BasicInfoStep />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,44 @@ interface Props {

const MiscellaneousStep: FC<Props> = () => {
return (
<>
<div className="flex flex-col md:flex-row w-full gap-4">
<section className="text-pycon-dark-blue bg-white/50 rounded-[40px] p-10">
<div className="flex flex-col w-full gap-4">
<p className="font-inter font-bold uppercase text-pycon-dark-blue/40 text-sm">Community</p>
<FormItem name="communityInvolvement">
{({ field }) => (
<div className="flex flex-col gap-1 grow md:basis-1/2">
<FormLabel>Are you a member of any local tech community?</FormLabel>
<RadioGroup onValueChange={(value) => field.onChange(Boolean(value))} value={field.value} className="flex flex-wrap gap-4 py-3 mt-auto">
<div className="flex items-center space-x-2">
<RadioGroupItem pyconStyles id="communityInvolvement-yes" checked={!!field.value} value={'true'} />
<Label htmlFor={`communityInvolvement-yes`}>Yes</Label>

<RadioGroupItem pyconStyles id="communityInvolvement-no" checked={!field.value} value="" />
<Label htmlFor={`communityInvolvement-no`}>No</Label>
<div className="flex flex-col gap-1 grow md:basis-1/2 bg-pycon-dirty-white p-6 rounded-[38.13px]">
<Label htmlFor="communityInvolvement-yes" aria-required>
Are you a member of any local tech community?
<span className="text-pycon-orange">&nbsp;*</span>
</Label>
<RadioGroup
onValueChange={(val) => field.onChange(val === 'true')}
value={typeof field.value === 'boolean' ? String(field.value) : undefined}
className="flex flex-wrap gap-4 py-3 mt-auto"
>
<div className="flex items-center gap-6">
<div className="flex items-center space-x-2">
<RadioGroupItem
pyconStyles
id="communityInvolvement-yes"
value="true"
className="border-pycon-dark-blue/30 bg-transparent cursor-pointer"
/>
<Label htmlFor="communityInvolvement-yes" className="cursor-pointer font-medium text-pycon-dark-blue">
Yes
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem
pyconStyles
id="communityInvolvement-no"
value="false"
className="border-pycon-dark-blue/30 bg-transparent cursor-pointer"
/>
<Label htmlFor="communityInvolvement-no" className="cursor-pointer font-medium text-pycon-dark-blue">
No
</Label>
</div>
</div>
</RadioGroup>
<FormError />
Expand All @@ -39,15 +64,28 @@ const MiscellaneousStep: FC<Props> = () => {

<FormItem name="futureVolunteer">
{({ field }) => (
<div className="flex flex-col gap-1 grow md:basis-1/2">
<FormLabel>Would you like to volunteer in the future?</FormLabel>
<RadioGroup onValueChange={(value) => field.onChange(Boolean(value))} value={field.value} className="flex flex-wrap gap-4 py-3 mt-auto">
<div className="flex items-center space-x-2">
<RadioGroupItem pyconStyles id="futureVolunteer-yes" checked={!!field.value} value={'true'} />
<Label htmlFor={`futureVolunteer-yes`}>Yes</Label>

<RadioGroupItem pyconStyles id="futureVolunteer-no" checked={!field.value} value="" />
<Label htmlFor={`futureVolunteer-no`}>No</Label>
<div className="flex flex-col gap-1 grow md:basis-1/2 bg-pycon-dirty-white p-6 rounded-[38.13px]">
<Label htmlFor="futureVolunteer-yes">
Would you like to volunteer in the future?<span className="text-pycon-orange">&nbsp;*</span>
</Label>
<RadioGroup
onValueChange={(val) => field.onChange(val === 'true')}
value={typeof field.value === 'boolean' ? String(field.value) : undefined}
className="flex flex-wrap gap-4 py-3 mt-auto"
>
<div className="flex items-center gap-6">
<div className="flex items-center space-x-2">
<RadioGroupItem pyconStyles id="futureVolunteer-yes" value="true" className="border-pycon-dark-blue/30 bg-transparent cursor-pointer" />
<Label htmlFor="futureVolunteer-yes" className="cursor-pointer font-medium text-pycon-dark-blue">
Yes
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem pyconStyles id="futureVolunteer-no" value="false" className="border-pycon-dark-blue/30 bg-transparent cursor-pointer" />
<Label htmlFor="futureVolunteer-no" className="cursor-pointer font-medium text-pycon-dark-blue">
No
</Label>
</div>
</div>
</RadioGroup>
<FormError />
Expand All @@ -56,14 +94,17 @@ const MiscellaneousStep: FC<Props> = () => {
</FormItem>
</div>

<div className="flex flex-col md:flex-row w-full gap-4">
<div className="flex flex-col w-full mt-10 gap-4">
<p className="font-inter font-bold uppercase text-pycon-dark-blue/40 text-sm">
Preferences <span className="lowercase font-light">(optional)</span>
</p>
<FormItem name="dietaryRestrictions">
{({ field }) => (
<div className="flex flex-col gap-1 grow basis-1/2">
<FormLabel optional optionalClass="text-pycon-custard-light">
<FormLabel optional optionalClass="sr-only" className="text-pycon-orange uppercase">
Dietary Restrictions
</FormLabel>
<Input pyconStyles type="text" {...field} />
<Input pyconStyles placeholder="e.g. vegetarian, nut allergy..." type="text" {...field} />
<FormError />
</div>
)}
Expand All @@ -72,16 +113,16 @@ const MiscellaneousStep: FC<Props> = () => {
<FormItem name="accessibilityNeeds">
{({ field }) => (
<div className="flex flex-col gap-1 grow basis-1/2">
<FormLabel optional optionalClass="text-pycon-custard-light">
<FormLabel optional optionalClass="sr-only" className="text-pycon-orange uppercase">
Accessibility Needs
</FormLabel>
<Input pyconStyles type="text" {...field} />
<Input pyconStyles placeholder="e.g. wheelchair access, sign language..." type="text" {...field} />
<FormError />
</div>
)}
</FormItem>
</div>
</>
</section>
);
};

Expand Down
1 change: 0 additions & 1 deletion frontend/src/styles/pycon.css
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,3 @@ body[data-page='pycon'] {
@apply font-sora font-extrabold text-pycon-teal scroll-m-20 text-4xl tracking-tight lg:text-5xl;
}
}

27 changes: 13 additions & 14 deletions frontend/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import path from 'path';
import { defineConfig } from 'vite';
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer';
// import { VitePWA } from 'vite-plugin-pwa';
import react from '@vitejs/plugin-react';
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer';

// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
Expand All @@ -12,24 +12,23 @@ export default defineConfig(({ mode }) => {
ViteImageOptimizer({
svg: {
plugins: [
{
name: 'removeMetadata',
},
{
name: 'removeViewBox',
},

]},
{
name: 'removeMetadata'
},
{
name: 'removeViewBox'
}
]
},
png: {
quality: 80
quality: 80
},
jpeg: {
quality: 80
quality: 80
},
jpg: {
quality: 80
},

quality: 80
}
})
// VitePWA({
// registerType: 'autoUpdate', // Automatically update the service worker
Expand Down
Loading