From 853ac9b1303d2fd5ab538aeb49b08fcf0b9d5853 Mon Sep 17 00:00:00 2001 From: Faiza Jahanzeb Date: Fri, 7 Feb 2025 13:21:07 -0500 Subject: [PATCH] primary identification screen with document type --- .../app/.server/locales/protected-en.json | 13 +- .../app/.server/locales/protected-fr.json | 13 +- frontend/app/@types/express-session.d.ts | 5 +- .../protected/person-case/primary-docs.tsx | 252 ++++++++++++------ 4 files changed, 205 insertions(+), 78 deletions(-) diff --git a/frontend/app/.server/locales/protected-en.json b/frontend/app/.server/locales/protected-en.json index 1834b35..4f41f30 100644 --- a/frontend/app/.server/locales/protected-en.json +++ b/frontend/app/.server/locales/protected-en.json @@ -53,6 +53,7 @@ "required": "Current status in Canada is required.", "invalid": "Please select Canadian Citizen born outside Canada.", "options": { + "select-option": "Select option", "canadian-citizen-born-in-canada": "Canadian citizen born in Canada", "canadian-citizen-born-outside-canada": "Canadian citizen born outside Canada", "registered-indian-born-in-canada": "Registered Indian born in Canada", @@ -62,7 +63,17 @@ "no-legal-status-in-canada": "No Legal Status in Canada" } }, - "please-select": "Please select", + "document-type": { + "title": "Document type", + "required": "Document type is required.", + "options": { + "select-option": "Select option", + "certificate-of-canadian-citizenship": "Certificate of Canadian citizenship", + "certificate-of-registration-of-birth-abroad": "Certificate of Registration of Birth Abroad", + "birth-certificate-and-certificate-of-indian-status": "Birth certificates and Certificates of Indian Status", + "certificate-of-canadian-citizenship-and-certificate-of-indian-status": "Certificate of Canadian Citizenship and Certificate of Indian Status" + } + }, "page-title": "Primary identity document" }, "request-details": { diff --git a/frontend/app/.server/locales/protected-fr.json b/frontend/app/.server/locales/protected-fr.json index f5f91ca..126c36f 100644 --- a/frontend/app/.server/locales/protected-fr.json +++ b/frontend/app/.server/locales/protected-fr.json @@ -53,6 +53,7 @@ "required": "Statut actuel au Canada est requis.", "invalid": "Veuillez sélectionner un citoyen canadien né en dehors du Canada.", "options": { + "select-option": "Sélectionner une option", "canadian-citizen-born-in-canada": "Citoyen canadien né au Canada", "canadian-citizen-born-outside-canada": "Citoyen canadien né à l'extérieur du Canada", "registered-indian-born-in-canada": "Indien enregistré né au Canada", @@ -62,7 +63,17 @@ "no-legal-status-in-canada": "Aucun statut légal au Canada" } }, - "please-select": "Veuillez sélectionner", + "document-type": { + "title": "Type de document", + "required": "Le type de document est requis.", + "options": { + "select-option": "Sélectionner une option", + "certificate-of-canadian-citizenship": "Certificat de citoyenneté canadienne", + "certificate-of-registration-of-birth-abroad": "Certificat d'enregistrement de naissance à l'étranger", + "birth-certificate-and-certificate-of-indian-status": "Certificats de naissance et certificats de statut indien", + "certificate-of-canadian-citizenship-and-certificate-of-indian-status": "Certificat de citoyenneté canadienne et certificat de statut indien" + } + }, "page-title": "Document d'identité principal" }, "request-details": { diff --git a/frontend/app/@types/express-session.d.ts b/frontend/app/@types/express-session.d.ts index d1a457d..5c316f9 100644 --- a/frontend/app/@types/express-session.d.ts +++ b/frontend/app/@types/express-session.d.ts @@ -34,7 +34,10 @@ declare module 'express-session' { type: string; scenario: string; }; - currentStatusInCanada?: string; + primaryDocuments?: { + currentStatusInCanada: string; + documentType: string; + }; }; } } diff --git a/frontend/app/routes/protected/person-case/primary-docs.tsx b/frontend/app/routes/protected/person-case/primary-docs.tsx index 5bf68ed..e779770 100644 --- a/frontend/app/routes/protected/person-case/primary-docs.tsx +++ b/frontend/app/routes/protected/person-case/primary-docs.tsx @@ -1,7 +1,7 @@ -import { useId } from 'react'; +import { useId, useState } from 'react'; import { data, useFetcher } from 'react-router'; -import type { RouteHandle } from 'react-router'; +import type { RouteHandle, SessionData } from 'react-router'; import { faExclamationCircle, faXmark } from '@fortawesome/free-solid-svg-icons'; import { useTranslation } from 'react-i18next'; @@ -16,10 +16,23 @@ import { FetcherErrorSummary } from '~/components/error-summary'; import { InputSelect } from '~/components/input-select'; import { PageTitle } from '~/components/page-title'; import { Progress } from '~/components/progress'; +import { AppError } from '~/errors/app-error'; +import { ErrorCodes } from '~/errors/error-codes'; import { getFixedT } from '~/i18n-config.server'; import { handle as parentHandle } from '~/routes/protected/layout'; import { getLanguage } from '~/utils/i18n-utils'; +type PrimaryDocumentsSessionData = NonNullable; + +/** + * Valid current status in Canada for proof of concept + */ +const VALID_CURRENT_STATUS = ['canadian-citizen-born-outside-canada'] as const; +/** + * Valid document type for proof of concept + */ +const VALID_DOCTYPE = ['certificate-of-canadian-citizenship', 'certificate-of-registration-of-birth-abroad'] as const; + export const handle = { i18nNamespace: [...parentHandle.i18nNamespace, 'protected'], } as const satisfies RouteHandle; @@ -30,9 +43,7 @@ export async function loader({ context, request }: Route.LoaderArgs) { return { documentTitle: t('protected:primary-identity-document.page-title'), - defaultFormValues: { - currentStatusInCanada: context.session.inPersonSINCase?.currentStatusInCanada, - }, + defaultFormValues: context.session.inPersonSINCase?.primaryDocuments, }; } @@ -46,37 +57,45 @@ export async function action({ context, request }: Route.ActionArgs) { const t = await getFixedT(request, handle.i18nNamespace); const formData = await request.formData(); + const action = formData.get('action'); + const documentTypeAvailable = formData.get('documentType') != null; - if (formData.get('action') === 'back') { - throw i18nRedirect('routes/protected/person-case/privacy-statement.tsx', request); //TODO: change it to redirect to file="routes/protected/person-case/request-details.tsx" - } + switch (action) { + case 'back': { + throw i18nRedirect('routes/protected/person-case/request-details.tsx', request); + } - // submit action - const schema = v.object({ - currentStatusInCanada: v.pipe( - v.string(t('protected:primary-identity-document.current-status-in-canada.required')), - v.trim(), - v.nonEmpty(t('protected:primary-identity-document.current-status-in-canada.required')), - v.literal( - 'canadian-citizen-born-outside-canada', - t('protected:primary-identity-document.current-status-in-canada.invalid'), - ), - ), - }); - - const input = { currentStatusInCanada: formData.get('currentStatusInCanada') as string }; - const parsedDataResult = v.safeParse(schema, input, { lang }); - - if (!parsedDataResult.success) { - return data({ errors: v.flatten(parsedDataResult.issues).nested }, { status: 400 }); - } + case 'next': { + const schema = v.object({ + currentStatusInCanada: v.pipe( + v.string(t('protected:primary-identity-document.current-status-in-canada.required')), + v.trim(), + v.nonEmpty(t('protected:primary-identity-document.current-status-in-canada.required')), + v.picklist(VALID_CURRENT_STATUS, t('protected:primary-identity-document.current-status-in-canada.invalid')), + ), + documentType: v.picklist(VALID_DOCTYPE, t('protected:primary-identity-document.document-type.required')), + }) satisfies v.GenericSchema; - context.session.inPersonSINCase = { - ...(context.session.inPersonSINCase ?? {}), - ...input, - }; + const input = { + currentStatusInCanada: formData.get('currentStatusInCanada') as string, + documentType: documentTypeAvailable ? (formData.get('documentType') as string) : undefined, + } satisfies Partial; + + const parseResult = v.safeParse(schema, input, { lang }); + + if (!parseResult.success) { + return data({ errors: v.flatten(parseResult.issues).nested }, { status: 400 }); + } - throw i18nRedirect('routes/protected/request.tsx', request); //TODO: change it to redirect to file="routes/protected/person-case/secondary-docs.tsx" + context.session.inPersonSINCase ??= {}; + context.session.inPersonSINCase.primaryDocuments = parseResult.output; + + throw i18nRedirect('routes/protected/request.tsx', request); //TODO: change it to redirect to file="routes/protected/person-case/secondary-docs.tsx" + } + default: { + throw new AppError(`Unrecognized action: ${action}`, ErrorCodes.UNRECOGNIZED_ACTION); + } + } } export default function PrimaryDocs({ loaderData, actionData, params }: Route.ComponentProps) { @@ -87,41 +106,11 @@ export default function PrimaryDocs({ loaderData, actionData, params }: Route.Co const isSubmitting = fetcher.state !== 'idle'; const errors = fetcher.data?.errors; - const dummyOption: { label: string; value: string } = { - label: t('protected:primary-identity-document.please-select'), - value: '', + const [currentStatus, setCurrentStatus] = useState(loaderData.defaultFormValues?.currentStatusInCanada); + + const handleCurrentStatusChange = (event: React.ChangeEvent) => { + setCurrentStatus(event.target.value); }; - const currentStatusInCanadaOptions: { label: string; value: string }[] = [ - dummyOption, - { - label: t('protected:primary-identity-document.current-status-in-canada.options.canadian-citizen-born-in-canada'), - value: 'canadian-citizen-born-in-canada', - }, - { - label: t('protected:primary-identity-document.current-status-in-canada.options.canadian-citizen-born-outside-canada'), - value: 'canadian-citizen-born-outside-canada', - }, - { - label: t('protected:primary-identity-document.current-status-in-canada.options.registered-indian-born-in-canada'), - value: 'registered-indian-born-in-canada', - }, - { - label: t('protected:primary-identity-document.current-status-in-canada.options.registered-indian-born-outside-canada'), - value: 'registered-indian-born-outside-canada', - }, - { - label: t('protected:primary-identity-document.current-status-in-canada.options.permanent-resident'), - value: 'permanent-resident', - }, - { - label: t('protected:primary-identity-document.current-status-in-canada.options.temporary-resident'), - value: 'temporary-resident', - }, - { - label: t('protected:primary-identity-document.current-status-in-canada.options.no-legal-status-in-canada'), - value: 'no-legal-status-in-canada', - }, - ]; return ( <> @@ -135,17 +124,23 @@ export default function PrimaryDocs({ loaderData, actionData, params }: Route.Co {t('protected:primary-identity-document.page-title')} + - +
+ + {currentStatus && ( + + )} +