Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f2dc755
CCM-14583: Empty page for the feature
m-salaudeen Mar 18, 2026
abdb796
trivvy
bhansell1 Mar 18, 2026
077ce73
CCM-14583: update page with content
bhansell1 Mar 18, 2026
afb1fcd
CCM-14583: tests
bhansell1 Mar 18, 2026
71140ae
lockfile
bhansell1 Mar 18, 2026
8be55bf
typing
bhansell1 Mar 18, 2026
f142503
CCM-14583: fix fallout from lintfix
bhansell1 Mar 18, 2026
f949db7
lockfile
bhansell1 Mar 18, 2026
e78bfdf
CCM-14583: lockfile
bhansell1 Mar 19, 2026
7536664
Merge branch 'main' of https://github.com/NHSDigital/nhs-notify-web-t…
bhansell1 Mar 25, 2026
b4f8884
CCM-14583: update destination when approving letter authoring
bhansell1 Mar 26, 2026
469df8d
typecheck
bhansell1 Mar 26, 2026
3092553
Merge branch 'main' of https://github.com/NHSDigital/nhs-notify-web-t…
bhansell1 Mar 26, 2026
8464b4e
review comments
bhansell1 Mar 26, 2026
0f8c892
CCM-14583: remove lockId check from the review and approve page
bhansell1 Mar 26, 2026
28a6536
CCM-14583: review
bhansell1 Mar 26, 2026
40acce4
Merge branch 'main' of https://github.com/NHSDigital/nhs-notify-web-t…
bhansell1 Apr 2, 2026
05679e9
Revert "CCM-14583: remove lockId check from the review and approve page"
bhansell1 Apr 2, 2026
b102a63
CCM-14583: pass through lockNumber
bhansell1 Apr 2, 2026
e80484f
CCM-14583: self review
bhansell1 Apr 2, 2026
80d714b
Merge branch 'main' into feature/CCM-14583_user_approves_their_letter…
bhansell1 Apr 7, 2026
a8d6ce9
Merge branch 'main' of https://github.com/NHSDigital/nhs-notify-web-t…
bhansell1 Apr 8, 2026
a132c39
Merge branch 'feature/CCM-14583_user_approves_their_letter_template' …
bhansell1 Apr 8, 2026
a9f4c1f
CCM-14583: fix broken fn name from pulling in main
bhansell1 Apr 8, 2026
2051dc2
CCM-14583: update lockfile
bhansell1 Apr 8, 2026
80fdc1c
Merge branch 'main' into feature/CCM-14583_user_approves_their_letter…
bhansell1 Apr 8, 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
@@ -0,0 +1,110 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`GetReadyToApproveLetterTemplatePage matches snapshot 1`] = `
<DocumentFragment>
<div
class="nhsuk-width-container"
>
<main
class="nhsuk-main-wrapper"
id="maincontent"
role="main"
>
<div
class="nhsuk-grid-row"
>
<div
class="nhsuk-grid-column-two-thirds"
>
<span
class="nhsuk-caption-xl"
>
Step 1 of 2
</span>
<h1
class="nhsuk-heading-xl"
id="get-ready-to-approve-authoring-letter-template-name"
>
Get ready to approve 'authoring letter template name'
</h1>
<p
class="nhsuk-body-l"
>
After you approve this letter, you can use it in your message plans.
</p>
<h2
class="nhsuk-heading-m"
id="before-you-approve"
>
Before you approve
</h2>
<p>
Make sure:
</p>
<ul>
<li>
the relevant stakeholders in your team have approved your letter template
</li>
<li>
your letter does not have any errors
</li>
</ul>
<h2
class="nhsuk-heading-m"
id="personalisation"
>
Personalisation
</h2>
<p>
Longer personalisation data can affect the final number of pages and price of your letter.
</p>
<div
class="nhsuk-warning-callout"
>
<h3
class="nhsuk-warning-callout__label"
>
Important
<span
class="nhsuk-u-visually-hidden"
>
:
</span>
</h3>
<p>
You cannot change your template settings after you approve this template.
</p>
<p>
If you need to make changes, edit your original template file on your computer then upload it as a new template.
</p>
</div>
<div
class="nhsuk-form-group"
>
<a
aria-disabled="false"
class="nhsuk-button"
data-testid="continue-button"
draggable="false"
href="/templates/review-and-approve-letter-template/authoring-letter-template-id?lockNumber=1"
role="button"
>
Continue
</a>
<a
aria-disabled="false"
class="nhsuk-button nhsuk-button--secondary nhsuk-u-margin-left-3"
data-testid="back-button"
draggable="false"
href="/templates/preview-letter-template/authoring-letter-template-id"
role="button"
>
Go back
</a>
</div>
</div>
</div>
</main>
</div>
</DocumentFragment>
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import GetReadyToApproveLetterTemplatePage, {
generateMetadata,
} from '@app/get-ready-to-approve-letter-template/[templateId]/page';
import { render } from '@testing-library/react';
import pageContent from '@content/content';
import { redirect } from 'next/navigation';
import { getTemplate } from '@utils/form-actions';
import {
EMAIL_TEMPLATE,
NHS_APP_TEMPLATE,
AUTHORING_LETTER_TEMPLATE,
SMS_TEMPLATE,
PDF_LETTER_TEMPLATE,
} from '@testhelpers/helpers';

jest.mock('@utils/form-actions');
jest.mock('next/navigation');

const getTemplateMock = jest.mocked(getTemplate);
const redirectMock = jest.mocked(redirect);

const {
pageTitle,
continue: continueLink,
back,
} = pageContent.pages.getReadyToApproveLetterTemplate;

describe('GetReadyToApproveLetterTemplatePage', () => {
beforeEach(() => {
jest.resetAllMocks();
});

it('matches snapshot', async () => {
getTemplateMock.mockResolvedValueOnce(AUTHORING_LETTER_TEMPLATE);

const page = render(
await GetReadyToApproveLetterTemplatePage({
params: Promise.resolve({
templateId: AUTHORING_LETTER_TEMPLATE.id,
}),
searchParams: Promise.resolve({ lockNumber: '1' }),
})
);

const continueElement = page.getByRole('button', {
name: continueLink.text,
});

expect(continueElement).toHaveAttribute(
'href',
`/templates/review-and-approve-letter-template/${AUTHORING_LETTER_TEMPLATE.id}?lockNumber=1`
);

const backLinkElement = page.getByRole('button', { name: back.text });

expect(backLinkElement).toHaveAttribute(
'href',
`/templates/preview-letter-template/${AUTHORING_LETTER_TEMPLATE.id}`
);

expect(page.asFragment()).toMatchSnapshot();
});

it('should return metadata', async () => {
expect(await generateMetadata()).toEqual({
title: pageTitle,
});
});

test.each([
['NHS_APP', NHS_APP_TEMPLATE],
['SMS', SMS_TEMPLATE],
['EMAIL', EMAIL_TEMPLATE],
])('should redirect user when templateType is %s', async (_, template) => {
getTemplateMock.mockResolvedValueOnce(template);

render(
await GetReadyToApproveLetterTemplatePage({
params: Promise.resolve({
templateId: template.id,
}),
})
);
expect(redirectMock).toHaveBeenCalledWith('/invalid-template', 'replace');
});

it('should redirect to invalid template page when letter template is not an authoring template', async () => {
getTemplateMock.mockResolvedValueOnce(PDF_LETTER_TEMPLATE);

await GetReadyToApproveLetterTemplatePage({
params: Promise.resolve({
templateId: PDF_LETTER_TEMPLATE.id,
}),
});

expect(redirectMock).toHaveBeenCalledWith('/invalid-template', 'replace');
});

it('should redirect to invalid template page when no template is found', async () => {
getTemplateMock.mockResolvedValueOnce(undefined);

await GetReadyToApproveLetterTemplatePage({
params: Promise.resolve({
templateId: 'template-id',
}),
searchParams: Promise.resolve({ lockNumber: '1' }),
});

expect(redirectMock).toHaveBeenCalledWith('/invalid-template', 'replace');
});

it('should redirect to preview page when template is valid but lockNumber is missing', async () => {
getTemplateMock.mockResolvedValueOnce(AUTHORING_LETTER_TEMPLATE);

await GetReadyToApproveLetterTemplatePage({
params: Promise.resolve({
templateId: AUTHORING_LETTER_TEMPLATE.id,
}),
searchParams: Promise.resolve({ notLockNumber: 'value' }),
});

expect(redirectMock).toHaveBeenCalledWith(
`/preview-letter-template/${AUTHORING_LETTER_TEMPLATE.id}`,
'replace'
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ describe('submitAuthoringLetterAction', () => {
jest.resetAllMocks();
});

it('should redirect to submit-letter-template page with valid form data', async () => {
it('should redirect to get-ready-to-approve-letter-template page with valid form data', async () => {
const formData = new FormData();
formData.append('templateId', 'template-123');
formData.append('lockNumber', '1');

await submitAuthoringLetterAction({}, formData);

expect(redirectMock).toHaveBeenCalledWith(
'/review-and-approve-letter-template/template-123?lockNumber=1'
'/get-ready-to-approve-letter-template/template-123?lockNumber=1'
);
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { Metadata } from 'next';
import { redirect, RedirectType } from 'next/navigation';
import {
getPreviewURL,
TemplatePageProps,
validateLetterTemplate,
} from 'nhs-notify-web-template-management-utils';
import { ContentRenderer } from '@molecules/ContentRenderer/ContentRenderer';
import { getTemplate } from '@utils/form-actions';
import { interpolate } from '@utils/interpolate';
import { NHSNotifyButton } from '@atoms/NHSNotifyButton/NHSNotifyButton';
import { NHSNotifyContainer } from '@layouts/container/container';
import { NHSNotifyMain } from '@atoms/NHSNotifyMain/NHSNotifyMain';
import { $LockNumber } from 'nhs-notify-backend-client/schemas';
import content from '@content/content';

const pageContent = content.pages.getReadyToApproveLetterTemplate;

export async function generateMetadata(): Promise<Metadata> {
return {
title: pageContent.pageTitle,
};
}
const GetReadyToApproveLetterTemplatePage = async (
props: TemplatePageProps
) => {
const { templateId } = await props.params;

const template = await getTemplate(templateId);

const validatedTemplate = validateLetterTemplate(template);

if (!validatedTemplate || validatedTemplate.letterVersion !== 'AUTHORING') {
return redirect('/invalid-template', RedirectType.replace);
}

const searchParams = await props.searchParams;

const lockNumberResult = $LockNumber.safeParse(searchParams?.lockNumber);

if (!lockNumberResult.success) {
return redirect(getPreviewURL(validatedTemplate), RedirectType.replace);
}

return (
<NHSNotifyContainer>
<NHSNotifyMain>
<div className='nhsuk-grid-row'>
<div className='nhsuk-grid-column-two-thirds'>
<span className='nhsuk-caption-xl'>{pageContent.stepCounter}</span>

<ContentRenderer
content={pageContent.body}
variables={{ templateName: validatedTemplate.name }}
/>

<div className='nhsuk-warning-callout'>
<h3 className='nhsuk-warning-callout__label'>
{pageContent.callout.label}
<span className='nhsuk-u-visually-hidden'>:</span>
</h3>
<ContentRenderer content={pageContent.callout.content} />
</div>

<div className='nhsuk-form-group'>
<NHSNotifyButton
href={interpolate(pageContent.continue.href, {
templateId: validatedTemplate.id,
lockNumber: lockNumberResult.data,
})}
data-testid='continue-button'
>
{pageContent.continue.text}
</NHSNotifyButton>

<NHSNotifyButton
secondary
href={interpolate(pageContent.back.href, {
templateId: validatedTemplate.id,
})}
className='nhsuk-u-margin-left-3'
data-testid='back-button'
>
{pageContent.back.text}
</NHSNotifyButton>
</div>
</div>
</div>
</NHSNotifyMain>
</NHSNotifyContainer>
);
};

export default GetReadyToApproveLetterTemplatePage;
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ export async function submitAuthoringLetterAction(
const { templateId, lockNumber } = result.data;

redirect(
// temporary destination
// TODO: CCM-14583 change to get ready to approve URL
`/review-and-approve-letter-template/${templateId}?lockNumber=${lockNumber}`
`/get-ready-to-approve-letter-template/${templateId}?lockNumber=${lockNumber}`
);
}
Loading
Loading