Skip to content

Commit 14199b3

Browse files
add new screen for primary documents (#173)
1 parent d733f31 commit 14199b3

File tree

5 files changed

+208
-2
lines changed

5 files changed

+208
-2
lines changed

frontend/app/.server/locales/protected-en.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,23 @@
4040
"required": "Privacy notice statement is required."
4141
},
4242
"page-title": "Privacy Statement"
43+
},
44+
"primary-identity-document": {
45+
"current-status-in-canada": {
46+
"title": "Current status in Canada",
47+
"required": "Current status in Canada is required.",
48+
"invalid": "Please select Canadian Citizen born outside Canada.",
49+
"options": {
50+
"canadian-citizen-born-in-canada": "Canadian citizen born in Canada",
51+
"canadian-citizen-born-outside-canada": "Canadian citizen born outside Canada",
52+
"registered-indian-born-in-canada": "Registered Indian born in Canada",
53+
"registered-indian-born-outside-canada": "Registered Indian born outside Canada",
54+
"permanent-resident": "Permanent Resident",
55+
"temporary-resident": "Temporary Resident",
56+
"no-legal-status-in-canada": "No Legal Status in Canada"
57+
}
58+
},
59+
"please-select": "Please select",
60+
"page-title": "Primary identity document"
4361
}
4462
}

frontend/app/.server/locales/protected-fr.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,23 @@
4040
"required": "Privacy notice statement is required."
4141
},
4242
"page-title": "Déclaration de confidentialité"
43+
},
44+
"primary-identity-document": {
45+
"current-status-in-canada": {
46+
"title": "Statut actuel au Canadaa",
47+
"required": "Statut actuel au Canada est requis.",
48+
"invalid": "Veuillez sélectionner un citoyen canadien né en dehors du Canada.",
49+
"options": {
50+
"canadian-citizen-born-in-canada": "Citoyen canadien né au Canada",
51+
"canadian-citizen-born-outside-canada": "Citoyen canadien né à l'extérieur du Canada",
52+
"registered-indian-born-in-canada": "Indien enregistré né au Canada",
53+
"registered-indian-born-outside-canada": "Indien enregistré né en dehors du Canada",
54+
"permanent-resident": "Résident permanent",
55+
"temporary-resident": "Résident temporaire",
56+
"no-legal-status-in-canada": "Aucun statut légal au Canada"
57+
}
58+
},
59+
"please-select": "Veuillez sélectionner",
60+
"page-title": "Document d'identité principal"
4361
}
4462
}

frontend/app/@types/express-session.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ declare module 'express-session' {
2222
firstName?: string;
2323
lastName?: string;
2424
confirmPrivacyNotice?: string;
25+
currentStatusInCanada?: string;
2526
};
2627
}
2728
}

frontend/app/i18n-routes.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,15 @@ export const i18nRoutes = [
6262
{
6363
id: 'PROT-0006',
6464
file: 'routes/protected/person-case/privacy-statement.tsx',
65-
paths: { en: '/en/protected/person-case/privacy-statement', fr: '/fr/protege/person-case/privacy-statement' },
65+
paths: {
66+
en: '/en/protected/person-case/privacy-statement',
67+
fr: '/fr/protege/person-case/declaration-de-confidentialite',
68+
},
69+
},
70+
{
71+
id: 'PROT-0007',
72+
file: 'routes/protected/person-case/primary-docs.tsx',
73+
paths: { en: '/en/protected/person-case/primary-documents', fr: '/fr/protege/person-case/documents-primaires' },
6674
},
6775
//
6876
// XState-driven in-person flow (poc)
@@ -98,7 +106,7 @@ export const i18nRoutes = [
98106
id: 'IPF-0003',
99107
file: 'routes/protected/in-person/primary-docs.tsx',
100108
paths: {
101-
en: '/en/protected/in-person/primary-docsuments',
109+
en: '/en/protected/in-person/primary-documents',
102110
fr: '/fr/protege/en-personne/documents-primaires',
103111
},
104112
},
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import { data, useFetcher } from 'react-router';
2+
import type { RouteHandle } from 'react-router';
3+
4+
import { faExclamationCircle, faXmark } from '@fortawesome/free-solid-svg-icons';
5+
import { useTranslation } from 'react-i18next';
6+
import * as v from 'valibot';
7+
8+
import type { Route, Info } from './+types/primary-docs';
9+
10+
import { requireAuth } from '~/.server/utils/auth-utils';
11+
import { i18nRedirect } from '~/.server/utils/route-utils';
12+
import { Button } from '~/components/button';
13+
import { ErrorSummary } from '~/components/error-summary';
14+
import { InputSelect } from '~/components/input-select';
15+
import { PageTitle } from '~/components/page-title';
16+
import { Progress } from '~/components/progress';
17+
import { getFixedT } from '~/i18n-config.server';
18+
import { handle as parentHandle } from '~/routes/protected/layout';
19+
import { getLanguage } from '~/utils/i18n-utils';
20+
21+
export const handle = {
22+
i18nNamespace: [...parentHandle.i18nNamespace, 'protected'],
23+
} as const satisfies RouteHandle;
24+
25+
export async function loader({ context, request }: Route.LoaderArgs) {
26+
requireAuth(context.session, new URL(request.url), ['user']);
27+
const t = await getFixedT(request, handle.i18nNamespace);
28+
29+
return {
30+
documentTitle: t('protected:primary-identity-document.page-title'),
31+
defaultFormValues: {
32+
currentStatusInCanada: context.session.inPersonSINCase?.currentStatusInCanada,
33+
},
34+
};
35+
}
36+
37+
export function meta({ data }: Route.MetaArgs) {
38+
return [{ title: data.documentTitle }];
39+
}
40+
41+
export async function action({ context, request }: Route.ActionArgs) {
42+
requireAuth(context.session, new URL(request.url), ['user']);
43+
const lang = getLanguage(request);
44+
const t = await getFixedT(request, handle.i18nNamespace);
45+
46+
const formData = await request.formData();
47+
48+
if (formData.get('action') === 'back') {
49+
throw i18nRedirect('routes/protected/person-case/privacy-statement.tsx', request); //TODO: change it to redirect to file="routes/protected/person-case/request-details.tsx"
50+
}
51+
52+
// submit action
53+
const schema = v.object({
54+
currentStatusInCanada: v.pipe(
55+
v.string(t('protected:primary-identity-document.current-status-in-canada.required')),
56+
v.trim(),
57+
v.nonEmpty(t('protected:primary-identity-document.current-status-in-canada.required')),
58+
v.literal(
59+
'canadian-citizen-born-outside-canada',
60+
t('protected:primary-identity-document.current-status-in-canada.invalid'),
61+
),
62+
),
63+
});
64+
65+
const input = { currentStatusInCanada: formData.get('currentStatusInCanada') as string };
66+
const parsedDataResult = v.safeParse(schema, input, { lang });
67+
68+
if (!parsedDataResult.success) {
69+
return data({ errors: v.flatten<typeof schema>(parsedDataResult.issues).nested }, { status: 400 });
70+
}
71+
72+
context.session.inPersonSINCase = {
73+
...(context.session.inPersonSINCase ?? {}),
74+
...input,
75+
};
76+
77+
throw i18nRedirect('routes/protected/person-case/first-name.tsx', request); //TODO: change it to redirect to file="routes/protected/person-case/secondary-docs.tsx"
78+
}
79+
80+
export default function PrimaryDocs({ loaderData, actionData, params }: Route.ComponentProps) {
81+
const { t } = useTranslation(handle.i18nNamespace);
82+
83+
const fetcher = useFetcher<Info['actionData']>();
84+
const isSubmitting = fetcher.state !== 'idle';
85+
const errors = fetcher.data?.errors;
86+
87+
const dummyOption: { label: string; value: string } = {
88+
label: t('protected:primary-identity-document.please-select'),
89+
value: '',
90+
};
91+
const currentStatusInCanadaOptions: { label: string; value: string }[] = [
92+
dummyOption,
93+
{
94+
label: t('protected:primary-identity-document.current-status-in-canada.options.canadian-citizen-born-in-canada'),
95+
value: 'canadian-citizen-born-in-canada',
96+
},
97+
{
98+
label: t('protected:primary-identity-document.current-status-in-canada.options.canadian-citizen-born-outside-canada'),
99+
value: 'canadian-citizen-born-outside-canada',
100+
},
101+
{
102+
label: t('protected:primary-identity-document.current-status-in-canada.options.registered-indian-born-in-canada'),
103+
value: 'registered-indian-born-in-canada',
104+
},
105+
{
106+
label: t('protected:primary-identity-document.current-status-in-canada.options.registered-indian-born-outside-canada'),
107+
value: 'registered-indian-born-outside-canada',
108+
},
109+
{
110+
label: t('protected:primary-identity-document.current-status-in-canada.options.permanent-resident'),
111+
value: 'permanent-resident',
112+
},
113+
{
114+
label: t('protected:primary-identity-document.current-status-in-canada.options.temporary-resident'),
115+
value: 'temporary-resident',
116+
},
117+
{
118+
label: t('protected:primary-identity-document.current-status-in-canada.options.no-legal-status-in-canada'),
119+
value: 'no-legal-status-in-canada',
120+
},
121+
];
122+
123+
return (
124+
<>
125+
<div className="flex justify-end">
126+
<Button id="abandon-button" endIcon={faXmark} variant="link">
127+
{t('protected:person-case.abandon-button')}
128+
</Button>
129+
<Button id="refer-button" endIcon={faExclamationCircle} variant="link">
130+
{t('protected:person-case.refer-button')}
131+
</Button>
132+
</div>
133+
<Progress className="mt-8" label="" value={30} />
134+
<PageTitle subTitle={t('protected:in-person.title')}>{t('protected:primary-identity-document.page-title')}</PageTitle>
135+
136+
<fetcher.Form method="post" noValidate>
137+
<div className="space-y-6">
138+
<ErrorSummary errors={errors} />
139+
140+
<InputSelect
141+
id="currentStatusInCanada"
142+
name="currentStatusInCanada"
143+
errorMessage={errors?.currentStatusInCanada?.[0]}
144+
defaultValue={loaderData.defaultFormValues.currentStatusInCanada}
145+
required
146+
options={currentStatusInCanadaOptions}
147+
label={t('protected:primary-identity-document.current-status-in-canada.title')}
148+
/>
149+
</div>
150+
<div className="mt-8 flex flex-wrap items-center gap-3">
151+
<Button name="action" value="back" id="back-button" disabled={isSubmitting}>
152+
{t('protected:person-case.previous')}
153+
</Button>
154+
<Button name="action" value="next" variant="primary" id="continue-button" disabled={isSubmitting}>
155+
{t('protected:person-case.next')}
156+
</Button>
157+
</div>
158+
</fetcher.Form>
159+
</>
160+
);
161+
}

0 commit comments

Comments
 (0)