Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
9e2d78f
HDPI-5180: Create gen app skeleton journey
scottstewart-sl Mar 9, 2026
8a94076
Merge remote-tracking branch 'origin/master' into HDPI-5180_gen_app_s…
scottstewart-sl Mar 23, 2026
c3882a0
Merge remote-tracking branch 'origin/master' into HDPI-5180_gen_app_s…
scottstewart-sl Mar 25, 2026
55690ec
HDPI-5180: Update CYA page for gen apps
scottstewart-sl Mar 25, 2026
0b59970
HDPI-5180: Fix lint issues
scottstewart-sl Mar 25, 2026
13fbd73
HDPI-5180: Fix unit test following property name change
scottstewart-sl Mar 25, 2026
378da77
HDPI-5180: Use Continue button for now
scottstewart-sl Mar 25, 2026
1a2adc3
HDPI-5180: Fix accidental space
scottstewart-sl Mar 25, 2026
ee93665
HDPI-5180: Remove Suspend as is no longer in scope
scottstewart-sl Mar 25, 2026
ee0c5d4
Merge branch 'master' into HDPI-5180_gen_app_skeleton
scottstewart-sl Mar 26, 2026
ff8cbf2
HDPI-5180: Resolve PR comments
scottstewart-sl Mar 26, 2026
5c8d84c
Merge branch 'HDPI-5180_gen_app_skeleton' of github.com:hmcts/pcs-fro…
scottstewart-sl Mar 26, 2026
7868fa8
Merge branch 'master' into HDPI-5180_gen_app_skeleton
scottstewart-sl Mar 26, 2026
e3f7e04
HDPI-5180: Use entity encoding in another HTML page title
scottstewart-sl Mar 26, 2026
735f105
Merge remote-tracking branch 'origin/master' into HDPI-5180_gen_app_s…
scottstewart-sl Mar 27, 2026
3fdd1b5
HDPI-5180: Adding placeholder banner to pages
scottstewart-sl Mar 27, 2026
8a27cf6
HDPI-5180: Update brace-expansion to resolve CVE
scottstewart-sl Mar 27, 2026
4064415
Merge remote-tracking branch 'origin/master' into HDPI-5180_gen_app_s…
scottstewart-sl Mar 27, 2026
ff80075
Merge branch 'master' into HDPI-5180_gen_app_skeleton
scottstewart-sl Mar 30, 2026
b929a54
Merge branch 'master' into HDPI-5180_gen_app_skeleton
scottstewart-sl Mar 30, 2026
ac758c9
Merge branch 'master' into HDPI-5180_gen_app_skeleton
scottstewart-sl Mar 31, 2026
e7e89b4
HDPI-5180: Fix merge issue
scottstewart-sl Mar 31, 2026
b692503
Merge branch 'master' into HDPI-5180_gen_app_skeleton
scottstewart-sl Apr 1, 2026
ee103ba
Merge branch 'master' into HDPI-5180_gen_app_skeleton
srinijg Apr 1, 2026
8bd26b6
Merge branch 'master' into HDPI-5180_gen_app_skeleton
scottstewart-sl Apr 2, 2026
aa0a479
Merge branch 'master' into HDPI-5180_gen_app_skeleton
srinijg Apr 7, 2026
151a179
Merge branch 'master' into HDPI-5180_gen_app_skeleton
srinijg Apr 8, 2026
253c858
Merge branch 'master' into HDPI-5180_gen_app_skeleton
srinijg Apr 8, 2026
7f55d97
Merge branch 'master' into HDPI-5180_gen_app_skeleton
srinijg Apr 9, 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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Run:
yarn start
```

The applications's home page will be available at http://localhost:3209
The application's home page will be available at http://localhost:3209

### Running with Docker

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"pageTitle": "cyCheck your answers",
"caption": "cyMake an application",
"heading": "cyCheck your answers (placeholder)",
"buttons": {
"submitApplication": "cySubmit application"
},
"answers": {
"chooseAnApplication": {
"label": "cyType of application",
"changeHint": "cyChange type of application",
"options": {
"ADJOURN": "cyAsk to adjourn (delay) the hearing",
"SET_ASIDE": "cyAsk the court to change (set aside) their decision to evict you",
"SOMETHING_ELSE": "cySomething else"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"pageTitle": "cyChoose an application",
"caption": "cyMake an application",
"heading": "cyChoose an application",
"question": "cyWhat do you want to apply for?",
"hintText": "cySome optional hint text here (placeholder)",
"options": {
"adjourn": {
"label": "cyAsk to adjourn (delay) the hearing"
},
"setAside": {
"label": "cyAsk the court to change (set aside) their decision to evict you"
},
"somethingElse": {
"label": "cySomething else"
}
},
"errors": {
"typeOfApplication": "cyYou must select an type of application to apply for"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"pageTitle": "Check your answers",
"caption": "Make an application",
"heading": "Check your answers",
"buttons": {
"submitApplication": "Submit application"
},
"answers": {
"chooseAnApplication": {
"label": "Type of application",
"changeHint": "Change type of application",
"options": {
"ADJOURN": "Ask to adjourn (delay) the hearing",
"SET_ASIDE": "Ask the court to change (set aside) their decision to evict you",
"SOMETHING_ELSE": "Something else"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"pageTitle": "Choose an application",
"caption": "Make an application",
"heading": "Choose an application",
"question": "What do you want to apply for?",
"hintText": "Some optional hint text here (placeholder)",
"options": {
"adjourn": {
"label": "Ask to adjourn (delay) the hearing"
},
"setAside": {
"label": "Ask the court to change (set aside) their decision to evict you"
},
"somethingElse": {
"label": "Something else"
}
},
"errors": {
"typeOfApplication": "You must select an type of application to apply for"
}
}
11 changes: 11 additions & 0 deletions src/main/interfaces/ccdCase.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,14 @@ export interface StartCallbackData {
};
};
}

export enum GenAppType {
SUSPEND,
ADJOURN,
SET_ASIDE,
SOMETHING_ELSE,
}

export interface CitizenGenAppRequest {
applicationType: GenAppType;
}
1 change: 1 addition & 0 deletions src/main/interfaces/formFieldConfig.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface FormFieldOption {
divider?: string;
translationKey?: string;
label?: string | ((translations: Record<string, string>) => string);
hint?: string | ((translations: Record<string, string>) => string);
conditionalText?: string | ((translations: Record<string, string>) => string);
// SubFields appear conditionally when this option is selected (e.g., text inputs under "No" radio button)
subFields?: Record<string, FormFieldConfig>;
Expand Down
6 changes: 6 additions & 0 deletions src/main/modules/steps/formBuilder/componentBuilders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ export function buildComponentConfig(
checked: radioValue === option.value,
};

if (option.hint) {
item.hint = {
text: option.hint,
};
}

// Build conditional HTML from conditionalText and subFields
const conditionalParts: string[] = [];

Expand Down
13 changes: 13 additions & 0 deletions src/main/services/ccdCaseService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,19 @@ export const ccdCaseService = {
return submitEvent(accessToken || '', url, 'respondPossessionClaim', eventToken, ccdCase.data);
},

async submitGeneralApplication(accessToken: string | undefined, ccdCase: CcdCase): Promise<CcdCase> {
if (!ccdCase.id) {
throw new HTTPError('Cannot submit general application, case ID not specified', 500);
}

const eventId = 'citizenCreateGenApp';
const eventUrl = `${getBaseUrl()}/cases/${ccdCase.id}/event-triggers/${eventId}`;
const eventToken = await getEventToken(accessToken || '', eventUrl);
const url = `${getBaseUrl()}/cases/${ccdCase.id}/events`;

return submitEvent(accessToken || '', url, eventId, eventToken, ccdCase.data);
},

async getExistingCaseData(accessToken: string | undefined, ccdCaseId: string): Promise<StartCallbackData> {
const eventUrl = `${getBaseUrl()}/cases/${ccdCaseId}/event-triggers/respondPossessionClaim?ignore-warning=false`;
logger.info('getExistingCaseData event URL', { eventUrl });
Expand Down
7 changes: 7 additions & 0 deletions src/main/steps/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { JourneyFlowConfig } from '../interfaces/stepFlow.interface';
import type { StepDefinition } from '../interfaces/stepFormData.interface';

import { flowConfig as makeAnApplicationFlowConfig } from './make-an-application/flow.config';
import { stepRegistry as makeAnApplicationStepRegistry } from './make-an-application/stepRegistry';
import { flowConfig as respondToClaimFlowConfig } from './respond-to-claim/flow.config';
import { stepRegistry as respondToClaimStepRegistry } from './respond-to-claim/stepRegistry';

Expand All @@ -16,6 +18,11 @@ export const journeyRegistry: Record<string, JourneyConfig> = {
flowConfig: respondToClaimFlowConfig,
stepRegistry: respondToClaimStepRegistry,
},
makeAnApplication: {
name: 'makeAnApplication',
flowConfig: makeAnApplicationFlowConfig,
stepRegistry: makeAnApplicationStepRegistry,
},
};

// Helper function to get steps for a specific journey
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{% extends "stepsTemplate.njk" %}
{% from "govuk/components/error-summary/macro.njk" import govukErrorSummary %}
{% from "govuk/components/button/macro.njk" import govukButton %}
{% from "govuk/components/summary-list/macro.njk" import govukSummaryList %}
{% from "macros/csrf.njk" import csrfProtection %}

{# GOV.UK pattern: [Browser title] - HM Courts & Tribunals Service – GOV.UK #}
{% block pageTitle %}{{ t('pageTitle') }} &ndash; HM Courts &amp; Tribunals Service &ndash; GOV.UK{% endblock %}

{% block mainContent %}
{% if errorSummary %}
{{ govukErrorSummary(errorSummary) }}
{% endif %}

<span class="govuk-caption-xl">{{ caption }}</span>
<h1 class="govuk-heading-l">{{ heading }}</h1>

<div class="govuk-notification-banner" role="region" aria-labelledby="experimental-banner">
<div class="govuk-notification-banner__content">
<p class="govuk-notification-banner__heading" id="experimental-banner">
Placeholder page
</p>
</div>
</div>

{{ govukSummaryList(summaryData) }}

<form method="post" action="{{ url }}" novalidate>
<div class="govuk-button-group">
{{ govukButton({
text: buttons.submitApplication,
attributes: { type: 'submit', name: 'action', value: 'submit' }
}) }}
</div>

{{ csrfProtection(csrfToken) }}
</form>
{% endblock %}

95 changes: 95 additions & 0 deletions src/main/steps/make-an-application/check-your-answers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import type { Request, Response } from 'express';
import type { TFunction } from 'i18next';

import { CitizenGenAppRequest } from '../../../interfaces/ccdCase.interface';
import type { StepDefinition } from '../../../interfaces/stepFormData.interface';
import { createGetController, createStepNavigation, getTranslationFunction } from '../../../modules/steps';
import { getDashboardUrl } from '../../../routes/dashboard';
import { ccdCaseService } from '../../../services/ccdCaseService';
import { MAKE_AN_APPLICATION_ROUTE, flowConfig } from '../flow.config';

const STEP_NAME = 'check-your-answers';
const stepNavigation = createStepNavigation(flowConfig);

export const step: StepDefinition = {
url: `${MAKE_AN_APPLICATION_ROUTE}/check-your-answers`,
name: STEP_NAME,
view: 'make-an-application/check-your-answers/checkYourAnswers.njk',
stepDir: __dirname,
getController: () => {
return createGetController(
'make-an-application/check-your-answers/checkYourAnswers.njk',
STEP_NAME,
stepNavigation,
(req: Request) => {
const t: TFunction = getTranslationFunction(req, STEP_NAME, ['common']);

const formData = req.session.formData;
Comment thread
paddy-hmcts marked this conversation as resolved.

if (!formData) {
throw Error('No existing formData in session');
}

const typeOfApplication = formData['choose-an-application']['typeOfApplication'];

return {
summaryData: {
rows: [
{
key: {
text: t('answers.chooseAnApplication.label'),
},
value: {
text: t(`answers.chooseAnApplication.options.${typeOfApplication}`),
Comment thread
paddy-hmcts marked this conversation as resolved.
},
actions: {
items: [
{
href: './choose-an-application',
text: t('change'),
Comment thread
paddy-hmcts marked this conversation as resolved.
visuallyHiddenText: t('answers.chooseAnApplication.changeHint'),
},
],
},
},
],
},
};
},
'makeAnApplication'
);
},
postController: {
post: async (req: Request, res: Response) => {
const formData = req.session.formData;

const ccdCase = res.locals.validatedCase;
Comment thread
scottstewart-sl marked this conversation as resolved.
if (!ccdCase) {
throw Error('No existing case details in session');
}

if (!formData) {
throw Error('No existing formData in session');
}

const citizenGenAppRequest: CitizenGenAppRequest = {
applicationType: formData['choose-an-application']['typeOfApplication'],
};

await ccdCaseService.submitGeneralApplication(req.session?.user?.accessToken, {
id: ccdCase.id,
data: {
citizenGenAppRequest,
},
});

const redirectPath = getDashboardUrl(ccdCase.id);

if (!redirectPath) {
return res.status(500).send('Unable to determine next step');
}

res.redirect(303, redirectPath);
},
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{% extends "stepsTemplate.njk" %}
{% from "govuk/components/button/macro.njk" import govukButton %}
{% from "govuk/components/error-summary/macro.njk" import govukErrorSummary %}
{% from "govuk/components/radios/macro.njk" import govukRadios %}
{% from "macros/csrf.njk" import csrfProtection %}

{# GOV.UK pattern: [Browser title] - HM Courts & Tribunals Service – GOV.UK #}
{% block pageTitle %}{{ pageTitle }} &ndash; HM Courts &amp; Tribunals Service &ndash; GOV.UK{% endblock %}

{% block mainContent %}
{% if errorSummary %}
{{ govukErrorSummary(errorSummary) }}
{% endif %}

<span class="govuk-caption-xl">{{ caption }}</span>
<h1 class="govuk-heading-l">{{ heading }}</h1>

<div class="govuk-notification-banner" role="region" aria-labelledby="experimental-banner">
<div class="govuk-notification-banner__content">
<p class="govuk-notification-banner__heading" id="experimental-banner">
Placeholder page
</p>
</div>
</div>

<form method="post" action="{{ url }}" novalidate>

{% for field in fields %}
{% if field.componentType == 'radios' %}
{{ govukRadios(field.component) }}
{% endif %}
{% endfor %}

<div class="govuk-button-group">
{{ govukButton({
text: t('buttons.continue', { ns: 'common' }),
attributes: { type: 'submit', name: 'action', value: 'continue' }
}) }}
<p class="govuk-body">
<a href="{{ dashboardUrl }}" class="govuk-link">{{ cancel }}</a>
</p>
</div>

{{ csrfProtection(csrfToken) }}
</form>
{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { StepDefinition } from '../../../interfaces/stepFormData.interface';
import { createFormStep } from '../../../modules/steps';
import { flowConfig } from '../flow.config';

export const step: StepDefinition = createFormStep({
stepName: 'choose-an-application',
journeyFolder: 'makeAnApplication',
stepDir: __dirname,
flowConfig,
customTemplate: `${__dirname}/chooseAnApplication.njk`,
fields: [
{
name: 'typeOfApplication',
type: 'radio',
required: true,
translationKey: { label: 'question' },
legendClasses: 'govuk-fieldset__legend--m',
options: [
{
value: 'ADJOURN',
translationKey: 'options.adjourn.label',
// TODO: Hint text translation support to be added as part of HDPI-5208
hint: 'You can apply to delay the hearing until a later date.',
},
{
value: 'SET_ASIDE',
translationKey: 'options.setAside.label',
// TODO: Hint text translation support to be added as part of HDPI-5208
hint: `You can ask the court to set aside the possession order if you have good reason.
For example, if you were unable to attend the court hearing because you were ill.`,
},
{ value: 'SOMETHING_ELSE', translationKey: 'options.somethingElse.label' },
],
},
],
translationKeys: {
pageTitle: 'pageTitle',
caption: 'caption',
heading: 'heading',
},
});
Loading
Loading