@defra/forms-engine-plugin 4.0.39 → 4.0.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.server/server/plugins/engine/pageControllers/StatusPageController.js +1 -1
- package/.server/server/plugins/engine/pageControllers/StatusPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +2 -1
- package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
- package/.server/server/plugins/engine/types.d.ts +1 -0
- package/.server/server/plugins/engine/types.js.map +1 -1
- package/package.json +1 -1
- package/src/server/plugins/engine/pageControllers/StatusPageController.ts +1 -1
- package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +2 -1
- package/src/server/plugins/engine/types.ts +1 -0
|
@@ -45,7 +45,7 @@ export class StatusPageController extends QuestionPageController {
|
|
|
45
45
|
submissionGuidance,
|
|
46
46
|
formName,
|
|
47
47
|
showReferenceNumber: this.showReferenceNumber,
|
|
48
|
-
referenceNumber:
|
|
48
|
+
referenceNumber: confirmationState.referenceNumber
|
|
49
49
|
});
|
|
50
50
|
};
|
|
51
51
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StatusPageController.js","names":["getCacheService","QuestionPageController","StatusPageController","allowSaveAndExit","showReferenceNumber","constructor","model","pageDef","viewName","def","options","getRelevantPath","getStatusPath","makeGetRouteHandler","request","context","h","viewModel","cacheService","server","confirmationState","getConfirmationState","confirmed","proceed","getStartPath","slug","params","formsService","services","getFormMetadata","getFormMetadataById","submissionGuidance","storedFormId","formId","formName","title","undefined","view","referenceNumber"],"sources":["../../../../../src/server/plugins/engine/pageControllers/StatusPageController.ts"],"sourcesContent":["import { type PageStatus } from '@defra/forms-model'\n\nimport { getCacheService } from '~/src/server/plugins/engine/helpers.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'\nimport { type FormContext } from '~/src/server/plugins/engine/types.js'\nimport {\n type FormRequest,\n type FormResponseToolkit\n} from '~/src/server/routes/types.js'\n\nexport class StatusPageController extends QuestionPageController {\n declare pageDef: PageStatus\n allowSaveAndExit = false\n showReferenceNumber = false\n\n constructor(model: FormModel, pageDef: PageStatus) {\n super(model, pageDef)\n this.viewName = 'confirmation'\n this.showReferenceNumber = model.def.options?.showReferenceNumber ?? false\n }\n\n getRelevantPath() {\n return this.getStatusPath()\n }\n\n makeGetRouteHandler() {\n return async (\n request: FormRequest,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { viewModel, viewName } = this\n\n const cacheService = getCacheService(request.server)\n const confirmationState = await cacheService.getConfirmationState(request)\n\n // If there's no confirmation state, then\n // redirect the user back to the start of the form\n if (!confirmationState.confirmed) {\n return this.proceed(request, h, this.getStartPath())\n }\n\n const slug = request.params.slug\n const { formsService } = this.model.services\n const { getFormMetadata, getFormMetadataById } = formsService\n\n const { submissionGuidance } = await getFormMetadata(slug)\n\n // Re-read form name if overriding display (for example, in a feedback form)\n const storedFormId = confirmationState.formId\n const formName = storedFormId\n ? (await getFormMetadataById(storedFormId)).title\n : undefined\n\n return h.view(viewName, {\n ...viewModel,\n submissionGuidance,\n formName,\n showReferenceNumber: this.showReferenceNumber,\n referenceNumber:
|
|
1
|
+
{"version":3,"file":"StatusPageController.js","names":["getCacheService","QuestionPageController","StatusPageController","allowSaveAndExit","showReferenceNumber","constructor","model","pageDef","viewName","def","options","getRelevantPath","getStatusPath","makeGetRouteHandler","request","context","h","viewModel","cacheService","server","confirmationState","getConfirmationState","confirmed","proceed","getStartPath","slug","params","formsService","services","getFormMetadata","getFormMetadataById","submissionGuidance","storedFormId","formId","formName","title","undefined","view","referenceNumber"],"sources":["../../../../../src/server/plugins/engine/pageControllers/StatusPageController.ts"],"sourcesContent":["import { type PageStatus } from '@defra/forms-model'\n\nimport { getCacheService } from '~/src/server/plugins/engine/helpers.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'\nimport { type FormContext } from '~/src/server/plugins/engine/types.js'\nimport {\n type FormRequest,\n type FormResponseToolkit\n} from '~/src/server/routes/types.js'\n\nexport class StatusPageController extends QuestionPageController {\n declare pageDef: PageStatus\n allowSaveAndExit = false\n showReferenceNumber = false\n\n constructor(model: FormModel, pageDef: PageStatus) {\n super(model, pageDef)\n this.viewName = 'confirmation'\n this.showReferenceNumber = model.def.options?.showReferenceNumber ?? false\n }\n\n getRelevantPath() {\n return this.getStatusPath()\n }\n\n makeGetRouteHandler() {\n return async (\n request: FormRequest,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { viewModel, viewName } = this\n\n const cacheService = getCacheService(request.server)\n const confirmationState = await cacheService.getConfirmationState(request)\n\n // If there's no confirmation state, then\n // redirect the user back to the start of the form\n if (!confirmationState.confirmed) {\n return this.proceed(request, h, this.getStartPath())\n }\n\n const slug = request.params.slug\n const { formsService } = this.model.services\n const { getFormMetadata, getFormMetadataById } = formsService\n\n const { submissionGuidance } = await getFormMetadata(slug)\n\n // Re-read form name if overriding display (for example, in a feedback form)\n const storedFormId = confirmationState.formId\n const formName = storedFormId\n ? (await getFormMetadataById(storedFormId)).title\n : undefined\n\n return h.view(viewName, {\n ...viewModel,\n submissionGuidance,\n formName,\n showReferenceNumber: this.showReferenceNumber,\n referenceNumber: confirmationState.referenceNumber\n })\n }\n }\n}\n"],"mappings":"AAEA,SAASA,eAAe;AAExB,SAASC,sBAAsB;AAO/B,OAAO,MAAMC,oBAAoB,SAASD,sBAAsB,CAAC;EAE/DE,gBAAgB,GAAG,KAAK;EACxBC,mBAAmB,GAAG,KAAK;EAE3BC,WAAWA,CAACC,KAAgB,EAAEC,OAAmB,EAAE;IACjD,KAAK,CAACD,KAAK,EAAEC,OAAO,CAAC;IACrB,IAAI,CAACC,QAAQ,GAAG,cAAc;IAC9B,IAAI,CAACJ,mBAAmB,GAAGE,KAAK,CAACG,GAAG,CAACC,OAAO,EAAEN,mBAAmB,IAAI,KAAK;EAC5E;EAEAO,eAAeA,CAAA,EAAG;IAChB,OAAO,IAAI,CAACC,aAAa,CAAC,CAAC;EAC7B;EAEAC,mBAAmBA,CAAA,EAAG;IACpB,OAAO,OACLC,OAAoB,EACpBC,OAAoB,EACpBC,CAAsB,KACnB;MACH,MAAM;QAAEC,SAAS;QAAET;MAAS,CAAC,GAAG,IAAI;MAEpC,MAAMU,YAAY,GAAGlB,eAAe,CAACc,OAAO,CAACK,MAAM,CAAC;MACpD,MAAMC,iBAAiB,GAAG,MAAMF,YAAY,CAACG,oBAAoB,CAACP,OAAO,CAAC;;MAE1E;MACA;MACA,IAAI,CAACM,iBAAiB,CAACE,SAAS,EAAE;QAChC,OAAO,IAAI,CAACC,OAAO,CAACT,OAAO,EAAEE,CAAC,EAAE,IAAI,CAACQ,YAAY,CAAC,CAAC,CAAC;MACtD;MAEA,MAAMC,IAAI,GAAGX,OAAO,CAACY,MAAM,CAACD,IAAI;MAChC,MAAM;QAAEE;MAAa,CAAC,GAAG,IAAI,CAACrB,KAAK,CAACsB,QAAQ;MAC5C,MAAM;QAAEC,eAAe;QAAEC;MAAoB,CAAC,GAAGH,YAAY;MAE7D,MAAM;QAAEI;MAAmB,CAAC,GAAG,MAAMF,eAAe,CAACJ,IAAI,CAAC;;MAE1D;MACA,MAAMO,YAAY,GAAGZ,iBAAiB,CAACa,MAAM;MAC7C,MAAMC,QAAQ,GAAGF,YAAY,GACzB,CAAC,MAAMF,mBAAmB,CAACE,YAAY,CAAC,EAAEG,KAAK,GAC/CC,SAAS;MAEb,OAAOpB,CAAC,CAACqB,IAAI,CAAC7B,QAAQ,EAAE;QACtB,GAAGS,SAAS;QACZc,kBAAkB;QAClBG,QAAQ;QACR9B,mBAAmB,EAAE,IAAI,CAACA,mBAAmB;QAC7CkC,eAAe,EAAElB,iBAAiB,CAACkB;MACrC,CAAC,CAAC;IACJ,CAAC;EACH;AACF","ignoreList":[]}
|
|
@@ -119,7 +119,8 @@ export class SummaryPageController extends QuestionPageController {
|
|
|
119
119
|
}
|
|
120
120
|
await cacheService.setConfirmationState(request, {
|
|
121
121
|
confirmed: true,
|
|
122
|
-
formId: context.state.formId
|
|
122
|
+
formId: context.state.formId,
|
|
123
|
+
referenceNumber: context.referenceNumber
|
|
123
124
|
});
|
|
124
125
|
|
|
125
126
|
// Clear all form data
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SummaryPageController.js","names":["hasComponentsEvenIfNoNext","Boom","COMPONENT_STATE_ERROR","ComponentCollection","getAnswer","checkEmailAddressForLiveFormSubmission","checkFormStatus","createError","getCacheService","SummaryViewModel","QuestionPageController","InvalidComponentStateError","FormAction","SummaryPageController","allowSaveAndExit","constructor","model","pageDef","viewName","collection","components","page","getSummaryViewModel","request","context","viewModel","query","payload","errors","getViewModel","backLink","getBackLink","feedbackLink","phaseTag","shouldShowSaveAndExit","server","makeGetRouteHandler","h","hasMissingNotificationEmail","view","makePostRouteHandler","action","SaveAndExit","handleSaveAndExit","handleFormSubmit","params","cacheService","formsService","services","getFormMetadata","formMetadata","slug","notificationEmail","isPreview","submitForm","error","govukError","component","name","userMessage","yar","flash","resetComponentStates","getStateKeys","proceed","path","setConfirmationState","confirmed","formId","state","clearState","getStatusPath","postRouteOptions","ext","onPreHandler","method","continue","metadata","summaryViewModel","emailAddress","finaliseComponents","formStatus","logTags","logger","info","items","getFormSubmissionData","details","submitResponse","submitData","id","undefined","badRequest","outputService","submit","relevantFields","relevantPages","flatMap","fields","onSubmit","retrievalKey","sessionId","formSubmissionService","main","filter","item","map","title","label","value","field","format","repeaters","subItems","detailItems","subItem","href","flat"],"sources":["../../../../../src/server/plugins/engine/pageControllers/SummaryPageController.ts"],"sourcesContent":["import {\n hasComponentsEvenIfNoNext,\n type FormMetadata,\n type Page,\n type SubmitPayload\n} from '@defra/forms-model'\nimport Boom from '@hapi/boom'\nimport { type RouteOptions } from '@hapi/hapi'\n\nimport { COMPONENT_STATE_ERROR } from '~/src/server/constants.js'\nimport { ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'\nimport { getAnswer } from '~/src/server/plugins/engine/components/helpers/components.js'\nimport {\n checkEmailAddressForLiveFormSubmission,\n checkFormStatus,\n createError,\n getCacheService\n} from '~/src/server/plugins/engine/helpers.js'\nimport {\n SummaryViewModel,\n type FormModel\n} from '~/src/server/plugins/engine/models/index.js'\nimport {\n type Detail,\n type DetailItem\n} from '~/src/server/plugins/engine/models/types.js'\nimport { QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'\nimport { InvalidComponentStateError } from '~/src/server/plugins/engine/pageControllers/errors.js'\nimport {\n type FormConfirmationState,\n type FormContext,\n type FormContextRequest\n} from '~/src/server/plugins/engine/types.js'\nimport {\n FormAction,\n type FormRequest,\n type FormRequestPayload,\n type FormRequestPayloadRefs,\n type FormResponseToolkit\n} from '~/src/server/routes/types.js'\n\nexport class SummaryPageController extends QuestionPageController {\n declare pageDef: Page\n allowSaveAndExit = true\n\n /**\n * The controller which is used when Page[\"controller\"] is defined as \"./pages/summary.js\"\n */\n\n constructor(model: FormModel, pageDef: Page) {\n super(model, pageDef)\n this.viewName = 'summary'\n\n // Components collection\n this.collection = new ComponentCollection(\n hasComponentsEvenIfNoNext(pageDef) ? pageDef.components : [],\n { model, page: this }\n )\n }\n\n getSummaryViewModel(\n request: FormContextRequest,\n context: FormContext\n ): SummaryViewModel {\n const viewModel = new SummaryViewModel(request, this, context)\n\n const { query } = request\n const { payload, errors } = context\n const components = this.collection.getViewModel(payload, errors, query)\n\n // We already figure these out in the base page controller. Take them and apply them to our page-specific model.\n // This is a stop-gap until we can add proper inheritance in place.\n viewModel.backLink = this.getBackLink(request, context)\n viewModel.feedbackLink = this.feedbackLink\n viewModel.phaseTag = this.phaseTag\n viewModel.components = components\n viewModel.allowSaveAndExit = this.shouldShowSaveAndExit(request.server)\n viewModel.errors = errors\n\n return viewModel\n }\n\n /**\n * Returns an async function. This is called in plugin.ts when there is a GET request at `/{id}/{path*}`,\n */\n makeGetRouteHandler() {\n return async (\n request: FormRequest,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { viewName } = this\n\n const viewModel = this.getSummaryViewModel(request, context)\n\n viewModel.hasMissingNotificationEmail =\n await this.hasMissingNotificationEmail(request, context)\n\n return h.view(viewName, viewModel)\n }\n }\n\n /**\n * Returns an async function. This is called in plugin.ts when there is a POST request at `/{id}/{path*}`.\n * If a form is incomplete, a user will be redirected to the start page.\n */\n makePostRouteHandler() {\n return async (\n request: FormRequestPayload,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n // Check if this is a save-and-exit action\n const { action } = request.payload\n if (action === FormAction.SaveAndExit) {\n return this.handleSaveAndExit(request, context, h)\n }\n\n return this.handleFormSubmit(request, context, h)\n }\n }\n\n async handleFormSubmit(\n request: FormRequestPayload,\n context: FormContext,\n h: FormResponseToolkit\n ) {\n const { model } = this\n const { params } = request\n\n const cacheService = getCacheService(request.server)\n\n const { formsService } = this.model.services\n const { getFormMetadata } = formsService\n\n // Get the form metadata using the `slug` param\n const formMetadata = await getFormMetadata(params.slug)\n const { notificationEmail } = formMetadata\n const { isPreview } = checkFormStatus(request.params)\n\n checkEmailAddressForLiveFormSubmission(notificationEmail, isPreview)\n\n // Send submission email\n if (notificationEmail) {\n const viewModel = this.getSummaryViewModel(request, context)\n\n try {\n await submitForm(\n context,\n formMetadata,\n request,\n viewModel,\n model,\n notificationEmail,\n formMetadata\n )\n } catch (error) {\n if (error instanceof InvalidComponentStateError) {\n const govukError = createError(\n error.component.name,\n error.userMessage\n )\n\n request.yar.flash(COMPONENT_STATE_ERROR, govukError, true)\n\n await cacheService.resetComponentStates(request, error.getStateKeys())\n\n return this.proceed(request, h, error.component.page?.path)\n }\n\n throw error\n }\n }\n\n await cacheService.setConfirmationState(request, {\n confirmed: true,\n formId: context.state.formId\n } as FormConfirmationState)\n\n // Clear all form data\n await cacheService.clearState(request)\n\n return this.proceed(request, h, this.getStatusPath())\n }\n\n get postRouteOptions(): RouteOptions<FormRequestPayloadRefs> {\n return {\n ext: {\n onPreHandler: {\n method(request, h) {\n return h.continue\n }\n }\n }\n }\n }\n}\n\nexport async function submitForm(\n context: FormContext,\n metadata: FormMetadata,\n request: FormRequestPayload,\n summaryViewModel: SummaryViewModel,\n model: FormModel,\n emailAddress: string,\n formMetadata: FormMetadata\n) {\n await finaliseComponents(request, metadata, context)\n\n const formStatus = checkFormStatus(request.params)\n const logTags = ['submit', 'submissionApi']\n\n request.logger.info(logTags, 'Preparing email', formStatus)\n\n // Get detail items\n const items = getFormSubmissionData(\n summaryViewModel.context,\n summaryViewModel.details\n )\n\n // Submit data\n request.logger.info(logTags, 'Submitting data')\n const submitResponse = await submitData(\n model,\n items,\n emailAddress,\n request.yar.id\n )\n\n if (submitResponse === undefined) {\n throw Boom.badRequest('Unexpected empty response from submit api')\n }\n\n return model.services.outputService.submit(\n context,\n request,\n model,\n emailAddress,\n items,\n submitResponse,\n formMetadata\n )\n}\n\n/**\n * Finalises any components that need post-processing before form submission. Candidates usually involve\n * those that have external state.\n * Examples include:\n * - file uploads which are 'persisted' before submission\n * - payments which are 'captured' before submission\n */\nasync function finaliseComponents(\n request: FormRequestPayload,\n metadata: FormMetadata,\n context: FormContext\n) {\n const relevantFields = context.relevantPages.flatMap(\n (page) => page.collection.fields\n )\n\n for (const component of relevantFields) {\n /*\n Each component will throw InvalidComponent if its state is invalid, which is handled\n by handleFormSubmit\n */\n await component.onSubmit(request, metadata, context)\n }\n}\n\nfunction submitData(\n model: FormModel,\n items: DetailItem[],\n retrievalKey: string,\n sessionId: string\n) {\n const { formSubmissionService } = model.services\n const { submit } = formSubmissionService\n\n const payload: SubmitPayload = {\n sessionId,\n retrievalKey,\n\n // Main form answers\n main: items\n .filter((item) => 'field' in item)\n .map((item) => ({\n name: item.name,\n title: item.label,\n value: getAnswer(item.field, item.state, { format: 'data' })\n })),\n\n // Repeater form answers\n repeaters: items\n .filter((item) => 'subItems' in item)\n .map((item) => ({\n name: item.name,\n title: item.label,\n\n // Repeater item values\n value: item.subItems.map((detailItems) =>\n detailItems.map((subItem) => ({\n name: subItem.name,\n title: subItem.label,\n value: getAnswer(subItem.field, subItem.state, { format: 'data' })\n }))\n )\n }))\n }\n\n return submit(payload)\n}\n\nexport function getFormSubmissionData(context: FormContext, details: Detail[]) {\n return context.relevantPages\n .map(({ href }) =>\n details.flatMap(({ items }) =>\n items.filter(({ page }) => page.href === href)\n )\n )\n .flat()\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"SummaryPageController.js","names":["hasComponentsEvenIfNoNext","Boom","COMPONENT_STATE_ERROR","ComponentCollection","getAnswer","checkEmailAddressForLiveFormSubmission","checkFormStatus","createError","getCacheService","SummaryViewModel","QuestionPageController","InvalidComponentStateError","FormAction","SummaryPageController","allowSaveAndExit","constructor","model","pageDef","viewName","collection","components","page","getSummaryViewModel","request","context","viewModel","query","payload","errors","getViewModel","backLink","getBackLink","feedbackLink","phaseTag","shouldShowSaveAndExit","server","makeGetRouteHandler","h","hasMissingNotificationEmail","view","makePostRouteHandler","action","SaveAndExit","handleSaveAndExit","handleFormSubmit","params","cacheService","formsService","services","getFormMetadata","formMetadata","slug","notificationEmail","isPreview","submitForm","error","govukError","component","name","userMessage","yar","flash","resetComponentStates","getStateKeys","proceed","path","setConfirmationState","confirmed","formId","state","referenceNumber","clearState","getStatusPath","postRouteOptions","ext","onPreHandler","method","continue","metadata","summaryViewModel","emailAddress","finaliseComponents","formStatus","logTags","logger","info","items","getFormSubmissionData","details","submitResponse","submitData","id","undefined","badRequest","outputService","submit","relevantFields","relevantPages","flatMap","fields","onSubmit","retrievalKey","sessionId","formSubmissionService","main","filter","item","map","title","label","value","field","format","repeaters","subItems","detailItems","subItem","href","flat"],"sources":["../../../../../src/server/plugins/engine/pageControllers/SummaryPageController.ts"],"sourcesContent":["import {\n hasComponentsEvenIfNoNext,\n type FormMetadata,\n type Page,\n type SubmitPayload\n} from '@defra/forms-model'\nimport Boom from '@hapi/boom'\nimport { type RouteOptions } from '@hapi/hapi'\n\nimport { COMPONENT_STATE_ERROR } from '~/src/server/constants.js'\nimport { ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'\nimport { getAnswer } from '~/src/server/plugins/engine/components/helpers/components.js'\nimport {\n checkEmailAddressForLiveFormSubmission,\n checkFormStatus,\n createError,\n getCacheService\n} from '~/src/server/plugins/engine/helpers.js'\nimport {\n SummaryViewModel,\n type FormModel\n} from '~/src/server/plugins/engine/models/index.js'\nimport {\n type Detail,\n type DetailItem\n} from '~/src/server/plugins/engine/models/types.js'\nimport { QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'\nimport { InvalidComponentStateError } from '~/src/server/plugins/engine/pageControllers/errors.js'\nimport {\n type FormConfirmationState,\n type FormContext,\n type FormContextRequest\n} from '~/src/server/plugins/engine/types.js'\nimport {\n FormAction,\n type FormRequest,\n type FormRequestPayload,\n type FormRequestPayloadRefs,\n type FormResponseToolkit\n} from '~/src/server/routes/types.js'\n\nexport class SummaryPageController extends QuestionPageController {\n declare pageDef: Page\n allowSaveAndExit = true\n\n /**\n * The controller which is used when Page[\"controller\"] is defined as \"./pages/summary.js\"\n */\n\n constructor(model: FormModel, pageDef: Page) {\n super(model, pageDef)\n this.viewName = 'summary'\n\n // Components collection\n this.collection = new ComponentCollection(\n hasComponentsEvenIfNoNext(pageDef) ? pageDef.components : [],\n { model, page: this }\n )\n }\n\n getSummaryViewModel(\n request: FormContextRequest,\n context: FormContext\n ): SummaryViewModel {\n const viewModel = new SummaryViewModel(request, this, context)\n\n const { query } = request\n const { payload, errors } = context\n const components = this.collection.getViewModel(payload, errors, query)\n\n // We already figure these out in the base page controller. Take them and apply them to our page-specific model.\n // This is a stop-gap until we can add proper inheritance in place.\n viewModel.backLink = this.getBackLink(request, context)\n viewModel.feedbackLink = this.feedbackLink\n viewModel.phaseTag = this.phaseTag\n viewModel.components = components\n viewModel.allowSaveAndExit = this.shouldShowSaveAndExit(request.server)\n viewModel.errors = errors\n\n return viewModel\n }\n\n /**\n * Returns an async function. This is called in plugin.ts when there is a GET request at `/{id}/{path*}`,\n */\n makeGetRouteHandler() {\n return async (\n request: FormRequest,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { viewName } = this\n\n const viewModel = this.getSummaryViewModel(request, context)\n\n viewModel.hasMissingNotificationEmail =\n await this.hasMissingNotificationEmail(request, context)\n\n return h.view(viewName, viewModel)\n }\n }\n\n /**\n * Returns an async function. This is called in plugin.ts when there is a POST request at `/{id}/{path*}`.\n * If a form is incomplete, a user will be redirected to the start page.\n */\n makePostRouteHandler() {\n return async (\n request: FormRequestPayload,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n // Check if this is a save-and-exit action\n const { action } = request.payload\n if (action === FormAction.SaveAndExit) {\n return this.handleSaveAndExit(request, context, h)\n }\n\n return this.handleFormSubmit(request, context, h)\n }\n }\n\n async handleFormSubmit(\n request: FormRequestPayload,\n context: FormContext,\n h: FormResponseToolkit\n ) {\n const { model } = this\n const { params } = request\n\n const cacheService = getCacheService(request.server)\n\n const { formsService } = this.model.services\n const { getFormMetadata } = formsService\n\n // Get the form metadata using the `slug` param\n const formMetadata = await getFormMetadata(params.slug)\n const { notificationEmail } = formMetadata\n const { isPreview } = checkFormStatus(request.params)\n\n checkEmailAddressForLiveFormSubmission(notificationEmail, isPreview)\n\n // Send submission email\n if (notificationEmail) {\n const viewModel = this.getSummaryViewModel(request, context)\n\n try {\n await submitForm(\n context,\n formMetadata,\n request,\n viewModel,\n model,\n notificationEmail,\n formMetadata\n )\n } catch (error) {\n if (error instanceof InvalidComponentStateError) {\n const govukError = createError(\n error.component.name,\n error.userMessage\n )\n\n request.yar.flash(COMPONENT_STATE_ERROR, govukError, true)\n\n await cacheService.resetComponentStates(request, error.getStateKeys())\n\n return this.proceed(request, h, error.component.page?.path)\n }\n\n throw error\n }\n }\n\n await cacheService.setConfirmationState(request, {\n confirmed: true,\n formId: context.state.formId,\n referenceNumber: context.referenceNumber\n } as FormConfirmationState)\n\n // Clear all form data\n await cacheService.clearState(request)\n\n return this.proceed(request, h, this.getStatusPath())\n }\n\n get postRouteOptions(): RouteOptions<FormRequestPayloadRefs> {\n return {\n ext: {\n onPreHandler: {\n method(request, h) {\n return h.continue\n }\n }\n }\n }\n }\n}\n\nexport async function submitForm(\n context: FormContext,\n metadata: FormMetadata,\n request: FormRequestPayload,\n summaryViewModel: SummaryViewModel,\n model: FormModel,\n emailAddress: string,\n formMetadata: FormMetadata\n) {\n await finaliseComponents(request, metadata, context)\n\n const formStatus = checkFormStatus(request.params)\n const logTags = ['submit', 'submissionApi']\n\n request.logger.info(logTags, 'Preparing email', formStatus)\n\n // Get detail items\n const items = getFormSubmissionData(\n summaryViewModel.context,\n summaryViewModel.details\n )\n\n // Submit data\n request.logger.info(logTags, 'Submitting data')\n const submitResponse = await submitData(\n model,\n items,\n emailAddress,\n request.yar.id\n )\n\n if (submitResponse === undefined) {\n throw Boom.badRequest('Unexpected empty response from submit api')\n }\n\n return model.services.outputService.submit(\n context,\n request,\n model,\n emailAddress,\n items,\n submitResponse,\n formMetadata\n )\n}\n\n/**\n * Finalises any components that need post-processing before form submission. Candidates usually involve\n * those that have external state.\n * Examples include:\n * - file uploads which are 'persisted' before submission\n * - payments which are 'captured' before submission\n */\nasync function finaliseComponents(\n request: FormRequestPayload,\n metadata: FormMetadata,\n context: FormContext\n) {\n const relevantFields = context.relevantPages.flatMap(\n (page) => page.collection.fields\n )\n\n for (const component of relevantFields) {\n /*\n Each component will throw InvalidComponent if its state is invalid, which is handled\n by handleFormSubmit\n */\n await component.onSubmit(request, metadata, context)\n }\n}\n\nfunction submitData(\n model: FormModel,\n items: DetailItem[],\n retrievalKey: string,\n sessionId: string\n) {\n const { formSubmissionService } = model.services\n const { submit } = formSubmissionService\n\n const payload: SubmitPayload = {\n sessionId,\n retrievalKey,\n\n // Main form answers\n main: items\n .filter((item) => 'field' in item)\n .map((item) => ({\n name: item.name,\n title: item.label,\n value: getAnswer(item.field, item.state, { format: 'data' })\n })),\n\n // Repeater form answers\n repeaters: items\n .filter((item) => 'subItems' in item)\n .map((item) => ({\n name: item.name,\n title: item.label,\n\n // Repeater item values\n value: item.subItems.map((detailItems) =>\n detailItems.map((subItem) => ({\n name: subItem.name,\n title: subItem.label,\n value: getAnswer(subItem.field, subItem.state, { format: 'data' })\n }))\n )\n }))\n }\n\n return submit(payload)\n}\n\nexport function getFormSubmissionData(context: FormContext, details: Detail[]) {\n return context.relevantPages\n .map(({ href }) =>\n details.flatMap(({ items }) =>\n items.filter(({ page }) => page.href === href)\n )\n )\n .flat()\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -62,6 +62,7 @@ export interface FormSubmissionError extends Pick<ValidationErrorItem, 'context'
|
|
|
62
62
|
export interface FormConfirmationState {
|
|
63
63
|
confirmed?: true;
|
|
64
64
|
formId?: string;
|
|
65
|
+
referenceNumber?: string;
|
|
65
66
|
}
|
|
66
67
|
export interface FormPayloadParams {
|
|
67
68
|
action?: FormAction;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","names":["FileStatus","UploadStatus"],"sources":["../../../../src/server/plugins/engine/types.ts"],"sourcesContent":["import {\n type ComponentDef,\n type Event,\n type FormVersionMetadata,\n type Item,\n type List,\n type Page,\n type UkAddressFieldComponent\n} from '@defra/forms-model'\nimport {\n type PluginProperties,\n type Request,\n type ResponseObject\n} from '@hapi/hapi'\nimport { type JoiExpression, type ValidationErrorItem } from 'joi'\n\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { type UkAddressState } from '~/src/server/plugins/engine/components/UkAddressField.js'\nimport { type Component } from '~/src/server/plugins/engine/components/helpers/components.js'\nimport { type FileUploadField } from '~/src/server/plugins/engine/components/index.js'\nimport {\n type BackLink,\n type ComponentText,\n type ComponentViewModel,\n type DatePartsState,\n type EastingNorthingState,\n type LatLongState,\n type MonthYearState\n} from '~/src/server/plugins/engine/components/types.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type DetailItemField } from '~/src/server/plugins/engine/models/types.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers/pages.js'\nimport { type QuestionPageController } from '~/src/server/plugins/engine/pageControllers/index.js'\nimport {\n type FileStatus,\n type FormAdapterSubmissionSchemaVersion,\n type UploadStatus\n} from '~/src/server/plugins/engine/types/enums.js'\nimport { type ViewContext } from '~/src/server/plugins/nunjucks/types.js'\nimport {\n type FormAction,\n type FormRequest,\n type FormRequestPayload,\n type FormResponseToolkit,\n type FormStatus\n} from '~/src/server/routes/types.js'\nimport { type CacheService } from '~/src/server/services/cacheService.js'\nimport { type RequestOptions } from '~/src/server/services/httpService.js'\nimport { type Services } from '~/src/server/types.js'\n\nexport type AnyFormRequest = FormRequest | FormRequestPayload\nexport type AnyRequest = Request | AnyFormRequest\n\n/**\n * Form submission state stores the following in Redis:\n * Props containing user's submitted values as `{ [inputId]: value }` or as `{ [sectionName]: { [inputName]: value } }`\n * a) . e.g:\n * ```ts\n * {\n * _C9PRHmsgt: 'Ben',\n * WfLk9McjzX: 'Music',\n * IK7jkUFCBL: 'Royal Academy of Music'\n * }\n * ```\n *\n * b)\n * ```ts\n * {\n * checkBeforeYouStart: { ukPassport: true },\n * applicantDetails: {\n * numberOfApplicants: 1,\n * phoneNumber: '77777777',\n * emailAddress: 'aaa@aaa.com'\n * },\n * applicantOneDetails: {\n * firstName: 'a',\n * middleName: 'a',\n * lastName: 'a',\n * address: { addressLine1: 'a', addressLine2: 'a', town: 'a', postcode: 'a' }\n * }\n * }\n * ```\n */\n\n/**\n * Form submission state\n */\nexport type FormSubmissionState = {\n upload?: Record<string, TempFileState>\n} & FormState\n\nexport interface FormSubmissionError\n extends Pick<ValidationErrorItem, 'context' | 'path'> {\n href: string // e.g: '#dateField__day'\n name: string // e.g: 'dateField__day'\n text: string // e.g: 'Date field must be a real date'\n}\n\nexport interface FormConfirmationState {\n confirmed?: true\n formId?: string\n}\n\nexport interface FormPayloadParams {\n action?: FormAction\n confirm?: true\n crumb?: string\n itemId?: string\n}\n\n/**\n * Form POST for question pages\n * (after Joi has converted value types)\n */\nexport type FormPayload = FormPayloadParams & Partial<Record<string, FormValue>>\n\nexport type FormValue =\n | Item['value']\n | Item['value'][]\n | UploadState\n | RepeatListState\n | undefined\n\nexport type FormState = Partial<Record<string, FormStateValue>>\nexport type FormStateValue = Exclude<FormValue, undefined> | null\n\nexport interface FormValidationResult<\n ValueType extends FormPayload | FormSubmissionState\n> {\n value: ValueType\n errors: FormSubmissionError[] | undefined\n}\n\nexport interface FormContext {\n /**\n * Evaluation form state only (filtered by visited paths),\n * with values formatted for condition evaluation using\n * {@link FormComponent.getContextValueFromState}\n */\n evaluationState: FormState\n\n /**\n * Relevant form state only (filtered by visited paths)\n */\n relevantState: FormState\n\n /**\n * Relevant pages only (filtered by visited paths)\n */\n relevantPages: PageControllerClass[]\n\n /**\n * Form submission payload (single page)\n */\n payload: FormPayload\n\n /**\n * Form submission state (entire form)\n */\n state: FormSubmissionState\n\n /**\n * Validation errors (entire form)\n */\n errors?: FormSubmissionError[]\n\n /**\n * Visited paths evaluated from form state\n */\n paths: string[]\n\n /**\n * Preview URL direct access is allowed\n */\n isForceAccess: boolean\n\n /**\n * Miscellaneous extra data from event responses\n */\n data: object\n\n pageDefMap: Map<string, Page>\n listDefMap: Map<string, List>\n componentDefMap: Map<string, ComponentDef>\n pageMap: Map<string, PageControllerClass>\n componentMap: Map<string, Component>\n referenceNumber: string\n submittedVersionNumber?: number\n}\n\nexport type FormContextRequest = (\n | {\n method: 'get'\n payload?: undefined\n }\n | {\n method: 'post'\n payload: FormPayload\n }\n | {\n method: FormRequest['method']\n payload?: object | undefined\n }\n) &\n Pick<\n FormRequest,\n 'app' | 'method' | 'params' | 'path' | 'query' | 'url' | 'server'\n >\n\nexport interface UploadInitiateResponse {\n uploadId: string\n uploadUrl: string\n statusUrl: string\n}\n\nexport {\n FileStatus,\n UploadStatus\n} from '~/src/server/plugins/engine/types/enums.js'\n\nexport type UploadState = FileState[]\n\nexport type FileUpload = {\n fileId: string\n filename: string\n contentLength: number\n} & (\n | {\n fileStatus: FileStatus.complete | FileStatus.rejected | FileStatus.pending\n errorMessage?: string\n }\n | {\n fileStatus: FileStatus.complete\n errorMessage?: undefined\n }\n)\n\nexport interface FileUploadMetadata {\n retrievalKey: string\n}\n\nexport type UploadStatusResponse =\n | {\n uploadStatus: UploadStatus.initiated\n metadata: FileUploadMetadata\n form: { file?: undefined }\n }\n | {\n uploadStatus: UploadStatus.pending | UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles?: number\n }\n | {\n uploadStatus: UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles: 0\n }\n\nexport type UploadStatusFileResponse = Exclude<\n UploadStatusResponse,\n { uploadStatus: UploadStatus.initiated }\n>\n\nexport interface FileState {\n uploadId: string\n status: UploadStatusFileResponse\n}\n\nexport interface TempFileState {\n upload?: UploadInitiateResponse\n files: UploadState\n}\n\nexport interface RepeatItemState extends FormPayload {\n itemId: string\n}\n\nexport type RepeatListState = RepeatItemState[]\n\nexport interface CheckAnswers {\n title?: ComponentText\n summaryList: SummaryList\n}\n\nexport interface SummaryList {\n classes?: string\n rows: SummaryListRow[]\n}\n\nexport interface SummaryListRow {\n key: ComponentText\n value: ComponentText\n actions?: { items: SummaryListAction[] }\n}\n\nexport type SummaryListAction = ComponentText & {\n href: string\n visuallyHiddenText: string\n}\n\nexport interface PageViewModelBase extends Partial<ViewContext> {\n page: PageController\n name?: string\n pageTitle: string\n sectionTitle?: string\n showTitle: boolean\n isStartPage: boolean\n backLink?: BackLink\n feedbackLink?: string\n serviceUrl: string\n phaseTag?: string\n}\n\nexport interface ItemDeletePageViewModel extends PageViewModelBase {\n context: FormContext\n itemTitle: string\n confirmation?: ComponentText\n buttonConfirm: ComponentText\n buttonCancel: ComponentText\n}\n\nexport interface FormPageViewModel extends PageViewModelBase {\n components: ComponentViewModel[]\n context: FormContext\n errors?: FormSubmissionError[]\n hasMissingNotificationEmail?: boolean\n allowSaveAndExit: boolean\n}\n\nexport interface RepeaterSummaryPageViewModel extends PageViewModelBase {\n context: FormContext\n errors?: FormSubmissionError[]\n checkAnswers: CheckAnswers[]\n repeatTitle: string\n allowSaveAndExit: boolean\n}\n\nexport interface FeaturedFormPageViewModel extends FormPageViewModel {\n formAction?: string\n formComponent: ComponentViewModel\n componentsBefore: ComponentViewModel[]\n uploadId: string | undefined\n proxyUrl: string | null\n}\n\nexport type PageViewModel =\n | PageViewModelBase\n | ItemDeletePageViewModel\n | FormPageViewModel\n | RepeaterSummaryPageViewModel\n | FeaturedFormPageViewModel\n\nexport type GlobalFunction = (value: unknown) => unknown\nexport type FilterFunction = (value: unknown) => unknown\nexport interface ErrorMessageTemplate {\n type: string\n template: JoiExpression\n}\n\nexport interface ErrorMessageTemplateList {\n baseErrors: ErrorMessageTemplate[]\n advancedSettingsErrors: ErrorMessageTemplate[]\n}\n\nexport type PreparePageEventRequestOptions = (\n options: RequestOptions,\n event: Event,\n page: PageControllerClass,\n context: FormContext\n) => void\n\nexport type OnRequestCallback = (\n request: AnyFormRequest,\n h: FormResponseToolkit,\n context: FormContext\n) =>\n | ResponseObject\n | FormResponseToolkit['continue']\n | Promise<ResponseObject | FormResponseToolkit['continue']>\n\nexport type SaveAndExitHandler = (\n request: FormRequestPayload,\n h: FormResponseToolkit,\n context: FormContext\n) => ResponseObject\n\nexport interface ExternalArgs {\n component: ComponentDef\n controller: QuestionPageController\n sourceUrl: string\n actionArgs: Record<string, string>\n}\n\nexport interface PostcodeLookupExternalArgs extends ExternalArgs {\n component: UkAddressFieldComponent\n actionArgs: { step: string }\n}\n\nexport interface ExternalStateAppendage {\n component: string\n data: FormStateValue | FormState\n}\n\nexport interface PluginOptions {\n model?: FormModel\n services?: Services\n controllers?: Record<string, typeof PageController>\n cache?: CacheService | string\n globals?: Record<string, GlobalFunction>\n filters?: Record<string, FilterFunction>\n saveAndExit?: SaveAndExitHandler\n pluginPath?: string\n nunjucks: {\n baseLayoutPath: string\n paths: string[]\n }\n viewContext: PluginProperties['forms-engine-plugin']['viewContext']\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n onRequest?: OnRequestCallback\n baseUrl: string // base URL of the application, protocol and hostname e.g. \"https://myapp.com\"\n ordnanceSurveyApiKey?: string\n}\n\nexport interface FormAdapterSubmissionMessageMeta {\n schemaVersion: FormAdapterSubmissionSchemaVersion\n timestamp: Date\n referenceNumber: string\n formName: string\n formId: string\n formSlug: string\n status: FormStatus\n isPreview: boolean\n notificationEmail: string\n versionMetadata?: FormVersionMetadata\n custom?: Record<string, unknown>\n}\n\nexport type FormAdapterSubmissionMessageMetaSerialised = Omit<\n FormAdapterSubmissionMessageMeta,\n 'schemaVersion' | 'timestamp' | 'status'\n> & {\n schemaVersion: number\n status: string\n timestamp: string\n}\nexport interface FormAdapterFile {\n fileName: string\n fileId: string\n userDownloadLink: string\n}\n\nexport interface FormAdapterSubmissionMessageResult {\n files: {\n main: string\n repeaters: Record<string, string>\n }\n}\n\n/**\n * A detail item specifically for files\n */\nexport type FileUploadFieldDetailitem = Omit<DetailItemField, 'field'> & {\n field: FileUploadField\n}\nexport type RichFormValue =\n | FormValue\n | FormPayload\n | DatePartsState\n | MonthYearState\n | UkAddressState\n | EastingNorthingState\n | LatLongState\n\nexport interface FormAdapterSubmissionMessageData {\n main: Record<string, RichFormValue | null>\n repeaters: Record<string, Record<string, RichFormValue>[]>\n files: Record<string, FormAdapterFile[]>\n}\n\nexport interface FormAdapterSubmissionMessagePayload {\n meta: FormAdapterSubmissionMessageMeta\n data: FormAdapterSubmissionMessageData\n result: FormAdapterSubmissionMessageResult\n}\n\nexport interface FormAdapterSubmissionMessage\n extends FormAdapterSubmissionMessagePayload {\n messageId: string\n recordCreatedAt: Date\n}\n\nexport interface FormAdapterSubmissionService {\n handleFormSubmission: (\n submissionMessage: FormAdapterSubmissionMessage\n ) => unknown\n}\n"],"mappings":"AAsDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAwBA;AACA;AACA;AACA;;AAsGA,SACEA,UAAU,EACVC,YAAY;;AAmPd;AACA;AACA","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"types.js","names":["FileStatus","UploadStatus"],"sources":["../../../../src/server/plugins/engine/types.ts"],"sourcesContent":["import {\n type ComponentDef,\n type Event,\n type FormVersionMetadata,\n type Item,\n type List,\n type Page,\n type UkAddressFieldComponent\n} from '@defra/forms-model'\nimport {\n type PluginProperties,\n type Request,\n type ResponseObject\n} from '@hapi/hapi'\nimport { type JoiExpression, type ValidationErrorItem } from 'joi'\n\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { type UkAddressState } from '~/src/server/plugins/engine/components/UkAddressField.js'\nimport { type Component } from '~/src/server/plugins/engine/components/helpers/components.js'\nimport { type FileUploadField } from '~/src/server/plugins/engine/components/index.js'\nimport {\n type BackLink,\n type ComponentText,\n type ComponentViewModel,\n type DatePartsState,\n type EastingNorthingState,\n type LatLongState,\n type MonthYearState\n} from '~/src/server/plugins/engine/components/types.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type DetailItemField } from '~/src/server/plugins/engine/models/types.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers/pages.js'\nimport { type QuestionPageController } from '~/src/server/plugins/engine/pageControllers/index.js'\nimport {\n type FileStatus,\n type FormAdapterSubmissionSchemaVersion,\n type UploadStatus\n} from '~/src/server/plugins/engine/types/enums.js'\nimport { type ViewContext } from '~/src/server/plugins/nunjucks/types.js'\nimport {\n type FormAction,\n type FormRequest,\n type FormRequestPayload,\n type FormResponseToolkit,\n type FormStatus\n} from '~/src/server/routes/types.js'\nimport { type CacheService } from '~/src/server/services/cacheService.js'\nimport { type RequestOptions } from '~/src/server/services/httpService.js'\nimport { type Services } from '~/src/server/types.js'\n\nexport type AnyFormRequest = FormRequest | FormRequestPayload\nexport type AnyRequest = Request | AnyFormRequest\n\n/**\n * Form submission state stores the following in Redis:\n * Props containing user's submitted values as `{ [inputId]: value }` or as `{ [sectionName]: { [inputName]: value } }`\n * a) . e.g:\n * ```ts\n * {\n * _C9PRHmsgt: 'Ben',\n * WfLk9McjzX: 'Music',\n * IK7jkUFCBL: 'Royal Academy of Music'\n * }\n * ```\n *\n * b)\n * ```ts\n * {\n * checkBeforeYouStart: { ukPassport: true },\n * applicantDetails: {\n * numberOfApplicants: 1,\n * phoneNumber: '77777777',\n * emailAddress: 'aaa@aaa.com'\n * },\n * applicantOneDetails: {\n * firstName: 'a',\n * middleName: 'a',\n * lastName: 'a',\n * address: { addressLine1: 'a', addressLine2: 'a', town: 'a', postcode: 'a' }\n * }\n * }\n * ```\n */\n\n/**\n * Form submission state\n */\nexport type FormSubmissionState = {\n upload?: Record<string, TempFileState>\n} & FormState\n\nexport interface FormSubmissionError\n extends Pick<ValidationErrorItem, 'context' | 'path'> {\n href: string // e.g: '#dateField__day'\n name: string // e.g: 'dateField__day'\n text: string // e.g: 'Date field must be a real date'\n}\n\nexport interface FormConfirmationState {\n confirmed?: true\n formId?: string\n referenceNumber?: string\n}\n\nexport interface FormPayloadParams {\n action?: FormAction\n confirm?: true\n crumb?: string\n itemId?: string\n}\n\n/**\n * Form POST for question pages\n * (after Joi has converted value types)\n */\nexport type FormPayload = FormPayloadParams & Partial<Record<string, FormValue>>\n\nexport type FormValue =\n | Item['value']\n | Item['value'][]\n | UploadState\n | RepeatListState\n | undefined\n\nexport type FormState = Partial<Record<string, FormStateValue>>\nexport type FormStateValue = Exclude<FormValue, undefined> | null\n\nexport interface FormValidationResult<\n ValueType extends FormPayload | FormSubmissionState\n> {\n value: ValueType\n errors: FormSubmissionError[] | undefined\n}\n\nexport interface FormContext {\n /**\n * Evaluation form state only (filtered by visited paths),\n * with values formatted for condition evaluation using\n * {@link FormComponent.getContextValueFromState}\n */\n evaluationState: FormState\n\n /**\n * Relevant form state only (filtered by visited paths)\n */\n relevantState: FormState\n\n /**\n * Relevant pages only (filtered by visited paths)\n */\n relevantPages: PageControllerClass[]\n\n /**\n * Form submission payload (single page)\n */\n payload: FormPayload\n\n /**\n * Form submission state (entire form)\n */\n state: FormSubmissionState\n\n /**\n * Validation errors (entire form)\n */\n errors?: FormSubmissionError[]\n\n /**\n * Visited paths evaluated from form state\n */\n paths: string[]\n\n /**\n * Preview URL direct access is allowed\n */\n isForceAccess: boolean\n\n /**\n * Miscellaneous extra data from event responses\n */\n data: object\n\n pageDefMap: Map<string, Page>\n listDefMap: Map<string, List>\n componentDefMap: Map<string, ComponentDef>\n pageMap: Map<string, PageControllerClass>\n componentMap: Map<string, Component>\n referenceNumber: string\n submittedVersionNumber?: number\n}\n\nexport type FormContextRequest = (\n | {\n method: 'get'\n payload?: undefined\n }\n | {\n method: 'post'\n payload: FormPayload\n }\n | {\n method: FormRequest['method']\n payload?: object | undefined\n }\n) &\n Pick<\n FormRequest,\n 'app' | 'method' | 'params' | 'path' | 'query' | 'url' | 'server'\n >\n\nexport interface UploadInitiateResponse {\n uploadId: string\n uploadUrl: string\n statusUrl: string\n}\n\nexport {\n FileStatus,\n UploadStatus\n} from '~/src/server/plugins/engine/types/enums.js'\n\nexport type UploadState = FileState[]\n\nexport type FileUpload = {\n fileId: string\n filename: string\n contentLength: number\n} & (\n | {\n fileStatus: FileStatus.complete | FileStatus.rejected | FileStatus.pending\n errorMessage?: string\n }\n | {\n fileStatus: FileStatus.complete\n errorMessage?: undefined\n }\n)\n\nexport interface FileUploadMetadata {\n retrievalKey: string\n}\n\nexport type UploadStatusResponse =\n | {\n uploadStatus: UploadStatus.initiated\n metadata: FileUploadMetadata\n form: { file?: undefined }\n }\n | {\n uploadStatus: UploadStatus.pending | UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles?: number\n }\n | {\n uploadStatus: UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles: 0\n }\n\nexport type UploadStatusFileResponse = Exclude<\n UploadStatusResponse,\n { uploadStatus: UploadStatus.initiated }\n>\n\nexport interface FileState {\n uploadId: string\n status: UploadStatusFileResponse\n}\n\nexport interface TempFileState {\n upload?: UploadInitiateResponse\n files: UploadState\n}\n\nexport interface RepeatItemState extends FormPayload {\n itemId: string\n}\n\nexport type RepeatListState = RepeatItemState[]\n\nexport interface CheckAnswers {\n title?: ComponentText\n summaryList: SummaryList\n}\n\nexport interface SummaryList {\n classes?: string\n rows: SummaryListRow[]\n}\n\nexport interface SummaryListRow {\n key: ComponentText\n value: ComponentText\n actions?: { items: SummaryListAction[] }\n}\n\nexport type SummaryListAction = ComponentText & {\n href: string\n visuallyHiddenText: string\n}\n\nexport interface PageViewModelBase extends Partial<ViewContext> {\n page: PageController\n name?: string\n pageTitle: string\n sectionTitle?: string\n showTitle: boolean\n isStartPage: boolean\n backLink?: BackLink\n feedbackLink?: string\n serviceUrl: string\n phaseTag?: string\n}\n\nexport interface ItemDeletePageViewModel extends PageViewModelBase {\n context: FormContext\n itemTitle: string\n confirmation?: ComponentText\n buttonConfirm: ComponentText\n buttonCancel: ComponentText\n}\n\nexport interface FormPageViewModel extends PageViewModelBase {\n components: ComponentViewModel[]\n context: FormContext\n errors?: FormSubmissionError[]\n hasMissingNotificationEmail?: boolean\n allowSaveAndExit: boolean\n}\n\nexport interface RepeaterSummaryPageViewModel extends PageViewModelBase {\n context: FormContext\n errors?: FormSubmissionError[]\n checkAnswers: CheckAnswers[]\n repeatTitle: string\n allowSaveAndExit: boolean\n}\n\nexport interface FeaturedFormPageViewModel extends FormPageViewModel {\n formAction?: string\n formComponent: ComponentViewModel\n componentsBefore: ComponentViewModel[]\n uploadId: string | undefined\n proxyUrl: string | null\n}\n\nexport type PageViewModel =\n | PageViewModelBase\n | ItemDeletePageViewModel\n | FormPageViewModel\n | RepeaterSummaryPageViewModel\n | FeaturedFormPageViewModel\n\nexport type GlobalFunction = (value: unknown) => unknown\nexport type FilterFunction = (value: unknown) => unknown\nexport interface ErrorMessageTemplate {\n type: string\n template: JoiExpression\n}\n\nexport interface ErrorMessageTemplateList {\n baseErrors: ErrorMessageTemplate[]\n advancedSettingsErrors: ErrorMessageTemplate[]\n}\n\nexport type PreparePageEventRequestOptions = (\n options: RequestOptions,\n event: Event,\n page: PageControllerClass,\n context: FormContext\n) => void\n\nexport type OnRequestCallback = (\n request: AnyFormRequest,\n h: FormResponseToolkit,\n context: FormContext\n) =>\n | ResponseObject\n | FormResponseToolkit['continue']\n | Promise<ResponseObject | FormResponseToolkit['continue']>\n\nexport type SaveAndExitHandler = (\n request: FormRequestPayload,\n h: FormResponseToolkit,\n context: FormContext\n) => ResponseObject\n\nexport interface ExternalArgs {\n component: ComponentDef\n controller: QuestionPageController\n sourceUrl: string\n actionArgs: Record<string, string>\n}\n\nexport interface PostcodeLookupExternalArgs extends ExternalArgs {\n component: UkAddressFieldComponent\n actionArgs: { step: string }\n}\n\nexport interface ExternalStateAppendage {\n component: string\n data: FormStateValue | FormState\n}\n\nexport interface PluginOptions {\n model?: FormModel\n services?: Services\n controllers?: Record<string, typeof PageController>\n cache?: CacheService | string\n globals?: Record<string, GlobalFunction>\n filters?: Record<string, FilterFunction>\n saveAndExit?: SaveAndExitHandler\n pluginPath?: string\n nunjucks: {\n baseLayoutPath: string\n paths: string[]\n }\n viewContext: PluginProperties['forms-engine-plugin']['viewContext']\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n onRequest?: OnRequestCallback\n baseUrl: string // base URL of the application, protocol and hostname e.g. \"https://myapp.com\"\n ordnanceSurveyApiKey?: string\n}\n\nexport interface FormAdapterSubmissionMessageMeta {\n schemaVersion: FormAdapterSubmissionSchemaVersion\n timestamp: Date\n referenceNumber: string\n formName: string\n formId: string\n formSlug: string\n status: FormStatus\n isPreview: boolean\n notificationEmail: string\n versionMetadata?: FormVersionMetadata\n custom?: Record<string, unknown>\n}\n\nexport type FormAdapterSubmissionMessageMetaSerialised = Omit<\n FormAdapterSubmissionMessageMeta,\n 'schemaVersion' | 'timestamp' | 'status'\n> & {\n schemaVersion: number\n status: string\n timestamp: string\n}\nexport interface FormAdapterFile {\n fileName: string\n fileId: string\n userDownloadLink: string\n}\n\nexport interface FormAdapterSubmissionMessageResult {\n files: {\n main: string\n repeaters: Record<string, string>\n }\n}\n\n/**\n * A detail item specifically for files\n */\nexport type FileUploadFieldDetailitem = Omit<DetailItemField, 'field'> & {\n field: FileUploadField\n}\nexport type RichFormValue =\n | FormValue\n | FormPayload\n | DatePartsState\n | MonthYearState\n | UkAddressState\n | EastingNorthingState\n | LatLongState\n\nexport interface FormAdapterSubmissionMessageData {\n main: Record<string, RichFormValue | null>\n repeaters: Record<string, Record<string, RichFormValue>[]>\n files: Record<string, FormAdapterFile[]>\n}\n\nexport interface FormAdapterSubmissionMessagePayload {\n meta: FormAdapterSubmissionMessageMeta\n data: FormAdapterSubmissionMessageData\n result: FormAdapterSubmissionMessageResult\n}\n\nexport interface FormAdapterSubmissionMessage\n extends FormAdapterSubmissionMessagePayload {\n messageId: string\n recordCreatedAt: Date\n}\n\nexport interface FormAdapterSubmissionService {\n handleFormSubmission: (\n submissionMessage: FormAdapterSubmissionMessage\n ) => unknown\n}\n"],"mappings":"AAsDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAyBA;AACA;AACA;AACA;;AAsGA,SACEA,UAAU,EACVC,YAAY;;AAmPd;AACA;AACA","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -58,7 +58,7 @@ export class StatusPageController extends QuestionPageController {
|
|
|
58
58
|
submissionGuidance,
|
|
59
59
|
formName,
|
|
60
60
|
showReferenceNumber: this.showReferenceNumber,
|
|
61
|
-
referenceNumber:
|
|
61
|
+
referenceNumber: confirmationState.referenceNumber
|
|
62
62
|
})
|
|
63
63
|
}
|
|
64
64
|
}
|
|
@@ -174,7 +174,8 @@ export class SummaryPageController extends QuestionPageController {
|
|
|
174
174
|
|
|
175
175
|
await cacheService.setConfirmationState(request, {
|
|
176
176
|
confirmed: true,
|
|
177
|
-
formId: context.state.formId
|
|
177
|
+
formId: context.state.formId,
|
|
178
|
+
referenceNumber: context.referenceNumber
|
|
178
179
|
} as FormConfirmationState)
|
|
179
180
|
|
|
180
181
|
// Clear all form data
|