Skip to content

Commit 5c2d3ce

Browse files
[PRMP-775] Implement download success page (#1007)
1 parent 37083ce commit 5c2d3ce

7 files changed

Lines changed: 181 additions & 9 deletions

File tree

app/src/components/blocks/_patientDocuments/documentView/DocumentView.test.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,11 +349,24 @@ describe('DocumentView', () => {
349349
it('calls removeDocument when remove action is triggered', () => {
350350
renderComponent();
351351

352-
// Assuming the first record link is remove action
352+
// assume first link is remove
353353
const removeRecordLink = screen.getByTestId(lloydGeorgeRecordLinks[0].key);
354354
fireEvent.click(removeRecordLink);
355355
expect(mockRemoveDocument).toHaveBeenCalled();
356356
});
357+
358+
it('navigates to download success page when download action is triggered', () => {
359+
vi.useFakeTimers();
360+
renderComponent();
361+
362+
// assume second link is download
363+
const downloadRecordLink = screen.getByTestId(lloydGeorgeRecordLinks[1].key);
364+
fireEvent.click(downloadRecordLink);
365+
366+
vi.advanceTimersByTime(5000000);
367+
expect(mockUseNavigate).toHaveBeenCalledWith(routes.DOWNLOAD_COMPLETE);
368+
vi.useRealTimers();
369+
});
357370
});
358371

359372
describe('Role-based rendering', () => {

app/src/components/blocks/_patientDocuments/documentView/DocumentView.tsx

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,12 @@ const DocumentView = ({
7070

7171
const details = (): React.JSX.Element => {
7272
return (
73-
<div className="lloydgeorge_record-details">
74-
<div className="lloydgeorge_record-details_details">
75-
<div className="lloydgeorge_record-details_details--last-updated mt-3">
73+
<div className="document_record-details">
74+
<div className="document_record-details_details">
75+
<div className="document_record-details_details--last-updated mt-3">
7676
Filename: {documentReference.fileName}
7777
</div>
78-
<div className="lloydgeorge_record-details_details--last-updated mt-3">
78+
<div className="document_record-details_details--last-updated mt-3">
7979
Last updated: {getFormattedDate(new Date(documentReference.created))}
8080
</div>
8181
</div>
@@ -85,12 +85,18 @@ const DocumentView = ({
8585

8686
const downloadClicked = (): void => {
8787
if (documentReference.url) {
88+
const estimatedDownloadDuration =
89+
Math.floor(documentReference.fileSize / 5000000 * 1000); // Estimate 5MB/s download speed
8890
const anchor = document.createElement('a');
8991
anchor.href = documentReference.url;
9092
anchor.download = documentReference.fileName;
9193
document.body.appendChild(anchor);
9294
anchor.click();
9395
anchor.remove();
96+
97+
setTimeout(() => {
98+
navigate(routes.DOWNLOAD_COMPLETE);
99+
}, estimatedDownloadDuration);
94100
}
95101
};
96102

@@ -182,10 +188,10 @@ const DocumentView = ({
182188
return session.isFullscreen ? (
183189
card
184190
) : (
185-
<div className="lloydgeorge_record-stage_flex">
191+
<div className="document_record-stage_flex">
186192
<div
187193
data-testid="record-card-container"
188-
className={`lloydgeorge_record-stage_flex-row lloydgeorge_record-stage_flex-row${showMenu ? '--menu' : '--upload'}`}
194+
className={`document_record-stage_flex-row document_record-stage_flex-row${showMenu ? '--menu' : '--upload'}`}
189195
>
190196
{card}
191197
</div>
@@ -200,7 +206,7 @@ const DocumentView = ({
200206
(role === REPOSITORY_ROLE.GP_ADMIN || role === REPOSITORY_ROLE.GP_CLINICAL);
201207

202208
return (
203-
<div className="lloydgeorge_record-stage">
209+
<div className="document_record-stage">
204210
{session.isFullscreen && (
205211
<div className="header">
206212
<div className="header-items">
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { buildPatientDetails } from '../../helpers/test/testBuilders';
2+
import { render, screen } from '@testing-library/react';
3+
import { runAxeTest } from '../../helpers/test/axeTestHelper';
4+
import { afterEach, beforeEach, describe, expect, it, vi, Mock } from 'vitest';
5+
import DownloadCompletePage from './DownloadCompletePage';
6+
import userEvent from '@testing-library/user-event';
7+
import { routes } from '../../types/generic/routes';
8+
import usePatient from '../../helpers/hooks/usePatient';
9+
10+
vi.mock('../../helpers/hooks/usePatient');
11+
12+
const mockedUseNavigate = vi.fn();
13+
const mockUsePatient = usePatient as Mock;
14+
15+
vi.mock('react-router-dom', () => ({
16+
useNavigate: () => mockedUseNavigate,
17+
}));
18+
19+
describe('DownloadCompletePage', () => {
20+
const mockPatient = buildPatientDetails();
21+
22+
beforeEach(() => {
23+
import.meta.env.VITE_ENVIRONMENT = 'vitest';
24+
mockUsePatient.mockReturnValue(mockPatient);
25+
});
26+
afterEach(() => {
27+
vi.clearAllMocks();
28+
});
29+
30+
it('renders the download complete screen', () => {
31+
render(<DownloadCompletePage />);
32+
33+
expect(screen.getByTestId('page-title')).toBeInTheDocument();
34+
expect(
35+
screen.getByText(`Patient name: ${mockPatient.familyName}, ${mockPatient.givenName}`),
36+
).toBeInTheDocument();
37+
expect(screen.getByText('Your responsibilities with this record')).toBeInTheDocument();
38+
expect(
39+
screen.getByText('Follow the Record Management Code of Practice'),
40+
).toBeInTheDocument();
41+
expect(
42+
screen.getByRole('button', {
43+
name: 'Go to home',
44+
}),
45+
).toBeInTheDocument();
46+
});
47+
48+
it('navigates to the home screen when go to home is clicked', async () => {
49+
render(<DownloadCompletePage />);
50+
51+
await userEvent.click(screen.getByRole('button', {name: 'Go to home'}));
52+
53+
expect(mockedUseNavigate).toHaveBeenCalledWith(routes.HOME);
54+
});
55+
56+
it('navigates to the home screen if patient details are undefined', async () => {
57+
mockUsePatient.mockReturnValue(undefined);
58+
render(<DownloadCompletePage />);
59+
60+
expect(mockedUseNavigate).toHaveBeenCalledWith(routes.HOME);
61+
});
62+
63+
describe('Accessibility', () => {
64+
it('passes accessibility checks', async () => {
65+
render(<DownloadCompletePage />);
66+
const results = await runAxeTest(document.body);
67+
expect(results).toHaveNoViolations();
68+
});
69+
});
70+
});
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import React from 'react';
2+
import { Button } from 'nhsuk-react-components';
3+
import useTitle from '../../helpers/hooks/useTitle';
4+
import { useNavigate } from 'react-router-dom';
5+
import { routes } from '../../types/generic/routes';
6+
import usePatient from '../../helpers/hooks/usePatient';
7+
import { formatNhsNumber } from '../../helpers/utils/formatNhsNumber';
8+
import { getFormattedDateFromString } from '../../helpers/utils/formatDate';
9+
import { getFormattedPatientFullName } from '../../helpers/utils/formatPatientFullName';
10+
11+
const DownloadCompletePage = (): React.JSX.Element => {
12+
const navigate = useNavigate();
13+
const patient = usePatient();
14+
15+
const pageHeader = 'Download complete';
16+
useTitle({ pageTitle: pageHeader });
17+
18+
if (!patient) {
19+
navigate(routes.HOME);
20+
return <></>;
21+
}
22+
23+
return (
24+
<div className="document_download-complete">
25+
<div className="nhsuk-panel" data-testid="download-complete-card">
26+
<h1 data-testid="page-title" className="nhsuk-panel__title">
27+
Download complete
28+
</h1>
29+
<br />
30+
<div className="nhsuk-panel__body">
31+
<strong data-testid="patient-name">Patient name: {getFormattedPatientFullName(patient)}</strong>
32+
<br />
33+
<span data-testid="nhs-number">NHS number: {formatNhsNumber(patient.nhsNumber)}</span>
34+
<br />
35+
<span data-testid="dob">Date of birth: {getFormattedDateFromString(patient.birthDate)}</span>
36+
</div>
37+
</div>
38+
39+
<h2 className="nhsuk-heading-l">Your responsibilities with this record</h2>
40+
<p>
41+
Everyone in a health and care organisation is responsible for managing records
42+
appropriately. It is important all general practice staff understand their
43+
responsibilities for creating, maintaining, and disposing of records appropriately.
44+
</p>
45+
46+
<h3 className="nhsuk-heading-m">Follow the Record Management Code of Practice</h3>
47+
<p>
48+
The{' '}
49+
<a href="https://transform.england.nhs.uk/information-governance/guidance/records-management-code">
50+
Record Management Code of Practice
51+
</a>{' '}
52+
provides a framework for consistent and effective records management, based on
53+
established standards.
54+
</p>
55+
56+
<Button
57+
data-testid="go-to-home-button"
58+
onClick={(): void => {
59+
navigate(routes.HOME);
60+
}}
61+
>
62+
Go to home
63+
</Button>
64+
</div>
65+
);
66+
};
67+
68+
export default DownloadCompletePage;

app/src/router/AppRouter.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import PatientAccessAuditPage from '../pages/patientAccessAuditPage/PatientAcces
2828
import MockLoginPage from '../pages/mockLoginPage/MockLoginPage';
2929
import DocumentUploadPage from '../pages/documentUploadPage/DocumentUploadPage';
3030
import AdminRoutesPage from '../pages/adminRoutesPage/AdminRoutesPage';
31+
import DownloadCompletePage from '../pages/downloadCompletePage/DownloadCompletePage';
3132

3233
const {
3334
START,
@@ -58,6 +59,7 @@ const {
5859
DOCUMENT_UPLOAD_WILDCARD,
5960
ADMIN_ROUTE,
6061
ADMIN_ROUTE_WILDCARD,
62+
DOWNLOAD_COMPLETE,
6163
} = routes;
6264

6365
type Routes = {
@@ -247,7 +249,7 @@ export const routeMap: Routes = {
247249
type: ROUTE_TYPE.PRIVATE,
248250
},
249251

250-
// App guard routes
252+
// Patient guard routes
251253
[VERIFY_PATIENT]: {
252254
page: <PatientResultPage />,
253255
type: ROUTE_TYPE.PATIENT,
@@ -286,6 +288,10 @@ export const routeMap: Routes = {
286288
page: <DocumentUploadPage />,
287289
type: ROUTE_TYPE.PATIENT,
288290
},
291+
[DOWNLOAD_COMPLETE]: {
292+
page: <DownloadCompletePage />,
293+
type: ROUTE_TYPE.PATIENT,
294+
},
289295
};
290296

291297
const createRoutesFromType = (routeType: ROUTE_TYPE): Array<React.JSX.Element> =>

app/src/styles/App.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ $hunit: '%';
254254
}
255255

256256
// Lloyd George
257+
.document,
257258
.lloydgeorge {
258259
&_record-stage {
259260
&_links {
@@ -571,6 +572,7 @@ $hunit: '%';
571572
}
572573

573574
.lloydgeorge,
575+
.document,
574576
.report {
575577
&_download-complete {
576578
max-width: 711px;
@@ -618,18 +620,21 @@ $hunit: '%';
618620

619621
@media (max-width: 685px) {
620622
.nhsuk-card__heading.report_download-complete_details-content_header,
623+
.nhsuk-card__heading.document_download-complete_details-content_header,
621624
.nhsuk-card__heading.lloydgeorge_download-complete_details-content_header {
622625
font-size: 2rem;
623626
}
624627
}
625628

626629
@media (max-width: 435px) {
627630
.nhsuk-card__heading.report_download-complete_details-content_header,
631+
.nhsuk-card__heading.document_download-complete_details-content_header,
628632
.nhsuk-card__heading.lloydgeorge_download-complete_details-content_header {
629633
font-size: 1.5rem;
630634
}
631635

632636
.nhsuk-card__description.report_download-complete_details-content_description,
637+
.nhsuk-card__description.document_download-complete_details-content_description,
633638
.nhsuk-card__description.lloydgeorge_download-complete_details-content_description {
634639
font-size: 1.2rem !important;
635640
}
@@ -929,6 +934,7 @@ $hunit: '%';
929934
margin-bottom: 50px;
930935
}
931936

937+
.document_drag-and-drop,
932938
.lloydgeorge_drag-and-drop {
933939
border-color: $nhsuk-error-color;
934940
}
@@ -975,6 +981,7 @@ $hunit: '%';
975981
padding: 0;
976982
}
977983

984+
.document_record-stage,
978985
.lloydgeorge_record-stage {
979986
height: calc(100vh - 93px);
980987
position: relative;

app/src/types/generic/routes.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ export enum routes {
3333

3434
ADMIN_ROUTE = '/admin',
3535
ADMIN_ROUTE_WILDCARD = '/admin/*',
36+
37+
DOWNLOAD_COMPLETE = '/download-complete',
3638
}
3739

3840
export enum routeChildren {

0 commit comments

Comments
 (0)