diff --git a/app/cypress/e2e/1-ndr-smoke-tests/gp_user_workflows/upload_lloyd_george_is_bsol_gp_admin_workflow.cy.js b/app/cypress/e2e/1-ndr-smoke-tests/gp_user_workflows/upload_lloyd_george_is_bsol_gp_admin_workflow.cy.js
index b788e6d93b..3cb50a0814 100644
--- a/app/cypress/e2e/1-ndr-smoke-tests/gp_user_workflows/upload_lloyd_george_is_bsol_gp_admin_workflow.cy.js
+++ b/app/cypress/e2e/1-ndr-smoke-tests/gp_user_workflows/upload_lloyd_george_is_bsol_gp_admin_workflow.cy.js
@@ -6,14 +6,14 @@ const workspace = Cypress.env('WORKSPACE');
const baseUrl = Cypress.config('baseUrl');
const uploadedFilePathNames = [
- 'cypress/fixtures/lg-files/zenia_lees/1of3_Lloyd_George_Record_[Zenia Ellisa LEES]_[9730153930]_[20-03-1929].pdf',
- 'cypress/fixtures/lg-files/zenia_lees/2of3_Lloyd_George_Record_[Zenia Ellisa LEES]_[9730153930]_[20-03-1929].pdf',
- 'cypress/fixtures/lg-files/zenia_lees/3of3_Lloyd_George_Record_[Zenia Ellisa LEES]_[9730153930]_[20-03-1929].pdf',
+ 'cypress/fixtures/lg-files/zenia_lees/1of3_Lloyd_George_Record_[Zenia Ellisa LEES]_[9730153930]_[20-03-1929].pdf',
+ 'cypress/fixtures/lg-files/zenia_lees/2of3_Lloyd_George_Record_[Zenia Ellisa LEES]_[9730153930]_[20-03-1929].pdf',
+ 'cypress/fixtures/lg-files/zenia_lees/3of3_Lloyd_George_Record_[Zenia Ellisa LEES]_[9730153930]_[20-03-1929].pdf',
];
const uploadedFileNames = [
- '1of3_Lloyd_George_Record_[Zenia Ellisa LEES]_[9730153930]_[20-03-1929].pdf',
- '2of3_Lloyd_George_Record_[Zenia Ellisa LEES]_[9730153930]_[20-03-1929].pdf',
- '3of3_Lloyd_George_Record_[Zenia Ellisa LEES]_[9730153930]_[20-03-1929].pdf',
+ '1of3_Lloyd_George_Record_[Zenia Ellisa LEES]_[9730153930]_[20-03-1929].pdf',
+ '2of3_Lloyd_George_Record_[Zenia Ellisa LEES]_[9730153930]_[20-03-1929].pdf',
+ '3of3_Lloyd_George_Record_[Zenia Ellisa LEES]_[9730153930]_[20-03-1929].pdf',
];
const bucketName = `${workspace}-lloyd-george-store`;
@@ -28,108 +28,111 @@ const confirmationUrl = '/patient/document-upload/confirmation';
const activePatient = pdsPatients.activeNoUpload;
describe('GP Workflow: Upload Lloyd George record', () => {
- context('Upload a Lloyd George document', () => {
- beforeEach(() => {
- //delete any records present for the active patient
- cy.deleteItemsBySecondaryKeyFromDynamoDb(
- referenceTableName,
- 'NhsNumberIndex',
- 'NhsNumber',
- activePatient.toString(),
- );
- cy.deleteItemsBySecondaryKeyFromDynamoDb(
- stitchTableName,
- 'NhsNumberIndex',
- 'NhsNumber',
- activePatient.toString()
- );
- uploadedFileNames.forEach((file) => {
- cy.deleteFileFromS3(bucketName, file);
- });
- });
-
- afterEach(() => {
- //clean up any records present for the active patient
- cy.deleteItemsBySecondaryKeyFromDynamoDb(
- referenceTableName,
- 'NhsNumberIndex',
- 'NhsNumber',
- activePatient.toString(),
- );
- cy.deleteItemsBySecondaryKeyFromDynamoDb(
- stitchTableName,
- 'NhsNumberIndex',
- 'NhsNumber',
- activePatient.toString()
- );
- uploadedFileNames.forEach((file) => {
- cy.deleteFileFromS3(bucketName, file);
- });
- });
-
- it(
- '[Smoke] GP ADMIN can upload multiple files and then view a Lloyd George record for an active patient with no record',
- { tags: 'smoke', defaultCommandTimeout: 20000 },
- () => {
- cy.smokeLogin(Roles.SMOKE_GP_ADMIN);
-
- cy.navigateToPatientSearchPage();
-
- cy.get('#nhs-number-input').should('exist');
- cy.get('#nhs-number-input').click();
- cy.get('#nhs-number-input').type(activePatient);
- cy.getByTestId('search-submit-btn').should('exist');
- cy.getByTestId('search-submit-btn').click();
-
- cy.url({ timeout: 15000 }).should('contain', patientVerifyUrl);
-
- cy.get('#verify-submit').should('exist');
- cy.get('#verify-submit').click();
-
- cy.url().should('contain', lloydGeorgeRecordUrl);
- cy.getByTestId('no-records-title').should(
- 'include.text',
- 'This patient does not have a Lloyd George record',
- );
- cy.getByTestId('upload-patient-record-button').should('exist');
- cy.getByTestId('upload-patient-record-button').click();
- uploadedFilePathNames.forEach((file) => {
- cy.getByTestId('button-input').selectFile(file, { force: true });
- var index = uploadedFilePathNames.indexOf(file);
- cy.get('#selected-documents-table').should('contain', uploadedFileNames[index]);
+ context('Upload a Lloyd George document', () => {
+ beforeEach(() => {
+ //delete any records present for the active patient
+ cy.deleteItemsBySecondaryKeyFromDynamoDb(
+ referenceTableName,
+ 'NhsNumberIndex',
+ 'NhsNumber',
+ activePatient.toString(),
+ );
+ cy.deleteItemsBySecondaryKeyFromDynamoDb(
+ stitchTableName,
+ 'NhsNumberIndex',
+ 'NhsNumber',
+ activePatient.toString(),
+ );
+ uploadedFileNames.forEach((file) => {
+ cy.deleteFileFromS3(bucketName, file);
+ });
});
- cy.get('#continue-button').click();
- cy.url().should('contain', selectOrderUrl);
- cy.get('#selected-documents-table').should('exist');
- uploadedFileNames.forEach((name) => {
- cy.get('#selected-documents-table').should('contain', name);
+ afterEach(() => {
+ //clean up any records present for the active patient
+ cy.deleteItemsBySecondaryKeyFromDynamoDb(
+ referenceTableName,
+ 'NhsNumberIndex',
+ 'NhsNumber',
+ activePatient.toString(),
+ );
+ cy.deleteItemsBySecondaryKeyFromDynamoDb(
+ stitchTableName,
+ 'NhsNumberIndex',
+ 'NhsNumber',
+ activePatient.toString(),
+ );
+ uploadedFileNames.forEach((file) => {
+ cy.deleteFileFromS3(bucketName, file);
+ });
});
- cy.getByTestId('form-submit-button').click();
- cy.url().should('contain', confirmationUrl);
- uploadedFileNames.forEach((name) => {
- cy.get('#selected-documents-table').should('contain', name);
- });
- cy.getByTestId('confirm-button').click();
-
- cy.getByTestId('upload-complete-page', { timeout: 25000 }).should('exist');
- cy.getByTestId('upload-complete-page')
- .should('include.text', 'You have successfully uploaded a digital Lloyd George record for');
-
- cy.getByTestId('upload-complete-card').should('be.visible');
-
- cy.getByTestId('home-btn').eq(1).click();
-
- cy.navigateToPatientSearchPage();
-
- cy.get('#nhs-number-input').type(activePatient);
- cy.get('#search-submit').click();
- cy.wait(5000)
-
- cy.get('.patient-results-form').submit();
-
- cy.get("#pdf-viewer", {timeout: 20000}).should('exist');
- });
- });
+ it(
+ '[Smoke] GP ADMIN can upload multiple files and then view a Lloyd George record for an active patient with no record',
+ { tags: 'smoke', defaultCommandTimeout: 20000 },
+ () => {
+ cy.smokeLogin(Roles.SMOKE_GP_ADMIN);
+
+ cy.navigateToPatientSearchPage();
+
+ cy.get('#nhs-number-input').should('exist');
+ cy.get('#nhs-number-input').click();
+ cy.get('#nhs-number-input').type(activePatient);
+ cy.getByTestId('search-submit-btn').should('exist');
+ cy.getByTestId('search-submit-btn').click();
+
+ cy.url({ timeout: 15000 }).should('contain', patientVerifyUrl);
+
+ cy.get('#verify-submit').should('exist');
+ cy.get('#verify-submit').click();
+
+ cy.url().should('contain', lloydGeorgeRecordUrl);
+ cy.getByTestId('no-records-title').should(
+ 'include.text',
+ 'This patient does not have a Lloyd George record',
+ );
+ cy.getByTestId('upload-patient-record-button').should('exist');
+ cy.getByTestId('upload-patient-record-button').click();
+ uploadedFilePathNames.forEach((file) => {
+ cy.getByTestId('button-input').selectFile(file, { force: true });
+ var index = uploadedFilePathNames.indexOf(file);
+ cy.get('#selected-documents-table').should('contain', uploadedFileNames[index]);
+ });
+ cy.get('#continue-button').click();
+
+ cy.url().should('contain', selectOrderUrl);
+ cy.get('#selected-documents-table').should('exist');
+ uploadedFileNames.forEach((name) => {
+ cy.get('#selected-documents-table').should('contain', name);
+ });
+ cy.getByTestId('form-submit-button').click();
+
+ cy.url().should('contain', confirmationUrl);
+ uploadedFileNames.forEach((name) => {
+ cy.get('#selected-documents-table').should('contain', name);
+ });
+ cy.getByTestId('confirm-button').click();
+
+ cy.getByTestId('upload-complete-page', { timeout: 25000 }).should('exist');
+ cy.getByTestId('upload-complete-page').should(
+ 'include.text',
+ 'You have successfully uploaded a digital Lloyd George record for',
+ );
+
+ cy.getByTestId('upload-complete-card').should('be.visible');
+
+ cy.getByTestId('home-btn').eq(1).click();
+
+ cy.navigateToPatientSearchPage();
+
+ cy.get('#nhs-number-input').type(activePatient);
+ cy.get('#search-submit').click();
+ cy.wait(5000);
+
+ cy.get('.patient-results-form').submit();
+
+ cy.get('#pdf-viewer', { timeout: 20000 }).should('exist');
+ },
+ );
+ });
});
diff --git a/app/cypress/support/patients.ts b/app/cypress/support/patients.ts
index fc03e13079..39e1be9ed2 100644
--- a/app/cypress/support/patients.ts
+++ b/app/cypress/support/patients.ts
@@ -1,8 +1,8 @@
export const pdsPatients = {
- activeUpload: 9730153817,
- activeNoUpload: 9730153930,
+ activeUpload: 9730153817,
+ activeNoUpload: 9730153930,
};
export const stubPatients = {
- activeUpload: 9730153817,
- activeNoUpload: 9000000068,
+ activeUpload: 9730153817,
+ activeNoUpload: 9000000068,
};
diff --git a/app/src/components/blocks/_documentUpload/documentUploadCompleteStage/DocumentUploadCompleteStage.tsx b/app/src/components/blocks/_documentUpload/documentUploadCompleteStage/DocumentUploadCompleteStage.tsx
index 0f2aeb9f6c..ae8de73e2b 100644
--- a/app/src/components/blocks/_documentUpload/documentUploadCompleteStage/DocumentUploadCompleteStage.tsx
+++ b/app/src/components/blocks/_documentUpload/documentUploadCompleteStage/DocumentUploadCompleteStage.tsx
@@ -42,12 +42,12 @@ const DocumentUploadCompleteStage = ({ documents, documentConfig }: Props): Reac
]);
useEffect(() => {
- if (!docsAreInFinishedState()) {
+ if (!docsAreInFinishedState() || patientDetails === null) {
navigate(routes.HOME);
}
- }, [navigate, documents]);
+ }, [navigate, documents, patientDetails]);
- if (!docsAreInFinishedState()) {
+ if (!docsAreInFinishedState() || patientDetails === null) {
return <>>;
}
@@ -108,7 +108,7 @@ const DocumentUploadCompleteStage = ({ documents, documentConfig }: Props): Reac
What happens next
- {journey === 'update' && (
+ {journey === 'update' && patientDetails.canManageRecord && (
You can now view the updated {documentConfig.displayName} for this patient in
this service by{' '}
diff --git a/app/src/components/blocks/_patientDocuments/documentView/DocumentView.tsx b/app/src/components/blocks/_patientDocuments/documentView/DocumentView.tsx
index 9f63905e08..b239ae8f9e 100644
--- a/app/src/components/blocks/_patientDocuments/documentView/DocumentView.tsx
+++ b/app/src/components/blocks/_patientDocuments/documentView/DocumentView.tsx
@@ -262,14 +262,14 @@ const DocumentView = ({
)}
- {documentReference.url
- ? getRecordCard()
- : (
-
- This document is currently being uploaded, please try again in a few minutes.
-
- )
- }
+ {documentReference.url ? (
+ getRecordCard()
+ ) : (
+
+ This document is currently being uploaded, please try again in a few
+ minutes.
+
+ )}
);
diff --git a/app/src/helpers/requests/downloadReport.test.ts b/app/src/helpers/requests/downloadReport.test.ts
index f7e23fbd0d..0001848c52 100644
--- a/app/src/helpers/requests/downloadReport.test.ts
+++ b/app/src/helpers/requests/downloadReport.test.ts
@@ -55,7 +55,7 @@ describe('downloadReport', () => {
expect(getSpy).toHaveBeenCalledWith(args.baseUrl + report.endpoint, {
headers: args.baseHeaders,
- params: { outputFileFormat: args.fileType, odsReportType: "PATIENT" },
+ params: { outputFileFormat: args.fileType, odsReportType: 'PATIENT' },
});
expect(mockAnchor.setAttribute).toHaveBeenCalledWith('download', '');
@@ -90,7 +90,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: 'PATIENT' },
});
});
});
diff --git a/app/src/helpers/requests/downloadReport.ts b/app/src/helpers/requests/downloadReport.ts
index 0a45f55bfe..25a213e431 100644
--- a/app/src/helpers/requests/downloadReport.ts
+++ b/app/src/helpers/requests/downloadReport.ts
@@ -23,7 +23,7 @@ const downloadReport = async ({ report, fileType, baseUrl, baseHeaders }: Args):
},
params: {
outputFileFormat: fileType,
- odsReportType: "PATIENT"
+ odsReportType: 'PATIENT',
},
});
diff --git a/app/src/helpers/requests/uploadDocument.test.ts b/app/src/helpers/requests/uploadDocument.test.ts
index b764d3c015..ba1d6fbb4f 100644
--- a/app/src/helpers/requests/uploadDocument.test.ts
+++ b/app/src/helpers/requests/uploadDocument.test.ts
@@ -613,7 +613,10 @@ describe('Upload Document Requests', () => {
birthDate: '1990-05-15',
});
- const result = generateStitchedFileName(patientDetails, docConfig as DOCUMENT_TYPE_CONFIG);
+ const result = generateStitchedFileName(
+ patientDetails,
+ docConfig as DOCUMENT_TYPE_CONFIG,
+ );
expect(result).toBe(
'1of1_Lloyd_George_Record_[John Michael SMITH]_[1234567890]_[15-05-1990].pdf',
@@ -628,9 +631,14 @@ describe('Upload Document Requests', () => {
birthDate: '1985-12-25',
});
- const result = generateStitchedFileName(patientDetails, docConfig as DOCUMENT_TYPE_CONFIG);
+ const result = generateStitchedFileName(
+ patientDetails,
+ docConfig as DOCUMENT_TYPE_CONFIG,
+ );
- expect(result).toBe('1of1_Lloyd_George_Record_[Jane DOE]_[0987654321]_[25-12-1985].pdf');
+ expect(result).toBe(
+ '1of1_Lloyd_George_Record_[Jane DOE]_[0987654321]_[25-12-1985].pdf',
+ );
});
it('handles special characters in given name by replacing them with dashes', () => {
@@ -641,7 +649,10 @@ describe('Upload Document Requests', () => {
birthDate: '1975-03-10',
});
- const result = generateStitchedFileName(patientDetails, docConfig as DOCUMENT_TYPE_CONFIG);
+ const result = generateStitchedFileName(
+ patientDetails,
+ docConfig as DOCUMENT_TYPE_CONFIG,
+ );
expect(result).toBe(
"1of1_Lloyd_George_Record_[Mary-Jane O'Connor SMITH-JONES]_[1111222233]_[10-03-1975].pdf",
@@ -656,7 +667,10 @@ describe('Upload Document Requests', () => {
birthDate: '2000-01-01',
});
- const result = generateStitchedFileName(patientDetails, docConfig as DOCUMENT_TYPE_CONFIG);
+ const result = generateStitchedFileName(
+ patientDetails,
+ docConfig as DOCUMENT_TYPE_CONFIG,
+ );
expect(result).toBe(
'1of1_Lloyd_George_Record_[Test-Name- SAMPLE*FAMILY]_[5555666677]_[01-01-2000].pdf',
@@ -671,9 +685,14 @@ describe('Upload Document Requests', () => {
birthDate: '1965-07-20',
});
- const result = generateStitchedFileName(patientDetails, docConfig as DOCUMENT_TYPE_CONFIG);
+ const result = generateStitchedFileName(
+ patientDetails,
+ docConfig as DOCUMENT_TYPE_CONFIG,
+ );
- expect(result).toBe('1of1_Lloyd_George_Record_[ ONLYFAMILY]_[9999888877]_[20-07-1965].pdf');
+ expect(result).toBe(
+ '1of1_Lloyd_George_Record_[ ONLYFAMILY]_[9999888877]_[20-07-1965].pdf',
+ );
});
it('handles birth date with single digit day and month', () => {
@@ -684,9 +703,14 @@ describe('Upload Document Requests', () => {
birthDate: '1992-02-05',
});
- const result = generateStitchedFileName(patientDetails, docConfig as DOCUMENT_TYPE_CONFIG);
+ const result = generateStitchedFileName(
+ patientDetails,
+ docConfig as DOCUMENT_TYPE_CONFIG,
+ );
- expect(result).toBe('1of1_Lloyd_George_Record_[Alex WILSON]_[1122334455]_[05-02-1992].pdf');
+ expect(result).toBe(
+ '1of1_Lloyd_George_Record_[Alex WILSON]_[1122334455]_[05-02-1992].pdf',
+ );
});
it('throws an error when patient details is null', () => {
@@ -703,7 +727,10 @@ describe('Upload Document Requests', () => {
birthDate: '1980-06-15',
});
- const result = generateStitchedFileName(patientDetails, docConfig as DOCUMENT_TYPE_CONFIG);
+ const result = generateStitchedFileName(
+ patientDetails,
+ docConfig as DOCUMENT_TYPE_CONFIG,
+ );
expect(result).toBe(
'1of1_Lloyd_George_Record_[Test-Name-With-Various-Characters-With-More---And-Finally- NORMALFAMILY]_[1234567890]_[15-06-1980].pdf',
@@ -718,7 +745,10 @@ describe('Upload Document Requests', () => {
birthDate: '1995-09-30',
});
- const result = generateStitchedFileName(patientDetails, docConfig as DOCUMENT_TYPE_CONFIG);
+ const result = generateStitchedFileName(
+ patientDetails,
+ docConfig as DOCUMENT_TYPE_CONFIG,
+ );
expect(result).toBe(
'1of1_Lloyd_George_Record_[Supercalifragilisticexpialidocious AnExtremelyLongMiddleName ANEXTREMELYLONGFAMILYNAMETHATGOESONANDON]_[1111111111]_[30-09-1995].pdf',
@@ -733,9 +763,14 @@ describe('Upload Document Requests', () => {
birthDate: 'invalid-date',
});
- const result = generateStitchedFileName(patientDetails, docConfig as DOCUMENT_TYPE_CONFIG);
+ const result = generateStitchedFileName(
+ patientDetails,
+ docConfig as DOCUMENT_TYPE_CONFIG,
+ );
- expect(result).toBe('1of1_Lloyd_George_Record_[Test USER]_[1234567890]_[NaN-NaN-NaN].pdf');
+ expect(result).toBe(
+ '1of1_Lloyd_George_Record_[Test USER]_[1234567890]_[NaN-NaN-NaN].pdf',
+ );
});
it('handles whitespace in names correctly', () => {
@@ -746,7 +781,10 @@ describe('Upload Document Requests', () => {
birthDate: '1990-01-01',
});
- const result = generateStitchedFileName(patientDetails, docConfig as DOCUMENT_TYPE_CONFIG);
+ const result = generateStitchedFileName(
+ patientDetails,
+ docConfig as DOCUMENT_TYPE_CONFIG,
+ );
expect(result).toBe(
'1of1_Lloyd_George_Record_[ John Michael SMITH ]_[1234567890]_[01-01-1990].pdf',
diff --git a/app/src/helpers/requests/uploadDocuments.ts b/app/src/helpers/requests/uploadDocuments.ts
index 2d90e811db..8977a4b05c 100644
--- a/app/src/helpers/requests/uploadDocuments.ts
+++ b/app/src/helpers/requests/uploadDocuments.ts
@@ -3,11 +3,7 @@ import { endpoints } from '../../types/generic/endpoints';
import { DOCUMENT_UPLOAD_STATE, UploadDocument } from '../../types/pages/UploadDocumentsPage/types';
import axios, { AxiosError } from 'axios';
-import {
- DocumentStatusResult,
- S3Upload,
- UploadSession,
-} from '../../types/generic/uploadResult';
+import { DocumentStatusResult, S3Upload, UploadSession } from '../../types/generic/uploadResult';
import { Dispatch, SetStateAction } from 'react';
import { extractUploadSession, setSingleDocument } from '../utils/uploadDocumentHelpers';
import { PatientDetails } from '../../types/generic/patientDetails';
diff --git a/app/src/helpers/test/testBuilders.ts b/app/src/helpers/test/testBuilders.ts
index bc0f288f76..8e58accd24 100644
--- a/app/src/helpers/test/testBuilders.ts
+++ b/app/src/helpers/test/testBuilders.ts
@@ -40,6 +40,7 @@ const buildPatientDetails = (patientDetailsOverride?: Partial):
restricted: false,
active: true,
deceased: false,
+ canManageRecord: true,
...patientDetailsOverride,
};
diff --git a/app/src/helpers/utils/documentUpload.test.ts b/app/src/helpers/utils/documentUpload.test.ts
index 4518c8dc3f..155486cfd2 100644
--- a/app/src/helpers/utils/documentUpload.test.ts
+++ b/app/src/helpers/utils/documentUpload.test.ts
@@ -1,12 +1,26 @@
-import { describe, it, expect, vi, beforeEach } from 'vitest';
-import { reduceDocumentsForUpload } from './documentUpload';
+import { describe, it, expect, vi, beforeEach, Mock } from 'vitest';
+import {
+ getUploadSession,
+ handleDocReviewStatusResult,
+ handleDocStatusResult,
+ reduceDocumentsForUpload,
+} from './documentUpload';
import { PatientDetails } from '../../types/generic/patientDetails';
-import { DOCUMENT_UPLOAD_STATE, UploadDocument } from '../../types/pages/UploadDocumentsPage/types';
+import {
+ DOCUMENT_STATUS,
+ DOCUMENT_UPLOAD_STATE,
+ UploadDocument,
+} from '../../types/pages/UploadDocumentsPage/types';
import { DOCUMENT_TYPE, DOCUMENT_TYPE_CONFIG } from './documentType';
-import { generateStitchedFileName } from '../requests/uploadDocuments';
+import uploadDocuments, { generateStitchedFileName } from '../requests/uploadDocuments';
import { zipFiles } from './zip';
+import { buildMockUploadSession } from '../test/testBuilders';
+import { uploadDocumentForReview } from '../requests/documentReview';
+import * as isLocal from './isLocal';
+import { DocumentReviewStatus } from '../../types/blocks/documentReview';
vi.mock('../requests/uploadDocuments');
+vi.mock('../requests/documentReview');
vi.mock('./zip');
vi.mock('uuid', () => ({
v4: vi.fn(() => 'mock-uuid-123'),
@@ -49,6 +63,8 @@ describe('documentUpload', () => {
const mockMergedPdfBlob = new Blob(['merged pdf content'], { type: 'application/pdf' });
beforeEach(() => {
+ import.meta.env.VITE_ENVIRONMENT = 'vitest';
+ vi.spyOn(isLocal, 'isLocal', 'get').mockReturnValue(false);
vi.clearAllMocks();
});
@@ -201,4 +217,435 @@ describe('documentUpload', () => {
expect(result[0].file.name).toBe('empty_documents_(0).zip');
});
});
+
+ describe('getUploadSession', () => {
+ const baseUrl = 'https://api.example.com';
+ const baseHeaders = { Authorization: 'Bearer token', 'Content-Type': 'application/json' };
+ const mockSetDocuments = vi.fn();
+
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ it('should return mock upload session when isLocal is true', async () => {
+ vi.spyOn(isLocal, 'isLocal', 'get').mockReturnValueOnce(true);
+
+ const result = await getUploadSession(
+ mockPatientDetails,
+ baseUrl,
+ baseHeaders,
+ [],
+ mockDocuments,
+ mockSetDocuments,
+ );
+
+ expect(result).toEqual(buildMockUploadSession(mockDocuments));
+ });
+
+ it('should call uploadDocuments when user can manage patient record', async () => {
+ const patientWithPermission = {
+ ...mockPatientDetails,
+ canManageRecord: true,
+ };
+
+ const mockUploadSession = {
+ doc1: { url: 'presigned-url-1' },
+ doc2: { url: 'presigned-url-2' },
+ };
+
+ vi.mocked(uploadDocuments).mockResolvedValue(mockUploadSession);
+
+ const existingDocs = [{ ...mockDocuments[0], id: 'existing-doc-id' }];
+
+ const result = await getUploadSession(
+ patientWithPermission,
+ baseUrl,
+ baseHeaders,
+ existingDocs,
+ mockDocuments,
+ mockSetDocuments,
+ );
+
+ expect(uploadDocuments).toHaveBeenCalledWith({
+ nhsNumber: patientWithPermission.nhsNumber,
+ documents: mockDocuments,
+ baseUrl,
+ baseHeaders,
+ documentReferenceId: 'existing-doc-id',
+ });
+ expect(result).toEqual(mockUploadSession);
+ });
+
+ it('should call uploadDocumentForReview when user cannot manage patient record', async () => {
+ const patientWithoutPermission = {
+ ...mockPatientDetails,
+ canManageRecord: false,
+ };
+
+ const mockReviewDocs = [
+ {
+ id: 'review-id-1',
+ version: 'v1',
+ files: [{ presignedUrl: 'review-url-1' }],
+ },
+ {
+ id: 'review-id-2',
+ version: 'v2',
+ files: [{ presignedUrl: 'review-url-2' }],
+ },
+ ];
+
+ vi.mocked(uploadDocumentForReview)
+ .mockResolvedValueOnce(mockReviewDocs[0] as any)
+ .mockResolvedValueOnce(mockReviewDocs[1] as any);
+
+ const result = await getUploadSession(
+ patientWithoutPermission,
+ baseUrl,
+ baseHeaders,
+ [],
+ mockDocuments,
+ mockSetDocuments,
+ );
+
+ expect(uploadDocumentForReview).toHaveBeenCalledTimes(2);
+ expect(uploadDocumentForReview).toHaveBeenCalledWith({
+ nhsNumber: patientWithoutPermission.nhsNumber,
+ document: mockDocuments[0],
+ baseUrl,
+ baseHeaders,
+ });
+ expect(mockSetDocuments).toHaveBeenCalled();
+ expect(result).toEqual({
+ 'review-id-1': { url: 'review-url-1' },
+ 'review-id-2': { url: 'review-url-2' },
+ });
+ });
+
+ it('should update document ids and versions when uploading for review', async () => {
+ const patientWithoutPermission = {
+ ...mockPatientDetails,
+ canManageRecord: false,
+ };
+
+ const mockReview = {
+ id: 'new-review-id',
+ version: 'new-version',
+ files: [{ presignedUrl: 'new-presigned-url' }],
+ };
+
+ vi.mocked(uploadDocumentForReview).mockResolvedValue(mockReview as any);
+
+ await getUploadSession(
+ patientWithoutPermission,
+ baseUrl,
+ baseHeaders,
+ [],
+ [mockDocuments[0]],
+ mockSetDocuments,
+ );
+
+ const setDocumentsCall = mockSetDocuments.mock.calls[0][0];
+ expect(setDocumentsCall[0].id).toBe('new-review-id');
+ expect(setDocumentsCall[0].versionId).toBe('new-version');
+ });
+ });
+
+ describe('handleDocStatusResult', () => {
+ const mockSetDocuments = vi.fn();
+
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ it('should update document state to SUCCEEDED when status is FINAL', () => {
+ const documentStatusResult = {
+ 'doc-ref-1': { status: DOCUMENT_STATUS.FINAL as const },
+ };
+
+ const mockDocs: UploadDocument[] = [
+ {
+ ...mockDocuments[0],
+ ref: 'doc-ref-1',
+ state: DOCUMENT_UPLOAD_STATE.UPLOADING,
+ },
+ ];
+
+ mockSetDocuments.mockImplementation((updateFn) => {
+ const result = updateFn(mockDocs);
+ expect(result[0].state).toBe(DOCUMENT_UPLOAD_STATE.SUCCEEDED);
+ });
+
+ handleDocStatusResult(documentStatusResult, mockSetDocuments);
+
+ expect(mockSetDocuments).toHaveBeenCalledTimes(1);
+ });
+
+ it('should update document state to INFECTED when status is INFECTED', () => {
+ const documentStatusResult = {
+ 'doc-ref-1': { status: DOCUMENT_STATUS.INFECTED as const },
+ };
+
+ const mockDocs: UploadDocument[] = [
+ {
+ ...mockDocuments[0],
+ ref: 'doc-ref-1',
+ state: DOCUMENT_UPLOAD_STATE.UPLOADING,
+ },
+ ];
+
+ mockSetDocuments.mockImplementation((updateFn) => {
+ const result = updateFn(mockDocs);
+ expect(result[0].state).toBe(DOCUMENT_UPLOAD_STATE.INFECTED);
+ });
+
+ handleDocStatusResult(documentStatusResult, mockSetDocuments);
+
+ expect(mockSetDocuments).toHaveBeenCalledTimes(1);
+ });
+
+ it('should update document state to ERROR when status is NOT_FOUND', () => {
+ const documentStatusResult = {
+ 'doc-ref-1': { status: DOCUMENT_STATUS.NOT_FOUND as const, error_code: 'ERR_404' },
+ };
+
+ const mockDocs: UploadDocument[] = [
+ {
+ ...mockDocuments[0],
+ ref: 'doc-ref-1',
+ state: DOCUMENT_UPLOAD_STATE.UPLOADING,
+ },
+ ];
+
+ mockSetDocuments.mockImplementation((updateFn) => {
+ const result = updateFn(mockDocs);
+ expect(result[0].state).toBe(DOCUMENT_UPLOAD_STATE.ERROR);
+ expect(result[0].errorCode).toBe('ERR_404');
+ });
+
+ handleDocStatusResult(documentStatusResult, mockSetDocuments);
+
+ expect(mockSetDocuments).toHaveBeenCalledTimes(1);
+ });
+
+ it('should update document state to ERROR when status is CANCELLED', () => {
+ const documentStatusResult = {
+ 'doc-ref-1': {
+ status: DOCUMENT_STATUS.CANCELLED as const,
+ error_code: 'ERR_CANCELLED',
+ },
+ };
+
+ const mockDocs: UploadDocument[] = [
+ {
+ ...mockDocuments[0],
+ ref: 'doc-ref-1',
+ state: DOCUMENT_UPLOAD_STATE.UPLOADING,
+ },
+ ];
+
+ mockSetDocuments.mockImplementation((updateFn) => {
+ const result = updateFn(mockDocs);
+ expect(result[0].state).toBe(DOCUMENT_UPLOAD_STATE.ERROR);
+ expect(result[0].errorCode).toBe('ERR_CANCELLED');
+ });
+
+ handleDocStatusResult(documentStatusResult, mockSetDocuments);
+
+ expect(mockSetDocuments).toHaveBeenCalledTimes(1);
+ });
+
+ it('should handle multiple documents with different statuses', () => {
+ const documentStatusResult = {
+ 'doc-ref-1': { status: DOCUMENT_STATUS.FINAL as const },
+ 'doc-ref-2': { status: DOCUMENT_STATUS.INFECTED as const },
+ 'doc-ref-3': { status: DOCUMENT_STATUS.NOT_FOUND as const, error_code: 'ERR_404' },
+ };
+
+ const mockDocs: UploadDocument[] = [
+ { ...mockDocuments[0], ref: 'doc-ref-1', state: DOCUMENT_UPLOAD_STATE.UPLOADING },
+ { ...mockDocuments[1], ref: 'doc-ref-2', state: DOCUMENT_UPLOAD_STATE.UPLOADING },
+ { ...mockDocuments[0], ref: 'doc-ref-3', state: DOCUMENT_UPLOAD_STATE.UPLOADING },
+ ];
+
+ mockSetDocuments.mockImplementation((updateFn) => {
+ const result = updateFn(mockDocs);
+ expect(result[0].state).toBe(DOCUMENT_UPLOAD_STATE.SUCCEEDED);
+ expect(result[1].state).toBe(DOCUMENT_UPLOAD_STATE.INFECTED);
+ expect(result[2].state).toBe(DOCUMENT_UPLOAD_STATE.ERROR);
+ expect(result[2].errorCode).toBe('ERR_404');
+ });
+
+ handleDocStatusResult(documentStatusResult, mockSetDocuments);
+
+ expect(mockSetDocuments).toHaveBeenCalledTimes(1);
+ });
+
+ it('should not modify documents without matching refs', () => {
+ const documentStatusResult = {
+ 'doc-ref-1': { status: DOCUMENT_STATUS.FINAL as const },
+ };
+
+ const mockDocs: UploadDocument[] = [
+ { ...mockDocuments[0], ref: 'doc-ref-2', state: DOCUMENT_UPLOAD_STATE.UPLOADING },
+ ];
+
+ mockSetDocuments.mockImplementation((updateFn) => {
+ const result = updateFn(mockDocs);
+ expect(result[0].state).toBe(DOCUMENT_UPLOAD_STATE.UPLOADING);
+ });
+
+ handleDocStatusResult(documentStatusResult, mockSetDocuments);
+
+ expect(mockSetDocuments).toHaveBeenCalledTimes(1);
+ });
+
+ it('should preserve other document properties when updating state', () => {
+ const documentStatusResult = {
+ 'doc-ref-1': { status: DOCUMENT_STATUS.FINAL as const },
+ };
+
+ const mockDocs: UploadDocument[] = [
+ {
+ ...mockDocuments[0],
+ ref: 'doc-ref-1',
+ state: DOCUMENT_UPLOAD_STATE.UPLOADING,
+ progress: 75,
+ },
+ ];
+
+ mockSetDocuments.mockImplementation((updateFn) => {
+ const result = updateFn(mockDocs);
+ expect(result[0].progress).toBe(75);
+ expect(result[0].docType).toBe(DOCUMENT_TYPE.LLOYD_GEORGE);
+ expect(result[0].file).toBe(mockDocs[0].file);
+ });
+
+ handleDocStatusResult(documentStatusResult, mockSetDocuments);
+
+ expect(mockSetDocuments).toHaveBeenCalledTimes(1);
+ });
+ });
+
+ describe('handleDocReviewStatusResult', () => {
+ const mockSetDocuments = vi.fn();
+
+ const baseDoc: UploadDocument = {
+ id: 'doc1',
+ file: new File(['content1'], 'file1.pdf', { type: 'application/pdf' }),
+ state: DOCUMENT_UPLOAD_STATE.UPLOADING,
+ progress: 0,
+ docType: DOCUMENT_TYPE.LLOYD_GEORGE,
+ attempts: 0,
+ versionId: 'v1',
+ };
+
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ it('should update document state to SUCCEEDED when status is PENDING_REVIEW', () => {
+ const reviewStatusDto = {
+ id: 'doc1',
+ status: DocumentReviewStatus.PENDING_REVIEW,
+ };
+
+ mockSetDocuments.mockImplementation((updateFn) => {
+ const result = updateFn([baseDoc]);
+ expect(result[0].state).toBe(DOCUMENT_UPLOAD_STATE.SUCCEEDED);
+ });
+
+ // 1 = DocumentReviewStatus.PENDING_REVIEW
+ handleDocReviewStatusResult(reviewStatusDto as any, mockSetDocuments);
+
+ expect(mockSetDocuments).toHaveBeenCalledTimes(1);
+ });
+
+ it('should update document state to INFECTED when status is VIRUS_SCAN_FAILED', () => {
+ const reviewStatusDto = {
+ id: 'doc1',
+ status: DocumentReviewStatus.VIRUS_SCAN_FAILED,
+ };
+
+ mockSetDocuments.mockImplementation((updateFn) => {
+ const result = updateFn([baseDoc]);
+ expect(result[0].state).toBe(DOCUMENT_UPLOAD_STATE.INFECTED);
+ });
+
+ // 2 = DocumentReviewStatus.VIRUS_SCAN_FAILED
+ handleDocReviewStatusResult(reviewStatusDto as any, mockSetDocuments);
+
+ expect(mockSetDocuments).toHaveBeenCalledTimes(1);
+ });
+
+ it('should update document state to SCANNING when status is REVIEW_PENDING_UPLOAD', () => {
+ const reviewStatusDto = {
+ id: 'doc1',
+ status: DocumentReviewStatus.REVIEW_PENDING_UPLOAD,
+ };
+
+ mockSetDocuments.mockImplementation((updateFn) => {
+ const result = updateFn([baseDoc]);
+ expect(result[0].state).toBe(DOCUMENT_UPLOAD_STATE.SCANNING);
+ });
+
+ handleDocReviewStatusResult(reviewStatusDto as any, mockSetDocuments);
+
+ expect(mockSetDocuments).toHaveBeenCalledTimes(1);
+ });
+
+ it('should update document state to ERROR and set errorCode for unknown status', () => {
+ const reviewStatusDto = {
+ id: 'doc1',
+ status: 'unknown',
+ reviewReason: 'Some error reason',
+ };
+
+ mockSetDocuments.mockImplementation((updateFn) => {
+ const result = updateFn([baseDoc]);
+ expect(result[0].state).toBe(DOCUMENT_UPLOAD_STATE.ERROR);
+ expect(result[0].errorCode).toBe('Some error reason');
+ });
+
+ handleDocReviewStatusResult(reviewStatusDto as any, mockSetDocuments);
+
+ expect(mockSetDocuments).toHaveBeenCalledTimes(1);
+ });
+
+ it('should not modify documents with non-matching ids', () => {
+ const reviewStatusDto = {
+ id: 'other-doc',
+ status: DocumentReviewStatus.PENDING_REVIEW,
+ };
+
+ mockSetDocuments.mockImplementation((updateFn) => {
+ const result = updateFn([baseDoc]);
+ expect(result[0].state).toBe(DOCUMENT_UPLOAD_STATE.UPLOADING);
+ });
+
+ handleDocReviewStatusResult(reviewStatusDto as any, mockSetDocuments);
+
+ expect(mockSetDocuments).toHaveBeenCalledTimes(1);
+ });
+
+ it('should preserve other document properties when updating state', () => {
+ const reviewStatusDto = {
+ id: 'doc1',
+ status: DocumentReviewStatus.PENDING_REVIEW,
+ };
+
+ const docWithProps = { ...baseDoc, progress: 55, attempts: 2 };
+
+ mockSetDocuments.mockImplementation((updateFn) => {
+ const result = updateFn([docWithProps]);
+ expect(result[0].progress).toBe(55);
+ expect(result[0].attempts).toBe(2);
+ expect(result[0].file).toBe(docWithProps.file);
+ });
+
+ handleDocReviewStatusResult(reviewStatusDto as any, mockSetDocuments);
+
+ expect(mockSetDocuments).toHaveBeenCalledTimes(1);
+ });
+ });
});
diff --git a/app/src/helpers/utils/documentUpload.ts b/app/src/helpers/utils/documentUpload.ts
index 3ac49132a7..616c98b1f9 100644
--- a/app/src/helpers/utils/documentUpload.ts
+++ b/app/src/helpers/utils/documentUpload.ts
@@ -1,9 +1,24 @@
import { PatientDetails } from '../../types/generic/patientDetails';
-import { DOCUMENT_UPLOAD_STATE, UploadDocument } from '../../types/pages/UploadDocumentsPage/types';
-import { generateStitchedFileName } from '../requests/uploadDocuments';
+import {
+ DOCUMENT_STATUS,
+ DOCUMENT_UPLOAD_STATE,
+ UploadDocument,
+} from '../../types/pages/UploadDocumentsPage/types';
+import uploadDocuments, { generateStitchedFileName } from '../requests/uploadDocuments';
import { DOCUMENT_TYPE, DOCUMENT_TYPE_CONFIG } from './documentType';
import { v4 as uuidv4 } from 'uuid';
import { zipFiles } from './zip';
+import { isLocal } from './isLocal';
+import { buildMockUploadSession } from '../test/testBuilders';
+import { DocumentStatusResult, UploadSession } from '../../types/generic/uploadResult';
+import { AuthHeaders } from '../../types/blocks/authHeaders';
+import {
+ DocumentReviewDto,
+ DocumentReviewStatus,
+ DocumentReviewStatusDto,
+} from '../../types/blocks/documentReview';
+import { uploadDocumentForReview } from '../requests/documentReview';
+import { Dispatch, SetStateAction } from 'react';
export const reduceDocumentsForUpload = async (
documents: UploadDocument[],
@@ -49,3 +64,125 @@ export const reduceDocumentsForUpload = async (
return documents;
};
+
+export const getUploadSession = async (
+ patientDetails: PatientDetails,
+ baseUrl: string,
+ baseHeaders: AuthHeaders,
+ existingDocuments: UploadDocument[],
+ documents: UploadDocument[],
+ setDocuments: Dispatch>,
+): Promise => {
+ if (isLocal) {
+ return buildMockUploadSession(documents);
+ } else if (patientDetails?.canManageRecord) {
+ return await uploadDocuments({
+ nhsNumber: patientDetails.nhsNumber,
+ documents: documents,
+ baseUrl,
+ baseHeaders,
+ documentReferenceId: existingDocuments[0]?.id,
+ });
+ } else {
+ const uploadSession: UploadSession = {};
+ const requests: Promise[] = [];
+
+ const reviewDocs = documents.map((document) => {
+ const documentReview = uploadDocumentForReview({
+ nhsNumber: patientDetails.nhsNumber,
+ document,
+ baseUrl,
+ baseHeaders,
+ });
+
+ documentReview.then((review: DocumentReviewDto) => {
+ document.id = review.id;
+ document.versionId = review.version;
+ uploadSession[review.id] = {
+ url: review.files[0].presignedUrl,
+ };
+ });
+
+ requests.push(documentReview);
+
+ return document;
+ });
+
+ await Promise.all(requests);
+
+ setDocuments(reviewDocs);
+
+ return uploadSession;
+ }
+};
+
+export const handleDocStatusResult = (
+ documentStatusResult: DocumentStatusResult,
+ setDocuments: Dispatch>,
+): void => {
+ setDocuments((previousState) =>
+ previousState.map((doc) => {
+ const docStatus = documentStatusResult[doc.ref!];
+
+ const updatedDoc = {
+ ...doc,
+ };
+
+ switch (docStatus?.status) {
+ case DOCUMENT_STATUS.FINAL:
+ updatedDoc.state = DOCUMENT_UPLOAD_STATE.SUCCEEDED;
+ break;
+
+ case DOCUMENT_STATUS.INFECTED:
+ updatedDoc.state = DOCUMENT_UPLOAD_STATE.INFECTED;
+ break;
+
+ case DOCUMENT_STATUS.NOT_FOUND:
+ case DOCUMENT_STATUS.CANCELLED:
+ updatedDoc.state = DOCUMENT_UPLOAD_STATE.ERROR;
+ updatedDoc.errorCode = docStatus.error_code;
+ break;
+ }
+
+ return updatedDoc;
+ }),
+ );
+};
+
+export const handleDocReviewStatusResult = (
+ result: DocumentReviewStatusDto,
+ setDocuments: Dispatch>,
+): void => {
+ setDocuments((previousState) =>
+ previousState.map((doc) => {
+ if (doc.id !== result.id) {
+ return doc;
+ }
+
+ const updatedDoc = {
+ ...doc,
+ };
+
+ switch (result.reviewStatus) {
+ case DocumentReviewStatus.PENDING_REVIEW:
+ updatedDoc.state = DOCUMENT_UPLOAD_STATE.SUCCEEDED;
+ break;
+
+ case DocumentReviewStatus.VIRUS_SCAN_FAILED:
+ updatedDoc.state = DOCUMENT_UPLOAD_STATE.INFECTED;
+ break;
+
+ case DocumentReviewStatus.REVIEW_PENDING_UPLOAD:
+ updatedDoc.state = DOCUMENT_UPLOAD_STATE.SCANNING;
+ break;
+
+ default:
+ updatedDoc.state = DOCUMENT_UPLOAD_STATE.ERROR;
+ updatedDoc.errorCode = result.reviewReason;
+ break;
+ }
+
+ return updatedDoc;
+ }),
+ );
+};
diff --git a/app/src/pages/documentUploadPage/DocumentUploadPage.tsx b/app/src/pages/documentUploadPage/DocumentUploadPage.tsx
index af698df941..abd2bf87d8 100644
--- a/app/src/pages/documentUploadPage/DocumentUploadPage.tsx
+++ b/app/src/pages/documentUploadPage/DocumentUploadPage.tsx
@@ -13,7 +13,7 @@ import useBaseAPIHeaders from '../../helpers/hooks/useBaseAPIHeaders';
import useBaseAPIUrl from '../../helpers/hooks/useBaseAPIUrl';
import useConfig from '../../helpers/hooks/useConfig';
import usePatient from '../../helpers/hooks/usePatient';
-import uploadDocuments, {
+import {
getDocumentStatus,
uploadDocumentToS3,
} from '../../helpers/requests/uploadDocuments';
@@ -30,9 +30,8 @@ import {
useEnhancedNavigate,
} from '../../helpers/utils/urlManipulations';
import { routeChildren, routes } from '../../types/generic/routes';
-import { DocumentStatusResult, UploadSession } from '../../types/generic/uploadResult';
+import { UploadSession } from '../../types/generic/uploadResult';
import {
- DOCUMENT_STATUS,
DOCUMENT_UPLOAD_STATE,
ExistingDocument,
LocationParams,
@@ -40,9 +39,16 @@ import {
UploadDocument,
} from '../../types/pages/UploadDocumentsPage/types';
import { DOCUMENT_TYPE, getConfigForDocType } from '../../helpers/utils/documentType';
-import { buildMockUploadSession } from '../../helpers/test/testBuilders';
-import { reduceDocumentsForUpload } from '../../helpers/utils/documentUpload';
+import {
+ getUploadSession,
+ handleDocReviewStatusResult,
+ handleDocStatusResult,
+ reduceDocumentsForUpload,
+} from '../../helpers/utils/documentUpload';
import DocumentUploadIndex from '../../components/blocks/_documentUpload/documentUploadIndex/DocumentUploadIndex';
+import {
+ getDocumentReviewStatus,
+} from '../../helpers/requests/documentReview';
const DocumentUploadPage = (): React.JSX.Element => {
const patientDetails = usePatient();
@@ -158,7 +164,7 @@ const DocumentUploadPage = (): React.JSX.Element => {
setExistingDocuments(newDocuments);
};
- const uploadSingleLloydGeorgeDocument = async (
+ const uploadSingleDocument = async (
document: UploadDocument,
uploadSession: UploadSession,
): Promise => {
@@ -190,7 +196,7 @@ const DocumentUploadPage = (): React.JSX.Element => {
uploadSession: UploadSession,
): void => {
uploadDocuments.forEach((document) => {
- void uploadSingleLloydGeorgeDocument(document, uploadSession);
+ void uploadSingleDocument(document, uploadSession);
});
};
@@ -213,15 +219,14 @@ const DocumentUploadPage = (): React.JSX.Element => {
const startUpload = async (): Promise => {
try {
- const uploadSession: UploadSession = isLocal
- ? buildMockUploadSession(documents)
- : await uploadDocuments({
- nhsNumber,
- documents: documents,
- baseUrl,
- baseHeaders,
- documentReferenceId: existingDocuments[0]?.id,
- });
+ const uploadSession: UploadSession = await getUploadSession(
+ patientDetails!,
+ baseUrl,
+ baseHeaders,
+ existingDocuments,
+ documents,
+ setDocuments,
+ );
setUploadSession(uploadSession);
const uploadingDocuments = markDocumentsAsUploading(documents, uploadSession);
@@ -252,36 +257,6 @@ const DocumentUploadPage = (): React.JSX.Element => {
}
};
- const handleDocStatusResult = (documentStatusResult: DocumentStatusResult): void => {
- setDocuments((previousState) =>
- previousState.map((doc) => {
- const docStatus = documentStatusResult[doc.ref!];
-
- const updatedDoc = {
- ...doc,
- };
-
- switch (docStatus?.status) {
- case DOCUMENT_STATUS.FINAL:
- updatedDoc.state = DOCUMENT_UPLOAD_STATE.SUCCEEDED;
- break;
-
- case DOCUMENT_STATUS.INFECTED:
- updatedDoc.state = DOCUMENT_UPLOAD_STATE.INFECTED;
- break;
-
- case DOCUMENT_STATUS.NOT_FOUND:
- case DOCUMENT_STATUS.CANCELLED:
- updatedDoc.state = DOCUMENT_UPLOAD_STATE.ERROR;
- updatedDoc.errorCode = docStatus.error_code;
- break;
- }
-
- return updatedDoc;
- }),
- );
- };
-
const startIntervalTimer = (uploadDocuments: Array): number => {
return window.setInterval(async () => {
interval.current = interval.current + 1;
@@ -314,14 +289,25 @@ const DocumentUploadPage = (): React.JSX.Element => {
setDocuments(updatedDocuments);
} else {
try {
- const documentStatusResult = await getDocumentStatus({
- documents: uploadDocuments,
- baseUrl,
- baseHeaders,
- nhsNumber,
- });
-
- handleDocStatusResult(documentStatusResult);
+ if (patientDetails?.canManageRecord) {
+ const documentStatusResult = await getDocumentStatus({
+ documents: uploadDocuments,
+ baseUrl,
+ baseHeaders,
+ nhsNumber,
+ });
+
+ handleDocStatusResult(documentStatusResult, setDocuments);
+ } else {
+ uploadDocuments.forEach(async (document) => {
+ void getDocumentReviewStatus({
+ document,
+ baseUrl,
+ baseHeaders,
+ nhsNumber,
+ }).then((result) => handleDocReviewStatusResult(result, setDocuments));
+ });
+ }
} catch (e) {
const error = e as AxiosError;
navigate(routes.SERVER_ERROR + errorToParams(error));
diff --git a/app/src/pages/patientResultPage/PatientResultPage.test.tsx b/app/src/pages/patientResultPage/PatientResultPage.test.tsx
index aa80bd310f..bd3c3e5ad4 100644
--- a/app/src/pages/patientResultPage/PatientResultPage.test.tsx
+++ b/app/src/pages/patientResultPage/PatientResultPage.test.tsx
@@ -258,6 +258,33 @@ describe('PatientResultPage', () => {
});
});
+ it('navigates to upload page after user selects patient they cannot manage when role is GP Clinical and feature flag is enabled', async () => {
+ const patient = buildPatientDetails({ canManageRecord: false });
+
+ mockedUsePatient.mockReturnValue(patient);
+ mockedUseRole.mockReturnValue(REPOSITORY_ROLE.GP_CLINICAL);
+ mockedUseConfig.mockReturnValue({
+ featureFlags: {
+ uploadLambdaEnabled: true,
+ uploadArfWorkflowEnabled: false,
+ uploadLloydGeorgeWorkflowEnabled: true,
+ uploadDocumentIteration3Enabled: true,
+ },
+ mockLocal: {},
+ });
+
+ render();
+ await userEvent.click(
+ screen.getByRole('button', {
+ name: CONFIRM_BUTTON_TEXT,
+ }),
+ );
+
+ await waitFor(() => {
+ expect(mockedUseNavigate).toHaveBeenCalledWith(routes.DOCUMENT_UPLOAD);
+ });
+ });
+
it.each([REPOSITORY_ROLE.GP_ADMIN, REPOSITORY_ROLE.GP_CLINICAL])(
"navigates to Lloyd George Record page after user selects Active patient, when role is '%s' and uploadDocumentIteration3Enabled is false",
async (role) => {
@@ -276,7 +303,7 @@ describe('PatientResultPage', () => {
);
it.each([REPOSITORY_ROLE.GP_ADMIN, REPOSITORY_ROLE.GP_CLINICAL])(
- "navigates to patient documents page after user selects Active patient, when role is '%s' and uploadDocumentIteration3Enabled is true",
+ "navigates to patient documents page after user selects Active patient and canManageRecord, when role is '%s' and uploadDocumentIteration3Enabled is true",
async (role) => {
const patient = buildPatientDetails({ active: true });
mockedUseRole.mockReturnValue(role);
diff --git a/app/src/pages/patientResultPage/PatientResultPage.tsx b/app/src/pages/patientResultPage/PatientResultPage.tsx
index 0709b302ed..1505cad42d 100644
--- a/app/src/pages/patientResultPage/PatientResultPage.tsx
+++ b/app/src/pages/patientResultPage/PatientResultPage.tsx
@@ -37,6 +37,11 @@ const PatientResultPage = (): React.JSX.Element => {
return;
}
+ if (!patientDetails.canManageRecord && featureFlags.uploadDocumentIteration3Enabled) {
+ navigate(routes.DOCUMENT_UPLOAD);
+ return;
+ }
+
if (patientDetails?.active) {
navigate(
featureFlags.uploadDocumentIteration3Enabled
diff --git a/app/src/types/blocks/documentReview.ts b/app/src/types/blocks/documentReview.ts
index 8e808f295c..b41fb15235 100644
--- a/app/src/types/blocks/documentReview.ts
+++ b/app/src/types/blocks/documentReview.ts
@@ -15,7 +15,7 @@ type DocumentReviewFile = {
export type DocumentReviewStatusDto = {
id: string;
- status: string;
+ reviewStatus: string;
version: string;
reviewReason: string;
};
diff --git a/app/src/types/generic/patientDetails.ts b/app/src/types/generic/patientDetails.ts
index 1905c9bced..2df0fac4d3 100644
--- a/app/src/types/generic/patientDetails.ts
+++ b/app/src/types/generic/patientDetails.ts
@@ -8,4 +8,5 @@ export type PatientDetails = {
restricted: boolean;
active: boolean;
deceased: boolean;
+ canManageRecord?: boolean;
};
diff --git a/app/src/types/generic/uploadResult.ts b/app/src/types/generic/uploadResult.ts
index 42badf415c..b8f0911397 100644
--- a/app/src/types/generic/uploadResult.ts
+++ b/app/src/types/generic/uploadResult.ts
@@ -4,7 +4,7 @@ export type UploadSession = {
export type S3Upload = {
url: string;
- fields: S3UploadFields;
+ fields?: S3UploadFields;
};
export type S3UploadFields = {