Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -73,12 +73,9 @@ describe('DocumentUploadCompleteStage', () => {
expect(screen.getByTestId('dob').textContent).toEqual('Date of birth: ' + expectedDob);

expect(
screen.queryByText(
'You are not the data controller',
{
exact: false,
}
),
screen.queryByText('You are not the data controller', {
exact: false,
}),
).not.toBeInTheDocument();
});

Expand Down Expand Up @@ -123,9 +120,29 @@ describe('DocumentUploadCompleteStage', () => {
});
});

it('should navigate to patient documents if partial upload complete', async () => {
documents.push({
docType: DOCUMENT_TYPE.LLOYD_GEORGE,
id: '2',
file: buildLgFile(2),
attempts: 0,
state: DOCUMENT_UPLOAD_STATE.ERROR,
numPages: 3,
position: 2,
});

renderApp(documents);

await userEvent.click(screen.getByTestId('patient-docs-btn'));

await waitFor(async () => {
expect(mockNavigate).toHaveBeenCalledWith(routes.PATIENT_DOCUMENTS, { replace: true });
});
});

it.each([
{ docState: DOCUMENT_UPLOAD_STATE.SUCCEEDED, expectedTitle: 'Upload complete' },
{ docState: DOCUMENT_UPLOAD_STATE.FAILED, expectedTitle: 'Upload partially complete' },
{ docState: DOCUMENT_UPLOAD_STATE.ERROR, expectedTitle: 'Upload partially complete' },
])('should set the page title based on upload success', async ({ docState, expectedTitle }) => {
documents = [
{
Expand All @@ -150,7 +167,7 @@ describe('DocumentUploadCompleteStage', () => {
id: '2',
file: buildLgFile(2),
attempts: 0,
state: DOCUMENT_UPLOAD_STATE.FAILED,
state: DOCUMENT_UPLOAD_STATE.ERROR,
numPages: 3,
position: 2,
});
Expand All @@ -172,16 +189,13 @@ describe('DocumentUploadCompleteStage', () => {
renderApp(documents);

expect(
screen.getByText(
'You are not the data controller',
{
exact: false,
}
),
screen.getByText('You are not the data controller', {
exact: false,
}),
).toBeInTheDocument();
});

const renderApp = (documents: UploadDocument[]) => {
const renderApp = (documents: UploadDocument[]): void => {
render(
<MemoryRouter>
<DocumentUploadCompleteStage documents={documents} documentConfig={docConfig} />,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,13 @@ const DocumentUploadCompleteStage = ({ documents, documentConfig }: Props): Reac
const journey = getJourney();
const [showFiles, setShowFiles] = useState(false);

const failedDocuments = documents.filter((doc) => doc.state === DOCUMENT_UPLOAD_STATE.FAILED);
const failedDocuments = documents.filter((doc) => doc.state === DOCUMENT_UPLOAD_STATE.ERROR);

const pageTitle = failedDocuments.length > 0 ? 'Upload partially complete' : 'Upload complete';
useTitle({ pageTitle });

const docsAreInFinishedState = () =>
allDocsHaveState(documents, [
DOCUMENT_UPLOAD_STATE.SUCCEEDED,
DOCUMENT_UPLOAD_STATE.FAILED,
]);
const docsAreInFinishedState = (): boolean =>
allDocsHaveState(documents, [DOCUMENT_UPLOAD_STATE.SUCCEEDED, DOCUMENT_UPLOAD_STATE.ERROR]);

useEffect(() => {
if (!docsAreInFinishedState() || patientDetails === null) {
Expand All @@ -66,13 +63,12 @@ const DocumentUploadCompleteStage = ({ documents, documentConfig }: Props): Reac
<span data-testid="dob">Date of birth: {dob}</span>
</div>
</div>

{failedDocuments.length > 0 && (
{failedDocuments.length > 0 ? (
<div className="govuk-accordion mb-8 govuk-frontend-supported">
<h3>Some of your files failed to upload</h3>
<button
className="toggle-button govuk-accordion__section-button"
onClick={() => setShowFiles(!showFiles)}
onClick={(): void => setShowFiles(!showFiles)}
data-testid="accordion-toggle-button"
aria-expanded={showFiles}
aria-controls="failed-files-list"
Expand Down Expand Up @@ -103,66 +99,86 @@ const DocumentUploadCompleteStage = ({ documents, documentConfig }: Props): Reac
))}
</div>
)}
</div>
)}

<h3>What happens next</h3>

{journey === 'update' && patientDetails.canManageRecord && (
<p>
You can now view the updated {documentConfig.displayName} for this patient in
this service by{' '}
<Link
to=""
onClick={(e): void => {
e.preventDefault();
navigate(routes.SEARCH_PATIENT, { replace: true });
<br />
<h3>What you need to do</h3>
<p>
You must note which files uploaded successfully, then return to the
patient's record to upload any files that failed.
</p>
<Button
data-testid="patient-docs-btn"
type="button"
onClick={(): void => {
navigate(routes.PATIENT_DOCUMENTS, { replace: true });
}}
data-testid="search-patient-link"
>
searching using their NHS number
</Link>
{'.'}
</p>
)}
Go to Lloyd George records
</Button>
</div>
) : (
<>
<h3>What happens next</h3>

{journey === 'update' && patientDetails.canManageRecord && (
<p>
You can now view the updated {documentConfig.displayName} for this
patient in this service by{' '}
<Link
to=""
onClick={(e): void => {
e.preventDefault();
navigate(routes.SEARCH_PATIENT, { replace: true });
}}
data-testid="search-patient-link"
>
searching using their NHS number
</Link>
{'.'}
</p>
)}

{patientDetails.canManageRecord === false && (
<p>
You are not the data controller for this patient so you cannot view the files
you have uploaded in this service.
</p>
)}
{patientDetails.canManageRecord === false && (
<p>
You are not the data controller for this patient so you cannot view the
files you have uploaded in this service.
</p>
)}

<p>
If you think you've made a mistake, contact the Patient Record Management team at{' '}
<a href="mailto:england.prmteam@nhs.net">england.prmteam@nhs.net</a>.
</p>
<p>
If you think you've made a mistake, contact the Patient Record Management
team at{' '}
<a id="mail" href="mailto:england.prmteam@nhs.net">
england.prmteam@nhs.net
</a>
.
</p>

{documentConfig.content.uploadFilesExtraParagraph && (
<p>{documentConfig.content.uploadFilesExtraParagraph}</p>
)}

{documentConfig.content.uploadFilesExtraParagraph && (
<p>{documentConfig.content.uploadFilesExtraParagraph}</p>
<p>
For information on destroying your paper records and removing the digital
files from your system, read the article{' '}
<Link
to="https://future.nhs.uk/DigitalPC/view?objectId=185217477"
data-testid="digitisation-link"
>
Digitisation of Lloyd George records
</Link>
{'.'}
</p>
<Button
data-testid="home-btn"
type="button"
onClick={(): void => {
navigate(routes.HOME, { replace: true });
}}
>
Go to home
</Button>
</>
)}

<p>
For information on destroying your paper records and removing the digital files from
your system, read the article{' '}
<Link
to="https://future.nhs.uk/DigitalPC/view?objectId=185217477"
data-testid="digitisation-link"
>
Digitisation of Lloyd George records
</Link>
{'.'}
</p>

<Button
data-testid="home-btn"
type="button"
onClick={(): void => {
navigate(routes.HOME, { replace: true });
}}
>
Go to home
</Button>
</div>
);
};
Expand Down
4 changes: 2 additions & 2 deletions app/src/helpers/utils/documentUpload.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -814,7 +814,7 @@ describe('documentUpload', () => {
expect(updatedDocs[0].state).toBe(DOCUMENT_UPLOAD_STATE.INFECTED);
});

it('should set state to FAILED when virus-failed.pdf file is detected in local mode', async () => {
it('should set state to ERROR when virus-failed.pdf file is detected in local mode', async () => {
vi.spyOn(isLocal, 'isLocal', 'get').mockReturnValue(true);

const failedDoc = {
Expand All @@ -839,7 +839,7 @@ describe('documentUpload', () => {
await vi.advanceTimersByTimeAsync(1000);

const updatedDocs = mockSetDocuments.mock.calls[0][0];
expect(updatedDocs[0].state).toBe(DOCUMENT_UPLOAD_STATE.FAILED);
expect(updatedDocs[0].state).toBe(DOCUMENT_UPLOAD_STATE.ERROR);
});

it('should not change state when already SCANNING in local mode', async () => {
Expand Down
83 changes: 41 additions & 42 deletions app/src/helpers/utils/documentUpload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,53 +225,52 @@ export const startIntervalTimer = (
): number => {
return window.setInterval(async () => {
interval.current = interval.current + 1;
if (isLocal) {
const updatedDocuments = uploadDocuments.map((doc) => {
const min = (doc.progress ?? 0) + 40;
const max = 70;
doc.progress = Math.random() * (min + max - (min + 1)) + min;
doc.progress = Math.min(doc.progress, 100);
if (doc.progress < 100) {
doc.state = DOCUMENT_UPLOAD_STATE.UPLOADING;
} else if (doc.state !== DOCUMENT_UPLOAD_STATE.SCANNING) {
const hasVirusFile = documents.filter(
(d) => d.file.name.toLocaleLowerCase() === 'virus.pdf',
);
const hasFailedFile = documents.filter(
(d) => d.file.name.toLocaleLowerCase() === 'virus-failed.pdf',
);

if (hasVirusFile.length > 0) {
doc.state = DOCUMENT_UPLOAD_STATE.INFECTED;
} else if (hasFailedFile.length > 0) {
doc.state = DOCUMENT_UPLOAD_STATE.FAILED;
} else {
doc.state = DOCUMENT_UPLOAD_STATE.SUCCEEDED;
try {
if (isLocal) {
const updatedDocuments = uploadDocuments.map((doc) => {
const min = (doc.progress ?? 0) + 40;
const max = 70;
doc.progress = Math.random() * (min + max - (min + 1)) + min;
doc.progress = Math.min(doc.progress, 100);
if (doc.progress < 100) {
doc.state = DOCUMENT_UPLOAD_STATE.UPLOADING;
} else if (doc.state !== DOCUMENT_UPLOAD_STATE.SCANNING) {
const hasVirusFile = documents.filter(
(d) => d.file.name.toLocaleLowerCase() === 'virus.pdf',
);

if (hasVirusFile.length > 0) {
doc.state = DOCUMENT_UPLOAD_STATE.INFECTED;
} else if (doc.file.name.toLocaleLowerCase() === 'virus-failed.pdf') {
doc.state = DOCUMENT_UPLOAD_STATE.ERROR;
} else {
doc.state = DOCUMENT_UPLOAD_STATE.SUCCEEDED;
}
}
}

return doc;
});
setDocuments(updatedDocuments);
} else if (patientDetails?.canManageRecord) {
const documentStatusResult = await getDocumentStatus({
documents: uploadDocuments,
baseUrl,
baseHeaders,
nhsNumber,
});

handleDocStatusResult(documentStatusResult, setDocuments);
} else {
uploadDocuments.forEach(async (document) => {
void getDocumentReviewStatus({
document,
return doc;
});
setDocuments(updatedDocuments);
} else if (patientDetails?.canManageRecord) {
const documentStatusResult = await getDocumentStatus({
documents: uploadDocuments,
baseUrl,
baseHeaders,
nhsNumber,
}).then((result) => handleDocReviewStatusResult(result, setDocuments));
});
}
});

handleDocStatusResult(documentStatusResult, setDocuments);
} else {
uploadDocuments.forEach(async (document) => {
void getDocumentReviewStatus({
document,
baseUrl,
baseHeaders,
nhsNumber,
}).then((result) => handleDocReviewStatusResult(result, setDocuments));
});
}
} catch {}
}, timeout);
};

Expand Down
12 changes: 4 additions & 8 deletions app/src/pages/documentUploadPage/DocumentUploadPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,16 +182,12 @@ const DocumentUploadPage = (): React.JSX.Element => {
uploadSession,
setDocuments,
});
} catch (e) {
window.clearInterval(intervalTimer);
markDocumentAsFailed(document);

const error = e as AxiosError;
navigate(routes.SERVER_ERROR + errorToParams(error));
} catch {
markDocumentWithError(document);
}
};

const markDocumentAsFailed = (document: UploadDocument): void => {
const markDocumentWithError = (document: UploadDocument): void => {
setSingleDocument(setDocuments, {
id: document.id,
state: DOCUMENT_UPLOAD_STATE.ERROR,
Expand Down Expand Up @@ -265,7 +261,7 @@ const DocumentUploadPage = (): React.JSX.Element => {
}

const updateStateInterval = startIntervalTimer(
uploadingDocuments,
uploadingDocuments.filter(d => d.state !== DOCUMENT_UPLOAD_STATE.ERROR),
interval,
documents,
setDocuments,
Expand Down
Loading
Loading