Skip to content

Commit

Permalink
review screen (#286)
Browse files Browse the repository at this point in the history
  • Loading branch information
faiza-jahanzeb authored Feb 27, 2025
1 parent d13cdd4 commit bf13ebd
Show file tree
Hide file tree
Showing 5 changed files with 258 additions and 0 deletions.
7 changes: 7 additions & 0 deletions frontend/app/.server/locales/protected-en.json
Original file line number Diff line number Diff line change
Expand Up @@ -391,5 +391,12 @@
"required-city": "City, town, or village of birth is required.",
"invalid-city": "City, town, or village of birth is invalid."
}
},
"review": {
"page-title": "Review",
"read-carefully": "Review information below for accuracy before creating case.",
"document-uploaded": "Document uploaded",
"choosen-file": "File_chosen.pdf",
"edit-primary-identity-document": "Edit primary identity document"
}
}
7 changes: 7 additions & 0 deletions frontend/app/.server/locales/protected-fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -392,5 +392,12 @@
"required-city": "La ville, la commune ou le village de naissance est requis.",
"invalid-city": "La ville, la commune ou le village de naissance est invalide."
}
},
"review": {
"page-title": "Review",
"read-carefully": "Veuillez examiner les informations ci-dessous pour en vérifier l'exactitude avant de créer le dossier.",
"document-uploaded": "Document téléchargé",
"choosen-file": "Fichier_choisi.pdf",
"edit-primary-identity-document": "Modifier le document d'identité principal"
}
}
20 changes: 20 additions & 0 deletions frontend/app/components/description-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { ComponentProps, ReactNode } from 'react';

import { cn } from '~/utils/tailwind-utils';

export interface DescriptionListItemProps extends ComponentProps<'div'> {
term: ReactNode;
}
export function DescriptionListItem({ className, children, term, ...rest }: DescriptionListItemProps) {
return (
<div className={cn('py-6 sm:grid sm:grid-cols-3 sm:gap-4', className)} {...rest}>
<dt className="font-semibold">{term}</dt>
<dd className="mt-1 sm:col-span-2 sm:mt-0">{children}</dd>
</div>
);
}

export interface DescriptionListProps extends ComponentProps<'dl'> {}
export function DescriptionList(props: DescriptionListProps) {
return <dl {...props} />;
}
8 changes: 8 additions & 0 deletions frontend/app/i18n-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,14 @@ export const i18nRoutes = [
fr: '/fr/protege/cas-personnel/contact-information',
},
},
{
id: 'PROT-0014',
file: 'routes/protected/person-case/review.tsx',
paths: {
en: '/en/protected/person-case/review',
fr: '/fr/protege/cas-personnel/revision',
},
},
//
// XState-driven in-person flow (poc)
//
Expand Down
216 changes: 216 additions & 0 deletions frontend/app/routes/protected/person-case/review.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
import { useId } from 'react';

import type { RouteHandle } from 'react-router';
import { useFetcher } from 'react-router';

import type { SessionData } from 'express-session';
import { useTranslation } from 'react-i18next';

import type { Info, Route } from './+types/review';

import { requireAuth } from '~/.server/utils/auth-utils';
import { i18nRedirect } from '~/.server/utils/route-utils';
import { Button } from '~/components/button';
import { ButtonLink } from '~/components/button-link';
import { DescriptionList, DescriptionListItem } from '~/components/description-list';
import { PageTitle } from '~/components/page-title';
import { AppError } from '~/errors/app-error';
import { ErrorCodes } from '~/errors/error-codes';
import { getTranslation } from '~/i18n-config.server';
import { handle as parentHandle } from '~/routes/protected/layout';

export const handle = {
i18nNamespace: [...parentHandle.i18nNamespace, 'protected'],
} as const satisfies RouteHandle;

export async function loader({ context, request }: Route.LoaderArgs) {
requireAuth(context.session, new URL(request.url), ['user']);
const { t } = await getTranslation(request, handle.i18nNamespace);
const inPersonSINCase = validateInPersonSINCaseSession(context.session, request);
return { documentTitle: t('protected:review.page-title'), inPersonSINCase };
}

/**
*
* @param session
* @param request
* @returns
*/
function validateInPersonSINCaseSession(
session: AppSession,
request: Request,
): Required<NonNullable<SessionData['inPersonSINCase']>> {
const inPersonSINCase = session.inPersonSINCase;

if (inPersonSINCase === undefined) {
throw i18nRedirect('routes/protected/person-case/privacy-statement.tsx', request);
}

const {
birthDetails,
contactInformation,
currentNameInfo,
parentDetails,
personalInformation,
previousSin,
primaryDocuments,
privacyStatement,
requestDetails,
secondaryDocument,
} = inPersonSINCase;

if (privacyStatement === undefined) {
throw i18nRedirect('routes/protected/index.tsx', request);
}

if (requestDetails === undefined) {
throw i18nRedirect('routes/protected/person-case/request-details.tsx', request);
}

if (primaryDocuments === undefined) {
throw i18nRedirect('routes/protected/person-case/primary-docs.tsx', request);
}

if (secondaryDocument === undefined) {
throw i18nRedirect('routes/protected/person-case/secondary-doc.tsx', request);
}

if (currentNameInfo === undefined) {
throw i18nRedirect('routes/protected/person-case/current-name.tsx', request);
}

if (personalInformation === undefined) {
throw i18nRedirect('routes/protected/person-case/personal-info.tsx', request);
}

if (birthDetails === undefined) {
throw i18nRedirect('routes/protected/person-case/birth-details.tsx', request);
}

if (parentDetails === undefined) {
throw i18nRedirect('routes/protected/person-case/parent-details.tsx', request);
}

if (previousSin === undefined) {
throw i18nRedirect('routes/protected/person-case/previous-sin.tsx', request);
}

if (contactInformation === undefined) {
throw i18nRedirect('routes/protected/person-case/contact-information.tsx', request);
}

return {
birthDetails,
contactInformation,
currentNameInfo,
parentDetails,
personalInformation,
previousSin,
primaryDocuments,
privacyStatement,
requestDetails,
secondaryDocument,
};
}

export function meta({ data }: Route.MetaArgs) {
return [{ title: data.documentTitle }];
}

export async function action({ context, request }: Route.ActionArgs) {
requireAuth(context.session, new URL(request.url), ['user']);
// const inPersonSINCase = validateInPersonSINCaseSession(context.session, request);

const formData = await request.formData();
const action = formData.get('action');

switch (action) {
case 'back': {
throw i18nRedirect('routes/protected/person-case/contact-information.tsx', request);
}

case 'next': {
throw i18nRedirect('routes/protected/index.tsx', request);
}
default: {
throw new AppError(`Unrecognized action: ${action}`, ErrorCodes.UNRECOGNIZED_ACTION);
}
}
}

export default function Review({ loaderData, actionData, params }: Route.ComponentProps) {
const { inPersonSINCase } = loaderData;
const { t } = useTranslation(handle.i18nNamespace);
const fetcherKey = useId();
const fetcher = useFetcher<Info['actionData']>({ key: fetcherKey });
const isSubmitting = fetcher.state !== 'idle';

return (
<>
<PageTitle subTitle={t('protected:in-person.title')}>{t('protected:review.page-title')}</PageTitle>
<div className="max-w-prose">
<fetcher.Form method="post" noValidate>
<p className="mb-8 text-lg">{t('protected:review.read-carefully')}</p>
<div className="space-y-10 divide-y border-y">
<section className="space-y-6">
<h2 className="font-lato mt-3 text-2xl font-bold">{t('protected:primary-identity-document.page-title')}</h2>
<DescriptionList>
<DescriptionListItem term={t('protected:primary-identity-document.current-status-in-canada.title')}>
<p>
{/* TODO: Code Table Value */}
{t(
`protected:primary-identity-document.current-status-in-canada.options.${inPersonSINCase.primaryDocuments.currentStatusInCanada}` as 'protected:primary-identity-document.current-status-in-canada.options.select-option',
)}
</p>
</DescriptionListItem>
<DescriptionListItem term={t('protected:primary-identity-document.document-type.title')}>
<p>
{/* TODO: Code Table Value */}
{t(
`protected:primary-identity-document.document-type.options.${inPersonSINCase.primaryDocuments.documentType}` as 'protected:primary-identity-document.document-type.options.select-option',
)}
</p>
</DescriptionListItem>
<DescriptionListItem term={t('protected:primary-identity-document.registration-number.label')}>
<p>{inPersonSINCase.primaryDocuments.registrationNumber}</p>
</DescriptionListItem>
<DescriptionListItem term={t('protected:primary-identity-document.client-number.label')}>
<p>{inPersonSINCase.primaryDocuments.clientNumber}</p>
</DescriptionListItem>
<DescriptionListItem term={t('protected:primary-identity-document.given-name.label')}>
<p>{inPersonSINCase.primaryDocuments.givenName}</p>
</DescriptionListItem>
<DescriptionListItem term={t('protected:primary-identity-document.last-name.label')}>
<p>{inPersonSINCase.primaryDocuments.lastName}</p>
</DescriptionListItem>
<DescriptionListItem term={t('protected:primary-identity-document.date-of-birth.label')}>
<p>{inPersonSINCase.primaryDocuments.dateOfBirth}</p>
</DescriptionListItem>
<DescriptionListItem term={t('protected:primary-identity-document.gender.label')}>
<p>{inPersonSINCase.primaryDocuments.gender}</p>
</DescriptionListItem>
<DescriptionListItem term={t('protected:primary-identity-document.citizenship-date.label')}>
<p>{inPersonSINCase.primaryDocuments.citizenshipDate}</p>
</DescriptionListItem>
<DescriptionListItem term={t('protected:review.document-uploaded')}>
<p>{t('protected:review.choosen-file')}</p>
</DescriptionListItem>
</DescriptionList>
<ButtonLink file="routes/protected/person-case/primary-docs.tsx" variant="link" size="lg">
{t('protected:review.edit-primary-identity-document')}
</ButtonLink>
</section>
</div>
<div className="mt-8 flex flex-row-reverse flex-wrap items-center justify-end gap-3">
<Button name="action" value="next" variant="primary" id="continue-button" disabled={isSubmitting}>
{t('protected:person-case.next')}
</Button>
<Button name="action" value="back" id="back-button" disabled={isSubmitting}>
{t('protected:person-case.previous')}
</Button>
</div>
</fetcher.Form>
</div>
</>
);
}

0 comments on commit bf13ebd

Please sign in to comment.