diff --git a/src/components/ApplicationForm.tsx b/src/components/ApplicationForm.tsx index 046c94b..1d9e0ec 100644 --- a/src/components/ApplicationForm.tsx +++ b/src/components/ApplicationForm.tsx @@ -9,23 +9,24 @@ import { updateApplicantPersonalInfo, } from "@/lib/supabase/actions/applicants.actions"; -import type { ApplicantType } from "@/types"; +import type { + ApplicantType, +} from "@/types"; const branches = [ "Aerospace Engineering", - "Chemical Engineering", + "Bachelor of Design (B.Des)", "Civil Engineering", "Computer Science & Engineering", - "Electrical Engineering", - "Electronics & Communication Engineering", - "Mechanical Engineering", - "Production & Industrial Engineering", "Computer Science & Engineering (AI)", "Computer Science & Engineering (DS)", + "Electrical Engineering", + "Electronics & Communication Engineering", "Electronics Engineering (VLSI)", "Materials and Metallurgical Engineering", "Mathematics and Computing", - "Bachelor of Design (B.Des)", + "Mechanical Engineering", + "Production & Industrial Engineering", ]; export const APPLICATION_QUESTIONS = [ @@ -47,29 +48,72 @@ export const APPLICATION_QUESTIONS = [ }, ]; -const createEmptyResponses = () => - APPLICATION_QUESTIONS.reduce( - (acc, question) => { - acc[question.id] = ""; - return acc; - }, - {} as Record - ); +const createEmptyResponses = + () => + APPLICATION_QUESTIONS.reduce( + ( + acc, + question + ) => { + acc[ + question.id + ] = ""; + + return acc; + }, + {} as Record< + string, + string + > + ); + +type FormState = { + name: string; + phone: string; + sid: string; + branch: string; + + gender: + | "" + | "male" + | "female"; + + /* + * null = not selected + * true = Yes + * false = No + */ + isHostellers: + | boolean + | null; + + responses: Record< + string, + string + >; +}; -const initialForm = { +const initialForm: FormState = { name: "", phone: "", sid: "", branch: "", + gender: "", + isHostellers: null, responses: createEmptyResponses(), }; export default function ApplicationForm() { const [form, setForm] = - useState(initialForm); + useState( + initialForm + ); - const [application, setApplication] = + const [ + application, + setApplication, + ] = useState( null ); @@ -82,8 +126,10 @@ export default function ApplicationForm() { const [error, setError] = useState(""); - const [submitting, setSubmitting] = - useState(false); + const [ + submitting, + setSubmitting, + ] = useState(false); const [ savingPersonalInfo, @@ -92,31 +138,43 @@ export default function ApplicationForm() { /* * --------------------------------------------------------- - * Field helpers + * Field helper * --------------------------------------------------------- */ - const updateField = ( - field: keyof typeof initialForm, - value: string + const updateField = < + K extends Exclude< + keyof FormState, + "responses" + > + >( + field: K, + value: FormState[K] ) => { - setForm((current) => ({ - ...current, - [field]: value, - })); + setForm( + (current) => ({ + ...current, + [field]: value, + }) + ); }; const updateResponse = ( questionId: string, value: string ) => { - setForm((current) => ({ - ...current, - responses: { - ...current.responses, - [questionId]: value, - }, - })); + setForm( + (current) => ({ + ...current, + + responses: { + ...current.responses, + + [questionId]: + value, + }, + }) + ); }; /* @@ -146,15 +204,27 @@ export default function ApplicationForm() { name: existing.name || "", + phone: existing.phone || "", + sid: existing.sid || "", + branch: existing.branch || "", + + gender: + existing.gender || + "", + + isHostellers: + existing.isHostellers ?? + null, + responses: existing.responses || createEmptyResponses(), @@ -175,162 +245,228 @@ export default function ApplicationForm() { /* * --------------------------------------------------------- - * Create new application + * Submit new application * --------------------------------------------------------- */ - const handleSubmit = async ( - event: React.FormEvent - ) => { - event.preventDefault(); + const handleSubmit = + async ( + event: React.FormEvent + ) => { + event.preventDefault(); - setError(""); + setError(""); - const name = - form.name.trim(); + const name = + form.name.trim(); - const phone = - form.phone.trim(); + const phone = + form.phone.trim(); - const sid = - form.sid.trim(); + const sid = + form.sid.trim(); - const branch = - form.branch; + const branch = + form.branch; - if ( - !name || - !phone || - !sid || - !branch - ) { - setError( - "Please fill in all the required personal fields." - ); + const gender = + form.gender; - return; - } + const isHostellers = + form.isHostellers; - if ( - !/^[6-9]\d{9}$/.test( - phone - ) - ) { - setError( - "Please enter a valid 10-digit phone number." - ); + /* + * Required fields + * + * Do NOT use !isHostellers. + * false is a valid value. + */ - return; - } + if ( + !name || + !phone || + !sid || + !branch || + !gender || + isHostellers === + null + ) { + setError( + "Please fill in all the required personal fields." + ); - if ( - !/^\d{8}$/.test(sid) - ) { - setError( - "SID must be exactly 8 digits." - ); + return; + } - return; - } + /* + * Phone + */ - const missingResponses = - APPLICATION_QUESTIONS.some( - (question) => - !form.responses[ - question.id - ].trim() - ); + if ( + !/^[6-9]\d{9}$/.test( + phone + ) + ) { + setError( + "Please enter a valid 10-digit phone number." + ); - if (missingResponses) { - setError( - "Please answer all the application questions." - ); + return; + } - return; - } + /* + * SID + */ - const trimmedResponses = - Object.keys( - form.responses - ).reduce( - (acc, key) => { - acc[key] = - form.responses[ - key - ].trim(); + if ( + !/^\d{8}$/.test( + sid + ) + ) { + setError( + "SID must be exactly 8 digits." + ); - return acc; - }, - {} as Record< - string, - string - > - ); + return; + } - setSubmitting(true); + /* + * Questions + */ - try { - const result = - await createApplicant( - name, - sid, - phone, - branch, - trimmedResponses + const missingResponses = + APPLICATION_QUESTIONS.some( + (question) => + !form.responses[ + question.id + ].trim() ); - if (!result.success) { - if ( - result.reason === - "duplicate" - ) { - setError( - "You have already submitted an application." - ); - } else { - setError( - "We could not submit your application. Please try again." - ); - } + if ( + missingResponses + ) { + setError( + "Please answer all the application questions." + ); return; } - setApplication( - result.applicant - ); + /* + * Trim responses + */ + + const trimmedResponses = + Object.keys( + form.responses + ).reduce( + ( + acc, + key + ) => { + acc[key] = + form.responses[ + key + ].trim(); + + return acc; + }, + {} as Record< + string, + string + > + ); + + setSubmitting(true); - setForm({ - name: - result.applicant.name || - "", - phone: - result.applicant.phone || - "", - sid: - result.applicant.sid || - "", - branch: - result.applicant.branch || - "", - responses: + try { + const result = + await createApplicant( + name, + sid, + phone, + branch, + gender, + isHostellers, + trimmedResponses + ); + + if ( + !result.success + ) { + if ( + result.reason === + "duplicate" + ) { + setError( + "You have already submitted an application." + ); + } else { + setError( + "We could not submit your application. Please try again." + ); + } + + return; + } + + setApplication( result.applicant - .responses || - createEmptyResponses(), - }); - } catch (submissionError) { - console.error( - "Unexpected application submission error:", - submissionError - ); + ); - setError( - "We could not submit your application. Please try again." - ); - } finally { - setSubmitting(false); - } - }; + setForm({ + name: + result + .applicant + .name || + "", + + phone: + result + .applicant + .phone || + "", + + sid: + result + .applicant + .sid || + "", + + branch: + result + .applicant + .branch || + "", + + gender: + result + .applicant + .gender || + "", + + isHostellers: + result + .applicant + .isHostellers ?? + null, + + responses: + result + .applicant + .responses || + createEmptyResponses(), + }); + } catch { + setError( + "We could not submit your application. Please try again." + ); + } finally { + setSubmitting( + false + ); + } + }; /* * --------------------------------------------------------- @@ -358,11 +494,20 @@ export default function ApplicationForm() { const branch = form.branch; + const gender = + form.gender; + + const isHostellers = + form.isHostellers; + if ( !name || !phone || !sid || - !branch + !branch || + !gender || + isHostellers === + null ) { setError( "Please fill in all the personal information." @@ -384,7 +529,9 @@ export default function ApplicationForm() { } if ( - !/^\d{8}$/.test(sid) + !/^\d{8}$/.test( + sid + ) ) { setError( "SID must be exactly 8 digits." @@ -404,10 +551,14 @@ export default function ApplicationForm() { name, phone, sid, - branch + branch, + gender, + isHostellers ); - if (!result.success) { + if ( + !result.success + ) { setError( result.reason === "not_found" @@ -423,30 +574,47 @@ export default function ApplicationForm() { ); setForm( - (current) => ({ + ( + current + ) => ({ ...current, + name: - result.applicant + result + .applicant .name, + phone: - result.applicant + result + .applicant .phone || "", + sid: - result.applicant + result + .applicant .sid, + branch: - result.applicant + result + .applicant .branch || "", + + gender: + result + .applicant + .gender || + "", + + isHostellers: + result + .applicant + .isHostellers ?? + null, }) ); - } catch (updateError) { - console.error( - "Error updating personal information:", - updateError - ); - + } catch { setError( "Could not update your personal information. Please try again." ); @@ -459,11 +627,13 @@ export default function ApplicationForm() { /* * --------------------------------------------------------- - * Loading state + * Loading * --------------------------------------------------------- */ - if (checkingApplication) { + if ( + checkingApplication + ) { return (

@@ -486,7 +656,6 @@ export default function ApplicationForm() { return (

- {/* Personal Information */}
@@ -525,6 +694,7 @@ export default function ApplicationForm() {
{/* Name + Phone */} +