From 300c4d0ce5bc12b9d4a1dc508604bf2b3215ef39 Mon Sep 17 00:00:00 2001 From: Anna-Maria Date: Wed, 16 Jul 2025 12:42:09 +0100 Subject: [PATCH 1/8] add travel reimbursement form changes --- .../post-travel_reimbursment form/form.tsx | 253 ++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 client/src/app/dashboard/(application)/post-travel_reimbursment form/form.tsx diff --git a/client/src/app/dashboard/(application)/post-travel_reimbursment form/form.tsx b/client/src/app/dashboard/(application)/post-travel_reimbursment form/form.tsx new file mode 100644 index 00000000..5809b804 --- /dev/null +++ b/client/src/app/dashboard/(application)/post-travel_reimbursment form/form.tsx @@ -0,0 +1,253 @@ +"use client" + +import { zodResolver } from "@hookform/resolvers/zod" +import { useRouter } from "next/navigation" +import * as React from "react" +import { useForm } from "react-hook-form" +import { z } from "zod" + +import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@durhack/web-components/ui/form" +import { Input } from "@durhack/web-components/ui/input" +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, + SelectValueClipper, +} from "@durhack/web-components/ui/select" + +import { FormSkeleton } from "@/components/dashboard/form-skeleton" +import { FormSubmitButton } from "@/components/dashboard/form-submit-button" +import type { Application } from "@/hooks/use-application" +import { useApplicationContext } from "@/hooks/use-application-context" +import { isLoaded } from "@/lib/is-loaded" +import { updateApplication } from "@/lib/update-application" + +type travelReimbursmentFormFields = { + firstNames: string + lastNames: string + email: string + phoneNumber: string + travelFromCity: string + travelDate1: string + travelDate2: string + transport: string + returnJourney: boolean +} + +const travelReimbursmentFormSchema = z.object({ + firstNames: z.string().trim().min(1, { message: "Please provide your first name(s)" }).max(256), + lastNames: z.string().trim().min(1, { message: "Please provide your last name(s)" }).max(256), + email: z.string().trim().min(1,{ message: "Please provide your email address" }).max(256), + phoneNumber:z.string().trim().min(1,{ message: "Please provide your phone number" }).max(15), + travelFromCity: z.string().trim().min(1, { message: "Please provide the city you are travelling from" }).max(256), + travelDate1: z.string().trim().min(1, { message: "Please provide the date you are travelling to Durham in the format DD/MM/YYYY" }).max(256), + travelDate2: z.string().trim().optional(), + transport: z.enum(["train", "bus", "car", "other"]), + returnJourney: z.boolean({required_error: "Please indicate if you require a return journey",}), +}) + +/** + * This component accepts application via props, rather than via + * useApplicationContext, because it requires the application to already be loaded before being rendered. + */ +function travelReimbursmentForm({ application }: { application: Application }) { + const router = useRouter() + const { mutateApplication } = useApplicationContext() + + const form = useForm>({ + resolver: zodResolver(travelReimbursmentFormSchema), + defaultValues: { + firstNames: application.firstNames ?? "", + lastNames: application.lastNames ?? "", + }, + }) + + async function onSubmit(values: z.infer): Promise { + await updateApplication("personal", values) + await mutateApplication({ ...application, ...values }) + if (application.age == null) router.push("/dashboard/contact") + } + + return ( +
+ +
+
+ ( + + First name(s) + + + + + + )} + /> +
+
+ ( + + Last name(s) + + + + + + )} + /> +
+
+
+
+ ( + + Preferred name(s) + + + + + + )} + /> +
+
+ ( + + Pronouns +
+ + {value === "other" && } +
+ +
+ )} + /> +
+
+
+ ( + + Age as of 1st November 2025 + + + + + + )} + /> +
+ +
+ ( + + Gender + + + + )} + /> +
+ +
+ ( + + Race/Ethnicity + + + + )} + /> +
+ +
+ Save Progress +
+
+ + ) +} + +function travelReimbursmentFormSkeleton() { + return +} + +export default function PersonalPage() { + const { application, applicationIsLoading } = useApplicationContext() + + if (!isLoaded(application, applicationIsLoading)) { + return + } + + return +} From 575468ba75cdbb244e2039664c28b1dcb3c0b9b5 Mon Sep 17 00:00:00 2001 From: Anna-Maria Date: Wed, 16 Jul 2025 13:51:46 +0100 Subject: [PATCH 2/8] changes to the form --- .../post-travel_reimbursment form/form.tsx | 131 +----------------- 1 file changed, 3 insertions(+), 128 deletions(-) diff --git a/client/src/app/dashboard/(application)/post-travel_reimbursment form/form.tsx b/client/src/app/dashboard/(application)/post-travel_reimbursment form/form.tsx index 5809b804..b877565d 100644 --- a/client/src/app/dashboard/(application)/post-travel_reimbursment form/form.tsx +++ b/client/src/app/dashboard/(application)/post-travel_reimbursment form/form.tsx @@ -105,131 +105,6 @@ function travelReimbursmentForm({ application }: { application: Application }) { /> -
-
- ( - - Preferred name(s) - - - - - - )} - /> -
-
- ( - - Pronouns -
- - {value === "other" && } -
- -
- )} - /> -
-
-
- ( - - Age as of 1st November 2025 - - - - - - )} - /> -
- -
- ( - - Gender - - - - )} - /> -
- -
- ( - - Race/Ethnicity - - - - )} - /> -
-
Save Progress
@@ -238,7 +113,7 @@ function travelReimbursmentForm({ application }: { application: Application }) { ) } -function travelReimbursmentFormSkeleton() { +function TravelReimbursmentFormSkeleton() { return } @@ -246,8 +121,8 @@ export default function PersonalPage() { const { application, applicationIsLoading } = useApplicationContext() if (!isLoaded(application, applicationIsLoading)) { - return + return } - return + return } From 2c1a162db4d71cc3290b225172aa78fafde58436 Mon Sep 17 00:00:00 2001 From: Anna-Maria Date: Wed, 16 Jul 2025 14:16:31 +0100 Subject: [PATCH 3/8] updates to the form (error and layout) --- .../post-travel_reimbursment form/error.tsx | 3 +++ .../post-travel_reimbursment form/form.tsx | 2 +- .../post-travel_reimbursment form/layout.tsx | 11 +++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 client/src/app/dashboard/(application)/post-travel_reimbursment form/error.tsx create mode 100644 client/src/app/dashboard/(application)/post-travel_reimbursment form/layout.tsx diff --git a/client/src/app/dashboard/(application)/post-travel_reimbursment form/error.tsx b/client/src/app/dashboard/(application)/post-travel_reimbursment form/error.tsx new file mode 100644 index 00000000..5611be6d --- /dev/null +++ b/client/src/app/dashboard/(application)/post-travel_reimbursment form/error.tsx @@ -0,0 +1,3 @@ +"use client" + +export { FormLoadingError as default } from "@/components/dashboard/form-loading-error" diff --git a/client/src/app/dashboard/(application)/post-travel_reimbursment form/form.tsx b/client/src/app/dashboard/(application)/post-travel_reimbursment form/form.tsx index b877565d..95cc82c6 100644 --- a/client/src/app/dashboard/(application)/post-travel_reimbursment form/form.tsx +++ b/client/src/app/dashboard/(application)/post-travel_reimbursment form/form.tsx @@ -117,7 +117,7 @@ function TravelReimbursmentFormSkeleton() { return } -export default function PersonalPage() { +export default function TravelReimbursmentForm() { const { application, applicationIsLoading } = useApplicationContext() if (!isLoaded(application, applicationIsLoading)) { diff --git a/client/src/app/dashboard/(application)/post-travel_reimbursment form/layout.tsx b/client/src/app/dashboard/(application)/post-travel_reimbursment form/layout.tsx new file mode 100644 index 00000000..16f19daa --- /dev/null +++ b/client/src/app/dashboard/(application)/post-travel_reimbursment form/layout.tsx @@ -0,0 +1,11 @@ +import type * as React from "react" + +export default function PostTravelReimbursmentFormLayout({ children }: { children: React.ReactNode }) { + return ( + <> +

Post Travel Reimbursement Form +

+ {children} + + ) +} From 8f8fba1bc6f0be5f34330536605db6c57466216c Mon Sep 17 00:00:00 2001 From: Anna-Maria Date: Thu, 17 Jul 2025 14:56:30 +0100 Subject: [PATCH 4/8] name change --- .../error.tsx | 0 .../form.tsx | 67 ++++++++++++++++--- .../layout.tsx | 2 +- 3 files changed, 57 insertions(+), 12 deletions(-) rename client/src/app/dashboard/(application)/{post-travel_reimbursment form => post-travel_reimbursement form}/error.tsx (100%) rename client/src/app/dashboard/(application)/{post-travel_reimbursment form => post-travel_reimbursement form}/form.tsx (63%) rename client/src/app/dashboard/(application)/{post-travel_reimbursment form => post-travel_reimbursement form}/layout.tsx (60%) diff --git a/client/src/app/dashboard/(application)/post-travel_reimbursment form/error.tsx b/client/src/app/dashboard/(application)/post-travel_reimbursement form/error.tsx similarity index 100% rename from client/src/app/dashboard/(application)/post-travel_reimbursment form/error.tsx rename to client/src/app/dashboard/(application)/post-travel_reimbursement form/error.tsx diff --git a/client/src/app/dashboard/(application)/post-travel_reimbursment form/form.tsx b/client/src/app/dashboard/(application)/post-travel_reimbursement form/form.tsx similarity index 63% rename from client/src/app/dashboard/(application)/post-travel_reimbursment form/form.tsx rename to client/src/app/dashboard/(application)/post-travel_reimbursement form/form.tsx index 95cc82c6..10c394c1 100644 --- a/client/src/app/dashboard/(application)/post-travel_reimbursment form/form.tsx +++ b/client/src/app/dashboard/(application)/post-travel_reimbursement form/form.tsx @@ -24,7 +24,7 @@ import { useApplicationContext } from "@/hooks/use-application-context" import { isLoaded } from "@/lib/is-loaded" import { updateApplication } from "@/lib/update-application" -type travelReimbursmentFormFields = { +type travelReimbursementFormFields = { firstNames: string lastNames: string email: string @@ -36,7 +36,7 @@ type travelReimbursmentFormFields = { returnJourney: boolean } -const travelReimbursmentFormSchema = z.object({ +const travelReimbursementFormSchema = z.object({ firstNames: z.string().trim().min(1, { message: "Please provide your first name(s)" }).max(256), lastNames: z.string().trim().min(1, { message: "Please provide your last name(s)" }).max(256), email: z.string().trim().min(1,{ message: "Please provide your email address" }).max(256), @@ -52,19 +52,19 @@ const travelReimbursmentFormSchema = z.object({ * This component accepts application via props, rather than via * useApplicationContext, because it requires the application to already be loaded before being rendered. */ -function travelReimbursmentForm({ application }: { application: Application }) { +function travelReimbursementForm({ application }: { application: Application }) { const router = useRouter() const { mutateApplication } = useApplicationContext() - const form = useForm>({ - resolver: zodResolver(travelReimbursmentFormSchema), + const form = useForm>({ + resolver: zodResolver(travelReimbursementFormSchema), defaultValues: { firstNames: application.firstNames ?? "", lastNames: application.lastNames ?? "", }, }) - async function onSubmit(values: z.infer): Promise { + async function onSubmit(values: z.infer): Promise { await updateApplication("personal", values) await mutateApplication({ ...application, ...values }) if (application.age == null) router.push("/dashboard/contact") @@ -104,25 +104,70 @@ function travelReimbursmentForm({ application }: { application: Application }) { )} /> +
+ ( + + Email + + + + + + )} + /> +
+
+ ( + + Phone number + + + + + + )} + /> +
+
+ ( + + Travel From City + + + + + + )} + /> +
- Save Progress + Submit travel reimbursement request
) } -function TravelReimbursmentFormSkeleton() { +function TravelReimbursementFormSkeleton() { return } -export default function TravelReimbursmentForm() { +export default function travelReimbursementForm() { const { application, applicationIsLoading } = useApplicationContext() if (!isLoaded(application, applicationIsLoading)) { - return + return } - return + return } diff --git a/client/src/app/dashboard/(application)/post-travel_reimbursment form/layout.tsx b/client/src/app/dashboard/(application)/post-travel_reimbursement form/layout.tsx similarity index 60% rename from client/src/app/dashboard/(application)/post-travel_reimbursment form/layout.tsx rename to client/src/app/dashboard/(application)/post-travel_reimbursement form/layout.tsx index 16f19daa..dff76c58 100644 --- a/client/src/app/dashboard/(application)/post-travel_reimbursment form/layout.tsx +++ b/client/src/app/dashboard/(application)/post-travel_reimbursement form/layout.tsx @@ -1,6 +1,6 @@ import type * as React from "react" -export default function PostTravelReimbursmentFormLayout({ children }: { children: React.ReactNode }) { +export default function PostTravelReimbursementFormLayout({ children }: { children: React.ReactNode }) { return ( <>

Post Travel Reimbursement Form From 9c79e13955c9b390c843ae162f00f762409298d0 Mon Sep 17 00:00:00 2001 From: Anna-Maria Date: Thu, 17 Jul 2025 15:56:22 +0100 Subject: [PATCH 5/8] renamed form to page, post-travel to travel --- .../error.tsx | 0 .../layout.tsx | 0 .../page.tsx} | 14 +++++++------- 3 files changed, 7 insertions(+), 7 deletions(-) rename client/src/app/dashboard/(application)/{post-travel_reimbursement form => travel_reimbursement form}/error.tsx (100%) rename client/src/app/dashboard/(application)/{post-travel_reimbursement form => travel_reimbursement form}/layout.tsx (100%) rename client/src/app/dashboard/(application)/{post-travel_reimbursement form/form.tsx => travel_reimbursement form/page.tsx} (92%) diff --git a/client/src/app/dashboard/(application)/post-travel_reimbursement form/error.tsx b/client/src/app/dashboard/(application)/travel_reimbursement form/error.tsx similarity index 100% rename from client/src/app/dashboard/(application)/post-travel_reimbursement form/error.tsx rename to client/src/app/dashboard/(application)/travel_reimbursement form/error.tsx diff --git a/client/src/app/dashboard/(application)/post-travel_reimbursement form/layout.tsx b/client/src/app/dashboard/(application)/travel_reimbursement form/layout.tsx similarity index 100% rename from client/src/app/dashboard/(application)/post-travel_reimbursement form/layout.tsx rename to client/src/app/dashboard/(application)/travel_reimbursement form/layout.tsx diff --git a/client/src/app/dashboard/(application)/post-travel_reimbursement form/form.tsx b/client/src/app/dashboard/(application)/travel_reimbursement form/page.tsx similarity index 92% rename from client/src/app/dashboard/(application)/post-travel_reimbursement form/form.tsx rename to client/src/app/dashboard/(application)/travel_reimbursement form/page.tsx index 10c394c1..5a11d88c 100644 --- a/client/src/app/dashboard/(application)/post-travel_reimbursement form/form.tsx +++ b/client/src/app/dashboard/(application)/travel_reimbursement form/page.tsx @@ -24,7 +24,7 @@ import { useApplicationContext } from "@/hooks/use-application-context" import { isLoaded } from "@/lib/is-loaded" import { updateApplication } from "@/lib/update-application" -type travelReimbursementFormFields = { +type TravelReimbursementFormFields = { firstNames: string lastNames: string email: string @@ -36,7 +36,7 @@ type travelReimbursementFormFields = { returnJourney: boolean } -const travelReimbursementFormSchema = z.object({ +const TravelReimbursementFormSchema = z.object({ firstNames: z.string().trim().min(1, { message: "Please provide your first name(s)" }).max(256), lastNames: z.string().trim().min(1, { message: "Please provide your last name(s)" }).max(256), email: z.string().trim().min(1,{ message: "Please provide your email address" }).max(256), @@ -52,19 +52,19 @@ const travelReimbursementFormSchema = z.object({ * This component accepts application via props, rather than via * useApplicationContext, because it requires the application to already be loaded before being rendered. */ -function travelReimbursementForm({ application }: { application: Application }) { +function TravelReimbursementForm({ application }: { application: Application }) { const router = useRouter() const { mutateApplication } = useApplicationContext() - const form = useForm>({ - resolver: zodResolver(travelReimbursementFormSchema), + const form = useForm>({ + resolver: zodResolver(TravelReimbursementFormSchema), defaultValues: { firstNames: application.firstNames ?? "", lastNames: application.lastNames ?? "", }, }) - async function onSubmit(values: z.infer): Promise { + async function onSubmit(values: z.infer): Promise { await updateApplication("personal", values) await mutateApplication({ ...application, ...values }) if (application.age == null) router.push("/dashboard/contact") @@ -162,7 +162,7 @@ function TravelReimbursementFormSkeleton() { return } -export default function travelReimbursementForm() { +export default function TravelReimbursementFormPage() { const { application, applicationIsLoading } = useApplicationContext() if (!isLoaded(application, applicationIsLoading)) { From 090fd904ce294b630102e550ea7697d13b91659b Mon Sep 17 00:00:00 2001 From: Anna-Maria Date: Thu, 17 Jul 2025 16:05:41 +0100 Subject: [PATCH 6/8] renamed files --- .../{travel_reimbursement form => travel-reimbursement}/error.tsx | 0 .../layout.tsx | 0 .../{travel_reimbursement form => travel-reimbursement}/page.tsx | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename client/src/app/dashboard/(application)/{travel_reimbursement form => travel-reimbursement}/error.tsx (100%) rename client/src/app/dashboard/(application)/{travel_reimbursement form => travel-reimbursement}/layout.tsx (100%) rename client/src/app/dashboard/(application)/{travel_reimbursement form => travel-reimbursement}/page.tsx (100%) diff --git a/client/src/app/dashboard/(application)/travel_reimbursement form/error.tsx b/client/src/app/dashboard/(application)/travel-reimbursement/error.tsx similarity index 100% rename from client/src/app/dashboard/(application)/travel_reimbursement form/error.tsx rename to client/src/app/dashboard/(application)/travel-reimbursement/error.tsx diff --git a/client/src/app/dashboard/(application)/travel_reimbursement form/layout.tsx b/client/src/app/dashboard/(application)/travel-reimbursement/layout.tsx similarity index 100% rename from client/src/app/dashboard/(application)/travel_reimbursement form/layout.tsx rename to client/src/app/dashboard/(application)/travel-reimbursement/layout.tsx diff --git a/client/src/app/dashboard/(application)/travel_reimbursement form/page.tsx b/client/src/app/dashboard/(application)/travel-reimbursement/page.tsx similarity index 100% rename from client/src/app/dashboard/(application)/travel_reimbursement form/page.tsx rename to client/src/app/dashboard/(application)/travel-reimbursement/page.tsx From 05da33f7b8b947a860b049d4482ab4bd832e88ff Mon Sep 17 00:00:00 2001 From: Anna-Maria Date: Mon, 28 Jul 2025 14:46:24 +0100 Subject: [PATCH 7/8] edits to travel reimbursement form (travel methods - multiselect, travel receipts upload) --- .../travel-reimbursement/layout.tsx | 4 +- .../travel-reimbursement/page.tsx | 217 ++++++++++-------- 2 files changed, 118 insertions(+), 103 deletions(-) diff --git a/client/src/app/dashboard/(application)/travel-reimbursement/layout.tsx b/client/src/app/dashboard/(application)/travel-reimbursement/layout.tsx index dff76c58..2729f07a 100644 --- a/client/src/app/dashboard/(application)/travel-reimbursement/layout.tsx +++ b/client/src/app/dashboard/(application)/travel-reimbursement/layout.tsx @@ -1,9 +1,9 @@ import type * as React from "react" -export default function PostTravelReimbursementFormLayout({ children }: { children: React.ReactNode }) { +export default function TravelReimbursementFormLayout({ children }: { children: React.ReactNode }) { return ( <> -

Post Travel Reimbursement Form +

Travel Reimbursement Form

{children} diff --git a/client/src/app/dashboard/(application)/travel-reimbursement/page.tsx b/client/src/app/dashboard/(application)/travel-reimbursement/page.tsx index 5a11d88c..b6aac83b 100644 --- a/client/src/app/dashboard/(application)/travel-reimbursement/page.tsx +++ b/client/src/app/dashboard/(application)/travel-reimbursement/page.tsx @@ -5,8 +5,9 @@ import { useRouter } from "next/navigation" import * as React from "react" import { useForm } from "react-hook-form" import { z } from "zod" +import { MultiSelect } from "@durhack/web-components/ui/multi-select" -import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@durhack/web-components/ui/form" +import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, FormDescription } from "@durhack/web-components/ui/form" import { Input } from "@durhack/web-components/ui/input" import { Select, @@ -16,6 +17,15 @@ import { SelectValue, SelectValueClipper, } from "@durhack/web-components/ui/select" +import { + FileUpload, + FileUploadDropzoneBasket, + FileUploadDropzoneInput, + FileUploadDropzoneRoot, + FileUploadErrorMessage, + FileUploadFileList, +} from "@durhack/web-components/ui/file-upload" + import { FormSkeleton } from "@/components/dashboard/form-skeleton" import { FormSubmitButton } from "@/components/dashboard/form-submit-button" @@ -25,28 +35,41 @@ import { isLoaded } from "@/lib/is-loaded" import { updateApplication } from "@/lib/update-application" type TravelReimbursementFormFields = { - firstNames: string - lastNames: string - email: string - phoneNumber: string - travelFromCity: string - travelDate1: string - travelDate2: string - transport: string - returnJourney: boolean + + methodOfTravel: string + receiptFiles: File[] } const TravelReimbursementFormSchema = z.object({ - firstNames: z.string().trim().min(1, { message: "Please provide your first name(s)" }).max(256), - lastNames: z.string().trim().min(1, { message: "Please provide your last name(s)" }).max(256), - email: z.string().trim().min(1,{ message: "Please provide your email address" }).max(256), - phoneNumber:z.string().trim().min(1,{ message: "Please provide your phone number" }).max(15), - travelFromCity: z.string().trim().min(1, { message: "Please provide the city you are travelling from" }).max(256), - travelDate1: z.string().trim().min(1, { message: "Please provide the date you are travelling to Durham in the format DD/MM/YYYY" }).max(256), - travelDate2: z.string().trim().optional(), - transport: z.enum(["train", "bus", "car", "other"]), - returnJourney: z.boolean({required_error: "Please indicate if you require a return journey",}), -}) + methodoftravel: z + .array( + z.enum(["train", "bus", "private road vehicle", "international transport", "other"]) + ), + receiptFiles: z + .array( + z + .custom((value) => value instanceof File) + .refine((value) => value.size <= 10485760, "Maximum file size is 10MB!") + .refine((value) => { + if ( + ![ + "application/pdf", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "application/msword", + "application/png", + "application/jpg" + ].includes(value.type) + ) + return false + + const split = value.name.split(".") + const extension = split[split.length - 1] + return ["doc", "docx", "pdf","png", "jpg"].includes(extension) + }, "Please upload a PDF or Word doc or a PNG or JPG image!"), + ) + + }) + /** * This component accepts application via props, rather than via @@ -58,10 +81,6 @@ function TravelReimbursementForm({ application }: { application: Application }) const form = useForm>({ resolver: zodResolver(TravelReimbursementFormSchema), - defaultValues: { - firstNames: application.firstNames ?? "", - lastNames: application.lastNames ?? "", - }, }) async function onSubmit(values: z.infer): Promise { @@ -73,82 +92,78 @@ function TravelReimbursementForm({ application }: { application: Application }) return (
-
-
- ( - - First name(s) - - - - - - )} - /> -
-
- ( - - Last name(s) - - - - - - )} - /> -
-
- ( - - Email - - - - - - )} - /> -
-
- ( - - Phone number - - - - - - )} - /> -
-
- ( - - Travel From City - - - - - - )} - /> -
+
+
+ ( + + Method of travel +
+ + + + + {value === "other" && } +
+ +
+ )} + /> +
+
+ + +
+ ( + + Travel receipts + +

+ Only pdf, doc, docs, png and jpg files are accepted. +

+
+ + + + + + + + + +
+ )} + />
Submit travel reimbursement request @@ -159,7 +174,7 @@ function TravelReimbursementForm({ application }: { application: Application }) } function TravelReimbursementFormSkeleton() { - return + return } export default function TravelReimbursementFormPage() { From 02bce7e670b10f6d24fc7e95ecb4b858601493e0 Mon Sep 17 00:00:00 2001 From: Anna-Maria Date: Mon, 28 Jul 2025 16:08:46 +0100 Subject: [PATCH 8/8] added travel section --- .../(application)/education/page.tsx | 2 +- .../travel-reimbursement/page.tsx | 6 +- .../dashboard/(application)/travel/error.tsx | 3 + .../dashboard/(application)/travel/layout.tsx | 10 ++ .../dashboard/(application)/travel/page.tsx | 103 ++++++++++++++++++ client/src/components/dashboard/sidebar.tsx | 1 + 6 files changed, 120 insertions(+), 5 deletions(-) create mode 100644 client/src/app/dashboard/(application)/travel/error.tsx create mode 100644 client/src/app/dashboard/(application)/travel/layout.tsx create mode 100644 client/src/app/dashboard/(application)/travel/page.tsx diff --git a/client/src/app/dashboard/(application)/education/page.tsx b/client/src/app/dashboard/(application)/education/page.tsx index 3a83738b..ff0aea1a 100644 --- a/client/src/app/dashboard/(application)/education/page.tsx +++ b/client/src/app/dashboard/(application)/education/page.tsx @@ -127,7 +127,7 @@ function EducationForm({ schoolOptions, countryOptions, application }: Education async function onSubmit(values: z.infer): Promise { await updateApplication("education", values) await mutateApplication({ ...application, ...values }) - if (application.university == null) router.push("/dashboard/cv") + if (application.university == null) router.push("/dashboard/travel") } return ( diff --git a/client/src/app/dashboard/(application)/travel-reimbursement/page.tsx b/client/src/app/dashboard/(application)/travel-reimbursement/page.tsx index b6aac83b..8f28c73e 100644 --- a/client/src/app/dashboard/(application)/travel-reimbursement/page.tsx +++ b/client/src/app/dashboard/(application)/travel-reimbursement/page.tsx @@ -6,7 +6,6 @@ import * as React from "react" import { useForm } from "react-hook-form" import { z } from "zod" import { MultiSelect } from "@durhack/web-components/ui/multi-select" - import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, FormDescription } from "@durhack/web-components/ui/form" import { Input } from "@durhack/web-components/ui/input" import { @@ -84,9 +83,8 @@ function TravelReimbursementForm({ application }: { application: Application }) }) async function onSubmit(values: z.infer): Promise { - await updateApplication("personal", values) + await updateApplication("travelReimbursement", values) await mutateApplication({ ...application, ...values }) - if (application.age == null) router.push("/dashboard/contact") } return ( @@ -133,7 +131,7 @@ function TravelReimbursementForm({ application }: { application: Application }) Travel receipts -

+

Only pdf, doc, docs, png and jpg files are accepted.

diff --git a/client/src/app/dashboard/(application)/travel/error.tsx b/client/src/app/dashboard/(application)/travel/error.tsx new file mode 100644 index 00000000..5611be6d --- /dev/null +++ b/client/src/app/dashboard/(application)/travel/error.tsx @@ -0,0 +1,3 @@ +"use client" + +export { FormLoadingError as default } from "@/components/dashboard/form-loading-error" diff --git a/client/src/app/dashboard/(application)/travel/layout.tsx b/client/src/app/dashboard/(application)/travel/layout.tsx new file mode 100644 index 00000000..63b85456 --- /dev/null +++ b/client/src/app/dashboard/(application)/travel/layout.tsx @@ -0,0 +1,10 @@ +import type * as React from "react" + +export default function TravelPageLayout({ children }: { children: React.ReactNode }) { + return ( + <> +

Travel Details

+ {children} + + ) +} diff --git a/client/src/app/dashboard/(application)/travel/page.tsx b/client/src/app/dashboard/(application)/travel/page.tsx new file mode 100644 index 00000000..e156f942 --- /dev/null +++ b/client/src/app/dashboard/(application)/travel/page.tsx @@ -0,0 +1,103 @@ +"use client" + +import { zodResolver } from "@hookform/resolvers/zod" +import { useRouter } from "next/navigation" +import * as React from "react" +import { useForm } from "react-hook-form" +import { z } from "zod" +import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, FormDescription } from "@durhack/web-components/ui/form" +import { Input } from "@durhack/web-components/ui/input" +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, + SelectValueClipper, +} from "@durhack/web-components/ui/select" +import { FormSkeleton } from "@/components/dashboard/form-skeleton" +import { FormSubmitButton } from "@/components/dashboard/form-submit-button" +import type { Application } from "@/hooks/use-application" +import { useApplicationContext } from "@/hooks/use-application-context" +import { isLoaded } from "@/lib/is-loaded" +import { updateApplication } from "@/lib/update-application" + +type TravelDetailsFormFields = { + travelOrigin: string +} +const TravelDetailsFormSchema = z.object({ + travelOrigin: z.enum(["prefer-not-to-answer", "Durham", "elsewhere in the UK", "abroad"]) + }) +/** + * This component accepts application via props, rather than via + * useApplicationContext, because it requires the application to already be loaded before being rendered. + */ +function TravelDetailsForm({ application }: { application: Application }) { + const router = useRouter() + const { mutateApplication } = useApplicationContext() + + const form = useForm>({ + resolver: zodResolver(TravelDetailsFormSchema), + }) +async function onSubmit(values: z.infer): Promise { + await updateApplication("travel", values) + await mutateApplication({ ...application, ...values }) + //if (application.travelOrigin == null) router.push("/dashboard/cv") + } + return ( + + +
+
+ ( + + Where will you be travelling from +
+ + {value === "elsewhere-in-the-uk" && } + {value === "prefer-not-to-answer" &&

+ You will not be able to apply for travel reimbursement.

} +
+ +
+ )} + /> +
+
+
+ Save Progress +
+ + + ) + } +function TravelDetailsFormSkeleton() { + return +} + +export default function TravelDetailsFormPage() { + const { application, applicationIsLoading } = useApplicationContext() + + if (!isLoaded(application, applicationIsLoading)) { + return + } + + return +} \ No newline at end of file diff --git a/client/src/components/dashboard/sidebar.tsx b/client/src/components/dashboard/sidebar.tsx index 9f21aa03..a7440d1a 100644 --- a/client/src/components/dashboard/sidebar.tsx +++ b/client/src/components/dashboard/sidebar.tsx @@ -19,6 +19,7 @@ const menuItems = [ { id: 4, name: "Contact", link: "/contact" }, { id: 5, name: "Extra", link: "/extra" }, { id: 6, name: "Education", link: "/education" }, + { id: 9, name: "Travel", link: "/travel" }, { id: 7, name: "CV", link: "/cv" }, { id: 8, name: "Submit", link: "/submit" }, ] as const satisfies readonly MenuItem[]