Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"use client"

export { FormLoadingError as default } from "@/components/dashboard/form-loading-error"
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
"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 <code>application</code> via props, rather than via
* <code>useApplicationContext</code>, because it requires the application to already be loaded before being rendered.
*/
function travelReimbursmentForm({ application }: { application: Application }) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
function travelReimbursmentForm({ application }: { application: Application }) {
function TravelReimbursmentForm({ application }: { application: Application }) {

React component names must be named in PascalCase

const router = useRouter()
const { mutateApplication } = useApplicationContext()

const form = useForm<travelReimbursmentFormFields, unknown, z.infer<typeof travelReimbursmentFormSchema>>({
resolver: zodResolver(travelReimbursmentFormSchema),
defaultValues: {
firstNames: application.firstNames ?? "",
lastNames: application.lastNames ?? "",
},
})

async function onSubmit(values: z.infer<typeof travelReimbursmentFormSchema>): Promise<void> {
await updateApplication("personal", values)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this needs to be removed/replaced with something that creates/updates the travel reimbursement request - it's fine for that function to be a placeholder for now

await mutateApplication({ ...application, ...values })
if (application.age == null) router.push("/dashboard/contact")
}

return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<div className="lg:columns-2">
<div className="mb-4">
<FormField
control={form.control}
name="firstNames"
render={({ field }) => (
<FormItem>
<FormLabel>First name(s)</FormLabel>
<FormControl>
<Input {...field} placeholder="Enter first name(s)..." />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="mb-4">
<FormField
control={form.control}
name="lastNames"
render={({ field }) => (
<FormItem>
<FormLabel>Last name(s)</FormLabel>
<FormControl>
<Input {...field} placeholder="Enter last name(s)..." />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
</div>
<div className="mt-16 flex justify-center">
<FormSubmitButton type="submit">Save Progress</FormSubmitButton>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Submit button should probably be more like 'submit travel reimbursement request'

</div>
</form>
</Form>
)
}

function TravelReimbursmentFormSkeleton() {
return <FormSkeleton rows={3} className="mt-2" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The number of rows in the skeleton should ultimately match the number of fields in the form - the fields in the form will be changing though, so that's okay

}

export default function TravelReimbursmentForm() {
const { application, applicationIsLoading } = useApplicationContext()

if (!isLoaded(application, applicationIsLoading)) {
return <TravelReimbursmentFormSkeleton />
}

return <TravelReimbursmentFormSkeleton application={application} />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once the application is loaded, you should return the actual form component, not just the skeleton

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type * as React from "react"

export default function PostTravelReimbursmentFormLayout({ children }: { children: React.ReactNode }) {
return (
<>
<h2 className="text-2xl">Post Travel Reimbursement Form
</h2>
{children}
</>
)
}