diff --git a/runner/src/server/plugins/engine/page-controllers/PageController.ts b/runner/src/server/plugins/engine/page-controllers/PageController.ts index 372f5e26..e3513dff 100644 --- a/runner/src/server/plugins/engine/page-controllers/PageController.ts +++ b/runner/src/server/plugins/engine/page-controllers/PageController.ts @@ -55,4 +55,43 @@ export class PageController extends PageControllerBase { }, }; } + + makeGetRouteHandler() { + return async (request: HapiRequest, h: HapiResponseToolkit) => { + // Call the parent class's makeGetRouteHandler method and get the response + const parentResponse = await super.makeGetRouteHandler()(request, h); + + const {adapterCacheService} = request.services([]); + + // @ts-ignore + const state = await adapterCacheService.getState(request); + + // Extract the viewModel from the parent's response + const viewModel = parentResponse.source.context; + + viewModel.changeRequests = []; + const changeRequests = state.metadata?.change_requests; + if (changeRequests && Object.keys(changeRequests).length > 0) { + for (let componentName in changeRequests) { + const messages = changeRequests[componentName]; + + // Find the component with the matching the change request + const pageComponents = this.pageDef.components; + const component = pageComponents.find(component => { + return component.name === componentName + }); + + if (component) { + // Add an object to viewModel.changeRequests + viewModel.changeRequests.push({ + title: component.title, + messages: messages + }); + } + } + } + + return h.view(this.viewName, viewModel); + }; + } } diff --git a/runner/src/server/plugins/engine/page-controllers/PageControllerBase.ts b/runner/src/server/plugins/engine/page-controllers/PageControllerBase.ts index c0edc466..eda429d9 100644 --- a/runner/src/server/plugins/engine/page-controllers/PageControllerBase.ts +++ b/runner/src/server/plugins/engine/page-controllers/PageControllerBase.ts @@ -10,6 +10,7 @@ import { RelativeUrl, } from "../../../../../../digital-form-builder/runner/src/server/plugins/engine"; import { + ChangeRequest, HapiRequest, HapiResponseObject, HapiResponseToolkit, @@ -153,6 +154,7 @@ export class PageControllerBase { backLinkText?: string; continueButtonText?: string; phaseTag?: string | undefined; + changeRequests?: ChangeRequest[] | undefined; } { let showTitle = true; let pageTitle = this.title; diff --git a/runner/src/server/types.ts b/runner/src/server/types.ts index d65fe60a..659ff259 100644 --- a/runner/src/server/types.ts +++ b/runner/src/server/types.ts @@ -18,6 +18,12 @@ import {AdapterStatusService} from "./services"; import {WebhookService} from "./services/WebhookService"; import {TranslationLoaderService} from "./services/TranslationLoaderService"; + +export type ChangeRequest = { + title: string; + messages: string[]; + } + export type Services = (services: string[]) => { adapterCacheService: AdapterCacheService; notifyService: NotifyService; diff --git a/runner/src/server/views/layout.html b/runner/src/server/views/layout.html index 3388fc7c..dc0136dc 100644 --- a/runner/src/server/views/layout.html +++ b/runner/src/server/views/layout.html @@ -4,6 +4,7 @@ {% from "footer/macro.njk" import govukFooter -%} {% from "phase-banner/macro.njk" import govukPhaseBanner %} {% from "skip-link/macro.njk" import govukSkipLink -%} +{% from "./partials/change-requests-banner.html" import changeRequestsBanner %} {% block head %} @@ -191,6 +192,10 @@ text: backLinkText }) }} {% endif %} + + {% if changeRequests.length > 0 %} + {{ changeRequestsBanner(changeRequests) }} + {% endif %} {% endblock %} diff --git a/runner/src/server/views/partials/change-requests-banner.html b/runner/src/server/views/partials/change-requests-banner.html new file mode 100644 index 00000000..c6260768 --- /dev/null +++ b/runner/src/server/views/partials/change-requests-banner.html @@ -0,0 +1,25 @@ +{% macro changeRequestsBanner(changeRequests) %} +
+
+
+
+
+

+ Change request +

+
+
+ {% for question in changeRequests %} +

+ {{ question.title }} +

+ {% for message in question.messages %} +

{{ message }}

+ {% endfor %} + {% endfor %} +
+
+
+
+
+{% endmacro %} diff --git a/runner/test/cases/server/plugins/engine/page-controllers/PageController.test.ts b/runner/test/cases/server/plugins/engine/page-controllers/PageController.test.ts index 980a9bd4..e86fd990 100644 --- a/runner/test/cases/server/plugins/engine/page-controllers/PageController.test.ts +++ b/runner/test/cases/server/plugins/engine/page-controllers/PageController.test.ts @@ -100,4 +100,60 @@ suite("PageController", () => { console.error("Error during test:", error); } }); + + test("should display change request banner on page with feedback", async () => { + server = await createServer({ + formFileName: "sample-page.test.json", + formFilePath: path.join(__dirname, "../../../"), + enforceCsrf: false, + }); + + const { adapterCacheService } = server.services(); + adapterCacheService.getState = () => { + return Promise.resolve({ + metadata: { + change_requests: { + "VcyKVN": ["Assessor Feedback"] + } + } + }); + }; + + const response = await server.inject({ + method: 'GET', + url: '/sample-page.test/project-name', + }); + + $ = cheerio.load(response.payload); + + expect($("h2").text()).to.contain("Change request"); + }); + + test("should no display change request banner on page without feedback", async () => { + server = await createServer({ + formFileName: "sample-page.test.json", + formFilePath: path.join(__dirname, "../../../"), + enforceCsrf: false, + }); + + const { adapterCacheService } = server.services(); + adapterCacheService.getState = () => { + return Promise.resolve({ + metadata: { + change_requests: { + "no_found": ["Assessor Feedback"] + } + } + }); + }; + + const response = await server.inject({ + method: 'GET', + url: '/sample-page.test/project-name', + }); + + $ = cheerio.load(response.payload); + + expect($("h2").text()).to.not.contain("Change request"); + }); }); diff --git a/runner/test/cases/server/sample-page.test.json b/runner/test/cases/server/sample-page.test.json new file mode 100644 index 00000000..4af643f2 --- /dev/null +++ b/runner/test/cases/server/sample-page.test.json @@ -0,0 +1,84 @@ +{ + "startPage": "/project-details", + "pages": [ + { + "path": "/summary", + "title": "Summary", + "components": [], + "next": [], + "section": "FabDefault", + "controller": "./pages/summary.js" + }, + { + "title": "Project details", + "path": "/project-details", + "components": [ + { + "name": "orUXQc", + "options": {}, + "type": "Para", + "content": "\n\n
\n\n
", + "schema": {} + } + ], + "next": [ + { + "path": "/project-name" + } + ], + "controller": "./pages/start.js" + }, + { + "path": "/project-overview", + "title": "Project overview", + "components": [ + { + "options": {}, + "type": "FreeTextField", + "title": "Project overview", + "hint": "Give a brief summary of your project, including what you hope to achieve", + "schema": {}, + "name": "KJtdhs" + } + ], + "next": [ + { + "path": "/summary" + } + ], + "section": "FabDefault" + }, + { + "path": "/project-name", + "title": "Project name", + "components": [ + { + "options": {}, + "type": "TextField", + "title": "Project name", + "hint": "", + "schema": {}, + "name": "VcyKVN" + } + ], + "next": [ + { + "path": "/project-overview" + } + ], + "section": "FabDefault" + } + ], + "lists": [], + "conditions": [], + "sections": [ + { + "name": "FabDefault", + "title": "Default section", + "hideTitle": true + } + ], + "outputs": [], + "skipSummary": false, + "name": "Apply for Sample Fund" +}