Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
2bb9a5c
[PRMP-915] Add support for ODS review summary report in download func…
NogaNHS Jan 5, 2026
a5bfdfb
Add download link for ODS review summary report in ReviewsPage
NogaNHS Jan 5, 2026
c3e739a
[PRMP-915] Refactor download report functionality to support dynamic …
NogaNHS Jan 5, 2026
73de2c1
[PRMP-915] Update download report functionality to use dynamic report…
NogaNHS Jan 5, 2026
2666266
[PRMP-915] clean up
NogaNHS Jan 5, 2026
9681edb
[PRMP-915] Rename build_review_query_filter to build_review_dynamo_fi…
NogaNHS Jan 5, 2026
36bd6e1
[PRMP-915] Update report type in URL for Lloyd George summary downloads
NogaNHS Jan 5, 2026
057f9e1
[PRMP-915] clean up after rebase
NogaNHS Jan 13, 2026
abf6663
[PRMP-915] Update alignment class for document review section in Revi…
NogaNHS Jan 13, 2026
4f15361
Merge branch 'main' into PRMP-915
NogaNHS Jan 21, 2026
351876c
Merge branch 'main' into PRMP-915
NogaNHS Jan 27, 2026
e5ea340
Merge branch 'main' into PRMP-915
NogaNHS Jan 28, 2026
3d46934
[PRMP-915] Update report download link to use 'PATIENT' report type
NogaNHS Jan 28, 2026
e27b5af
[PRMP-915] Refactor navigation logic in DownloadReportSelectStage to …
NogaNHS Jan 29, 2026
a922c2d
[PRMP-915] Replace 'Go to home' link with 'Go back' button in Downloa…
NogaNHS Jan 29, 2026
d90d7b1
Remove unused BackLink import in DownloadReportSelectStage
NogaNHS Jan 29, 2026
ac2714a
Remove unused useConfig import in DownloadReportSelectStage
NogaNHS Jan 29, 2026
7a2f002
Prevent default action on download report button click in HomePage
NogaNHS Jan 29, 2026
0a7f326
Merge branch 'main' into PRMP-915
NogaNHS Jan 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe('GP Workflow: Download Lloyd George summary report', () => {

cy.wait('@downloadReportFinished', { timeout: 20000 });

cy.url().should('contain', Cypress.config('baseUrl') + `${routes.createReportComplete}?reportType=0`);
cy.url().should('contain', Cypress.config('baseUrl') + `${routes.createReportComplete}?reportType=PATIENT`);
},
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe('GP Workflow: Download Lloyd George summary report', () => {

cy.url().should(
'contain',
Cypress.config('baseUrl') + `${routes.createReportComplete}?reportType=0`,
Cypress.config('baseUrl') + `${routes.createReportComplete}?reportType=PATIENT`,
);

cy.getByTestId('logout-btn').click();
Expand Down
2 changes: 1 addition & 1 deletion app/cypress/support/e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ Cypress.Commands.add('navigateToDownloadReportPage', () => {
cy.getByTestId('download-report-btn').should('exist');
cy.getByTestId('download-report-btn').click();

cy.url().should('contain', baseUrl + `${routes.createReport}?reportType=0`);
cy.url().should('contain', baseUrl + `${routes.createReport}?reportType=PATIENT`);
});

declare global {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -732,4 +732,18 @@ describe('ReviewsPage', () => {
});
});
});

describe('Report Download Link', () => {
it('renders download report link with correct href', async () => {
renderComponent();

await waitFor(() => {
expect(screen.getByText('900 000 0001')).toBeInTheDocument();
});

const downloadLink = screen.getByText('Download a report on this data');
expect(downloadLink).toBeInTheDocument();
expect(downloadLink).toHaveAttribute('href', '/create-report?reportType=REVIEW');
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import BackButton from '../../../generic/backButton/BackButton';
import { Pagination } from '../../../generic/paginationV2/Pagination';
import SpinnerButton from '../../../generic/spinnerButton/SpinnerButton';
import SpinnerV2 from '../../../generic/spinnerV2/SpinnerV2';
import { REPORT_TYPE } from '../../../../types/generic/reports';
import { AxiosError } from 'axios';
import { errorToParams } from '../../../../helpers/utils/errorToParams';

Expand Down Expand Up @@ -292,6 +293,13 @@ export const ReviewsPage = ({ setReviewData }: ReviewsPageProps): React.JSX.Elem
</p>

<Table.Panel heading="Documents to review" className="reviews-page" allowFullScreen>
<div className="nhsuk-u-text-align-right">
<a
href={`${routes.REPORT_DOWNLOAD}?reportType=${REPORT_TYPE.ODS_REVIEW_SUMMARY}`}
>
Download a report on this data
</a>
</div>
{/* Search box */}
<form
id="search-form"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { beforeEach, describe, expect, it, vi, MockedFunction, Mock } from 'vite
const mockDownloadReport = downloadReport as MockedFunction<typeof downloadReport>;
const mockUseConfig = vi.fn();


const mockedUseNavigate = vi.fn();
vi.mock('react-router-dom', async () => {
const actual = await vi.importActual('react-router-dom');
Expand Down Expand Up @@ -46,7 +45,7 @@ describe('DownloadReportSelectStage', () => {
const report = getReportByType(REPORT_TYPE.ODS_PATIENT_SUMMARY);
render(<DownloadReportSelectStage report={report!} />);

expect(screen.getByTestId('return-to-home-button')).toBeInTheDocument();
expect(screen.getByTestId('go-back-button')).toBeInTheDocument();
const title = screen.getByTestId('title');
expect(title).toBeInTheDocument();
expect(title.innerHTML).toContain(report!.title);
Expand All @@ -56,7 +55,7 @@ describe('DownloadReportSelectStage', () => {
).toBeInTheDocument();
});
expect(screen.queryByTestId('error-notification-banner')).not.toBeInTheDocument();
expect(screen.getByText('Go to home')).toBeInTheDocument();
expect(screen.getByText('Go back')).toBeInTheDocument();
});

it('should render error notification when download fails', async () => {
Expand Down Expand Up @@ -107,33 +106,16 @@ describe('DownloadReportSelectStage', () => {
});
});

it('should navigate to home when clicking go to home link', async () => {
it('should navigate to back when clicking go back link', async () => {
const report = getReportByType(REPORT_TYPE.ODS_PATIENT_SUMMARY);
render(<DownloadReportSelectStage report={report!} />);

await userEvent.click(screen.getByText('Go to home'));
await waitFor(() => {
expect(mockedUseNavigate).toHaveBeenCalledWith(routes.HOME);
});
});

it ('should navigate to admin hub, upload version 3 enabled', async () => {
mockUseConfig.mockReturnValue({
featureFlags: { uploadDocumentIteration3Enabled: true },
});

const report = getReportByType(REPORT_TYPE.ODS_PATIENT_SUMMARY);
render(<DownloadReportSelectStage report={report!} />);

let backLink: Element;
backLink = screen.getByTestId('return-to-home-button');

backLink = screen.getByTestId('go-back-button');
expect(backLink).toHaveTextContent('Go back');

await userEvent.click(backLink);

await waitFor(() => {
expect(mockedUseNavigate).toHaveBeenCalledWith(routes.ADMIN_ROUTE);
})
expect(mockedUseNavigate).toHaveBeenCalledWith(-1);
});
});
});
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { useNavigate } from 'react-router-dom';
import { FileTypeData, ReportData } from '../../../../types/generic/reports';
import { routeChildren, routes } from '../../../../types/generic/routes';
import { BackLink, Button } from 'nhsuk-react-components';
import { Button } from 'nhsuk-react-components';
import downloadReport from '../../../../helpers/requests/downloadReport';
import useBaseAPIUrl from '../../../../helpers/hooks/useBaseAPIUrl';
import useBaseAPIHeaders from '../../../../helpers/hooks/useBaseAPIHeaders';
import useConfig from '../../../../helpers/hooks/useConfig';
import { AxiosError } from 'axios';
import { isMock } from '../../../../helpers/utils/isLocal';
import { JSX, ReactNode, useRef } from 'react';
import NotificationBanner from '../../../layout/notificationBanner/NotificationBanner';
import SpinnerButton from '../../../generic/spinnerButton/SpinnerButton';
import React from 'react';
import BackButton from '../../../generic/backButton/BackButton';

type Props = {
report: ReportData;
Expand All @@ -20,7 +20,6 @@ type Props = {
const DownloadReportSelectStage = (props: Props): JSX.Element => {
const baseUrl = useBaseAPIUrl();
const baseHeaders = useBaseAPIHeaders();
const config = useConfig();
const navigate = useNavigate();
const [downloading, setDownloading] = React.useState(false);
const [downloadError, setDownloadError] = React.useState<ReactNode>(null);
Expand All @@ -30,7 +29,6 @@ const DownloadReportSelectStage = (props: Props): JSX.Element => {
navigate(`${routeChildren.REPORT_DOWNLOAD_COMPLETE}?reportType=${props.report.reportType}`);
};

const uploadV3Enabled: boolean = !!config.featureFlags.uploadDocumentIteration3Enabled;
const noDataContent = (): JSX.Element => {
return (
<>
Expand Down Expand Up @@ -88,7 +86,12 @@ const DownloadReportSelectStage = (props: Props): JSX.Element => {
setDownloading(true);

try {
await downloadReport({ report: props.report, fileType, baseUrl, baseHeaders });
await downloadReport({
report: props.report,
fileType,
baseUrl,
baseHeaders,
});
handleSuccess();
} catch (e) {
const error = e as AxiosError;
Expand Down Expand Up @@ -129,17 +132,7 @@ const DownloadReportSelectStage = (props: Props): JSX.Element => {

return (
<>
<BackLink
data-testid="return-to-home-button"
asElement="a"
href='#'
onClick={(): void => {
uploadV3Enabled ? navigate(routes.ADMIN_ROUTE) : navigate(routes.HOME);
}}
className="mb-5"
>
{uploadV3Enabled ? 'Go back' : 'Go to home'}
</BackLink>
<BackButton dataTestid="go-back-button"></BackButton>
{downloadError && (
<NotificationBanner
title="Important"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const ReviewSummaryDescription = (): React.JSX.Element => {
return (
<>
<p>This report contains:</p>
<ul className="ml-3">
<li>
the details of pending document records stored within this service for your
organisation
</li>
</ul>
</>
);
};

export default ReviewSummaryDescription;
9 changes: 5 additions & 4 deletions app/src/helpers/requests/downloadReport.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import axios, { AxiosError } from 'axios';
import downloadReport from './downloadReport';
import { ReportData } from '../../types/generic/reports';
import { REPORT_TYPE, ReportData } from '../../types/generic/reports';
import { AuthHeaders } from '../../types/blocks/authHeaders';
import { describe, expect, it, vi, Mocked, beforeEach, afterEach, Mock } from 'vitest';
import { afterEach, beforeEach, describe, expect, it, Mock, Mocked, vi } from 'vitest';
import { Procedure } from '@vitest/spy';

vi.mock('axios');
Expand All @@ -12,6 +12,7 @@ const mockedAxios = axios as Mocked<typeof axios>;
describe('downloadReport', () => {
const report = {
endpoint: '/download',
reportType: REPORT_TYPE.ODS_REVIEW_SUMMARY,
} as ReportData;

let clickSpy: Mock<Procedure>;
Expand Down Expand Up @@ -55,7 +56,7 @@ describe('downloadReport', () => {

expect(getSpy).toHaveBeenCalledWith(args.baseUrl + report.endpoint, {
headers: args.baseHeaders,
params: { outputFileFormat: args.fileType, odsReportType: 'PATIENT' },
params: { outputFileFormat: args.fileType, odsReportType: 'REVIEW' },
});

expect(mockAnchor.setAttribute).toHaveBeenCalledWith('download', '');
Expand Down Expand Up @@ -90,7 +91,7 @@ describe('downloadReport', () => {
expect(errorCode).toBe(404);
expect(getSpy).toHaveBeenCalledWith(args.baseUrl + report.endpoint, {
headers: args.baseHeaders,
params: { outputFileFormat: args.fileType, odsReportType: 'PATIENT' },
params: { outputFileFormat: args.fileType, odsReportType: 'REVIEW' },
});
});
});
3 changes: 1 addition & 2 deletions app/src/helpers/requests/downloadReport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ type Args = {
type DownloadReportResponseData = {
data: { url: string };
};

const downloadReport = async ({ report, fileType, baseUrl, baseHeaders }: Args): Promise<void> => {
const gatewayUrl = baseUrl + report.endpoint;

Expand All @@ -23,7 +22,7 @@ const downloadReport = async ({ report, fileType, baseUrl, baseHeaders }: Args):
},
params: {
outputFileFormat: fileType,
odsReportType: 'PATIENT',
odsReportType: report.reportType,
},
});

Expand Down
2 changes: 1 addition & 1 deletion app/src/pages/adminPage/AdminPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ describe('AdminPage', (): void => {
it('renders the Download a report card with correct href', (): void => {
render(<AdminPage />);
const reportLink = screen.getByTestId('download-report-btn');
expect(reportLink).toHaveAttribute('href', '/create-report?reportType=0');
expect(reportLink).toHaveAttribute('href', '/create-report?reportType=PATIENT');
});

it('renders the Download a report card description', (): void => {
Expand Down
3 changes: 2 additions & 1 deletion app/src/pages/homePage/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ const HomePage = (): React.JSX.Element => {
<Card.Link
data-testid="download-report-btn"
href="#"
onClick={(): void => {
onClick={(e): void => {
e.preventDefault()
navigate(
`${routes.REPORT_DOWNLOAD}?reportType=${REPORT_TYPE.ODS_PATIENT_SUMMARY}`,
);
Expand Down
11 changes: 10 additions & 1 deletion app/src/types/generic/reports.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import LloydGeorgeSummaryDescription from '../../components/blocks/_downloadReport/downloadReportSelectStage/ReportDescriptions/LloydGeorgeSummaryDescription';
import { endpoints } from './endpoints';
import ReviewSummaryDescription from '../../components/blocks/_downloadReport/downloadReportSelectStage/ReportDescriptions/ReviewSummaryDescription';

export enum REPORT_TYPE {
ODS_PATIENT_SUMMARY = '0',
ODS_PATIENT_SUMMARY = 'PATIENT',
ODS_REVIEW_SUMMARY = 'REVIEW',
}

export type FileTypeData = {
Expand Down Expand Up @@ -34,4 +36,11 @@ export const reports: ReportData[] = [
reportType: REPORT_TYPE.ODS_PATIENT_SUMMARY,
endpoint: endpoints.ODS_REPORT,
},
{
title: 'Documents review summary report',
description: ReviewSummaryDescription,
fileTypes: [{ extension: 'csv', label: 'a CSV' }],
reportType: REPORT_TYPE.ODS_REVIEW_SUMMARY,
endpoint: endpoints.ODS_REPORT,
},
];
2 changes: 1 addition & 1 deletion lambdas/services/ods_report_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def get_documents_for_review(
if output_file_type != FileType.CSV:
raise OdsReportException(400, LambdaError.UnsupportedFileType)

query_filter = self.document_upload_review_service.build_review_query_filter()
query_filter = self.document_upload_review_service.build_review_dynamo_filter()

results = self.document_upload_review_service.fetch_documents_from_table(
search_key="Custodian",
Expand Down
2 changes: 1 addition & 1 deletion lambdas/tests/unit/services/test_ods_report_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ def test_get_documents_for_review(

mocker.patch.object(
ods_report_service.document_upload_review_service,
"build_review_query_filter",
"build_review_dynamo_filter",
return_value=expected_query_filter,
)

Expand Down
Loading