@defra/forms-engine-plugin 5.0.0-alpha.5 → 5.0.0-alpha.6

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.
Files changed (43) hide show
  1. package/.server/server/constants.d.ts +1 -0
  2. package/.server/server/constants.js +1 -0
  3. package/.server/server/constants.js.map +1 -1
  4. package/.server/server/plugins/engine/components/ComponentBase.d.ts +1 -0
  5. package/.server/server/plugins/engine/components/ComponentBase.js +2 -0
  6. package/.server/server/plugins/engine/components/ComponentBase.js.map +1 -1
  7. package/.server/server/plugins/engine/components/ComponentCollection.js +2 -1
  8. package/.server/server/plugins/engine/components/ComponentCollection.js.map +1 -1
  9. package/.server/server/plugins/engine/components/DeclarationField.js +1 -1
  10. package/.server/server/plugins/engine/components/DeclarationField.js.map +1 -1
  11. package/.server/server/plugins/engine/components/FormComponent.js +2 -2
  12. package/.server/server/plugins/engine/components/FormComponent.js.map +1 -1
  13. package/.server/server/plugins/engine/components/Markdown.js +1 -1
  14. package/.server/server/plugins/engine/components/Markdown.js.map +1 -1
  15. package/.server/server/plugins/engine/i18n/types.d.ts +1 -0
  16. package/.server/server/plugins/engine/i18n/types.js.map +1 -1
  17. package/.server/server/plugins/engine/models/SummaryViewModel.js +7 -3
  18. package/.server/server/plugins/engine/models/SummaryViewModel.js.map +1 -1
  19. package/.server/server/plugins/engine/outputFormatters/adapter/v1.js +3 -5
  20. package/.server/server/plugins/engine/outputFormatters/adapter/v1.js.map +1 -1
  21. package/.server/server/plugins/engine/pageControllers/RepeatPageController.js +3 -4
  22. package/.server/server/plugins/engine/pageControllers/RepeatPageController.js.map +1 -1
  23. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +5 -2
  24. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
  25. package/.server/server/plugins/engine/services/notifyService.d.ts +1 -1
  26. package/.server/server/plugins/engine/services/notifyService.js +1 -1
  27. package/.server/server/plugins/engine/services/notifyService.js.map +1 -1
  28. package/.server/server/types.d.ts +1 -1
  29. package/.server/server/types.js.map +1 -1
  30. package/package.json +2 -2
  31. package/src/server/constants.js +1 -0
  32. package/src/server/plugins/engine/components/ComponentBase.ts +2 -0
  33. package/src/server/plugins/engine/components/ComponentCollection.ts +4 -5
  34. package/src/server/plugins/engine/components/DeclarationField.ts +1 -2
  35. package/src/server/plugins/engine/components/FormComponent.ts +2 -4
  36. package/src/server/plugins/engine/components/Markdown.ts +2 -3
  37. package/src/server/plugins/engine/i18n/types.ts +1 -0
  38. package/src/server/plugins/engine/models/SummaryViewModel.ts +4 -4
  39. package/src/server/plugins/engine/outputFormatters/adapter/v1.ts +3 -6
  40. package/src/server/plugins/engine/pageControllers/RepeatPageController.ts +2 -2
  41. package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +7 -1
  42. package/src/server/plugins/engine/services/notifyService.ts +1 -0
  43. package/src/server/types.ts +1 -0
@@ -326,11 +326,10 @@ export class RepeatPageController extends QuestionPageController {
326
326
  errors
327
327
  } = context;
328
328
  const {
329
- t
329
+ t,
330
+ tPage
330
331
  } = translator;
331
- const {
332
- title
333
- } = repeat.options;
332
+ const title = tPage(this.pageDef, 'repeatTitle') || repeat.options.title;
334
333
  const summaryList = {
335
334
  classes: 'govuk-summary-list--long-actions',
336
335
  rows: []
@@ -1 +1 @@
1
- {"version":3,"file":"RepeatPageController.js","names":["randomUUID","Boom","Joi","isRepeatState","redirectPath","QuestionPageController","FormAction","RepeatPageController","listSummaryViewName","listDeleteViewName","repeat","allowSaveAndExit","constructor","model","pageDef","options","schema","itemId","string","uuid","required","collection","formSchema","append","stateSchema","object","keys","name","array","items","min","max","label","title","getFormParams","request","params","payload","getFormDataFromState","state","list","getListFromState","getItemId","item","getItemFromList","getStateFromValidForm","badRequest","itemState","updated","newList","push","indexOf","proceed","h","nextPath","getSummaryPath","find","values","makeGetRouteHandler","context","path","query","summaryPath","returnUrl","force","length","makeGetListSummaryRouteHandler","translator","getTranslator","t","viewModel","getListSummaryViewModel","view","makePostListSummaryRouteHandler","action","hasErrorMin","Continue","hasErrorMax","AddAnother","count","errors","href","text","SaveAndExit","handleSaveAndExit","getNextPath","makeGetItemDeleteRouteHandler","notFound","backLink","getBackLink","pageTitle","itemTitle","buttonConfirm","buttonCancel","makePostItemDeleteRouteHandler","confirm","splice","update","mergeState","getViewModel","tPage","itemNumber","repeatCaption","sectionTitle","isForceAccess","summaryList","classes","rows","Array","isArray","forEach","index","getHref","visuallyHiddenText","itemDisplayText","fields","getDisplayStringFromState","key","value","actions","repeatTitle","showTitle","checkAnswers","shouldShowSaveAndExit","server"],"sources":["../../../../../src/server/plugins/engine/pageControllers/RepeatPageController.ts"],"sourcesContent":["import { randomUUID } from 'crypto'\n\nimport { type PageRepeat, type Repeat } from '@defra/forms-model'\nimport Boom from '@hapi/boom'\nimport Joi from 'joi'\n\nimport { isRepeatState } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { redirectPath } from '~/src/server/plugins/engine/helpers.js'\nimport { type Translator } from '~/src/server/plugins/engine/i18n/types.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'\nimport {\n type FormContext,\n type FormContextRequest,\n type FormPageViewModel,\n type FormPayload,\n type FormSubmissionState,\n type ItemDeletePageViewModel,\n type RepeatItemState,\n type RepeatListState,\n type RepeaterSummaryPageViewModel,\n type SummaryList,\n type SummaryListAction\n} from '~/src/server/plugins/engine/types.js'\nimport {\n FormAction,\n type FormRequest,\n type FormRequestPayload,\n type FormResponseToolkit\n} from '~/src/server/routes/types.js'\n\nexport class RepeatPageController extends QuestionPageController {\n declare pageDef: PageRepeat\n\n listSummaryViewName = 'repeat-list-summary'\n listDeleteViewName = 'item-delete'\n repeat: Repeat\n allowSaveAndExit = true\n\n constructor(model: FormModel, pageDef: PageRepeat) {\n super(model, pageDef)\n\n this.repeat = pageDef.repeat\n\n const { options, schema } = this.repeat\n const itemId = Joi.string().uuid().required()\n\n this.collection.formSchema = this.collection.formSchema.append({ itemId })\n this.collection.stateSchema = Joi.object<RepeatItemState>().keys({\n [options.name]: Joi.array()\n .items(this.collection.stateSchema.append({ itemId }))\n .min(schema.min)\n .max(schema.max)\n .label(`${options.title} list`)\n .required()\n })\n }\n\n get keys() {\n const { repeat } = this\n return [repeat.options.name, ...super.keys]\n }\n\n getFormParams(request?: FormContextRequest) {\n const params = super.getFormParams(request)\n\n // Apply an itemId to the form payload\n if (request?.payload) {\n params.itemId = request.params.itemId ?? randomUUID()\n }\n\n return params\n }\n\n getFormDataFromState(\n request: FormContextRequest | undefined,\n state: FormSubmissionState\n ) {\n const { repeat } = this\n\n const params = this.getFormParams(request)\n const list = this.getListFromState(state)\n const itemId = this.getItemId(request)\n\n // Create payload with repeater list state\n if (!itemId) {\n return {\n ...params,\n [repeat.options.name]: list\n }\n }\n\n // Create payload with repeater item state\n const item = this.getItemFromList(list, itemId)\n\n return {\n ...params,\n ...item\n }\n }\n\n getStateFromValidForm(\n request: FormContextRequest,\n state: FormSubmissionState,\n payload: FormPayload\n ) {\n const itemId = this.getItemId(request)\n\n if (!itemId) {\n throw Boom.badRequest('No item ID found')\n }\n\n const list = this.getListFromState(state)\n const item = this.getItemFromList(list, itemId)\n\n const itemState = super.getStateFromValidForm(request, state, payload)\n const updated: RepeatItemState = { ...itemState, itemId }\n const newList = [...list]\n\n if (!item) {\n // Adding a new item\n newList.push(updated)\n } else {\n // Update an existing item\n newList[list.indexOf(item)] = updated\n }\n\n return {\n [this.repeat.options.name]: newList\n }\n }\n\n proceed(request: FormContextRequest, h: FormResponseToolkit) {\n const nextPath = this.getSummaryPath(request)\n return super.proceed(request, h, nextPath)\n }\n\n getItemFromList(list: RepeatListState, itemId?: string) {\n return list.find((item) => item.itemId === itemId)\n }\n\n getListFromState(state: FormSubmissionState) {\n const { name } = this.repeat.options\n const values = state[name]\n\n return isRepeatState(values) ? values : []\n }\n\n makeGetRouteHandler() {\n return async (\n request: FormRequest,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { path } = this\n const { query } = request\n const { state } = context\n\n const itemId = this.getItemId(request)\n const list = this.getListFromState(state)\n\n if (!itemId) {\n const summaryPath = this.getSummaryPath(request)\n const nextPath = redirectPath(`${path}/${randomUUID()}`, {\n returnUrl: query.returnUrl,\n force: query.force\n })\n\n // Only redirect to new item when list is empty\n return super.proceed(request, h, list.length ? summaryPath : nextPath)\n }\n\n return super.makeGetRouteHandler()(request, context, h)\n }\n }\n\n makeGetListSummaryRouteHandler() {\n return (\n request: FormRequest,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { path } = this\n const { query } = request\n const { state } = context\n\n const list = this.getListFromState(state)\n\n if (!list.length) {\n const nextPath = redirectPath(`${path}/${randomUUID()}`, {\n returnUrl: query.returnUrl\n })\n\n return super.proceed(request, h, nextPath)\n }\n\n const translator = this.getTranslator(request)\n const { t } = translator\n\n const viewModel = this.getListSummaryViewModel(\n request,\n context,\n list,\n translator\n )\n\n return h.view(this.listSummaryViewName, {\n ...viewModel,\n t\n })\n }\n }\n\n makePostListSummaryRouteHandler() {\n return (\n request: FormRequestPayload,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { path, repeat } = this\n const { query } = request\n const { schema } = repeat\n const { state } = context\n\n const list = this.getListFromState(state)\n\n if (!list.length) {\n const nextPath = redirectPath(`${path}/${randomUUID()}`, {\n returnUrl: query.returnUrl\n })\n\n return super.proceed(request, h, nextPath)\n }\n\n const translator = this.getTranslator(request)\n const { t } = translator\n\n const { action } = this.getFormParams(request)\n\n const hasErrorMin =\n action === FormAction.Continue && list.length < schema.min\n\n const hasErrorMax =\n (action === FormAction.AddAnother && list.length >= schema.max) ||\n (action === FormAction.Continue && list.length > schema.max)\n\n // Show error if repeat limits apply\n if (hasErrorMin || hasErrorMax) {\n const count = hasErrorMax ? schema.max : schema.min\n\n context.errors = [\n {\n path: [],\n href: '',\n name: '',\n text: hasErrorMax\n ? t('pages.repeater.tooMany', { count })\n : t('pages.repeater.tooFew', { count })\n }\n ]\n\n const viewModel = this.getListSummaryViewModel(\n request,\n context,\n list,\n translator\n )\n\n return h.view(this.listSummaryViewName, {\n ...viewModel,\n t\n })\n }\n\n if (action === FormAction.AddAnother) {\n const nextPath = redirectPath(`${path}/${randomUUID()}`, {\n returnUrl: query.returnUrl\n })\n\n return super.proceed(request, h, nextPath)\n }\n\n // Check if this is a save-and-exit action\n if (action === FormAction.SaveAndExit) {\n return this.handleSaveAndExit(request, context, h)\n }\n\n const nextPath = this.getNextPath(context)\n return super.proceed(request, h, nextPath)\n }\n }\n\n makeGetItemDeleteRouteHandler() {\n return (\n request: FormRequest,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { viewModel } = this\n const { state } = context\n\n const list = this.getListFromState(state)\n const itemId = this.getItemId(request)\n const item = this.getItemFromList(list, itemId)\n\n const translator = this.getTranslator(request)\n const { t } = translator\n\n if (!item || list.length === 1) {\n throw Boom.notFound(\n item\n ? t('pages.repeater.lastItemCannotBeRemoved')\n : t('pages.repeater.itemToRemoveNotFound')\n )\n }\n\n const { title } = this.repeat.options\n\n return h.view(this.listDeleteViewName, {\n ...viewModel,\n context,\n backLink: this.getBackLink(request, context, t),\n pageTitle: t('pages.repeater.removeAnswer'),\n itemTitle: `${title} ${list.indexOf(item) + 1}`,\n buttonConfirm: { text: t('pages.repeater.remove') },\n buttonCancel: { text: t('pages.repeater.cancel') },\n t\n } satisfies ItemDeletePageViewModel)\n }\n }\n\n makePostItemDeleteRouteHandler() {\n return async (\n request: FormRequestPayload,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { repeat } = this\n const { state } = context\n\n const { confirm } = this.getFormParams(request)\n\n const list = this.getListFromState(state)\n const itemId = this.getItemId(request)\n const item = this.getItemFromList(list, itemId)\n\n const { t } = this.getTranslator(request)\n\n if (!item || list.length === 1) {\n throw Boom.notFound(\n item\n ? t('pages.repeater.lastItemCannotBeRemoved')\n : t('pages.repeater.itemToRemoveNotFound')\n )\n }\n\n // Remove the item from the list\n if (confirm) {\n list.splice(list.indexOf(item), 1)\n\n const update = {\n [repeat.options.name]: list\n }\n\n await this.mergeState(request, state, update)\n }\n\n return this.proceed(request, h)\n }\n }\n\n getViewModel(\n request: FormContextRequest,\n context: FormContext,\n translator: Translator\n ): FormPageViewModel {\n const { state } = context\n const { tPage } = translator\n\n const list = this.getListFromState(state)\n const itemId = this.getItemId(request)\n const item = this.getItemFromList(list, itemId)\n\n const viewModel = super.getViewModel(request, context, translator)\n const itemNumber = item ? list.indexOf(item) + 1 : list.length + 1\n const title =\n tPage(this.pageDef, 'repeatTitle') || this.pageDef.repeat.options.title\n const repeatCaption = `${title} ${itemNumber}`\n\n return {\n ...viewModel,\n\n sectionTitle: viewModel.sectionTitle\n ? `${viewModel.sectionTitle}: ${repeatCaption}`\n : repeatCaption\n }\n }\n\n getListSummaryViewModel(\n request: FormContextRequest,\n context: FormContext,\n list: RepeatListState,\n translator: Translator\n ): RepeaterSummaryPageViewModel {\n const { collection, href, repeat } = this\n const { query } = request\n const { isForceAccess, errors } = context\n\n const { t } = translator\n\n const { title } = repeat.options\n\n const summaryList: SummaryList = {\n classes: 'govuk-summary-list--long-actions',\n rows: []\n }\n\n let count = 0\n\n if (Array.isArray(list)) {\n count = list.length\n\n const summaryPath = this.getSummaryPath(request)\n\n list.forEach((item, index) => {\n const items: SummaryListAction[] = []\n\n // Remove summary list actions from previews\n if (!isForceAccess) {\n items.push({\n href: redirectPath(`${href}/${item.itemId}`, {\n returnUrl: query.returnUrl ?? this.getHref(summaryPath)\n }),\n text: t('pages.repeater.change'),\n classes: 'govuk-link--no-visited-state',\n visuallyHiddenText: t('pages.repeater.visuallyHiddenItem', {\n index: index + 1\n })\n })\n\n if (count > 1) {\n items.push({\n href: redirectPath(`${href}/${item.itemId}/confirm-delete`, {\n returnUrl: query.returnUrl\n }),\n text: t('pages.repeater.remove'),\n classes: 'govuk-link--no-visited-state',\n visuallyHiddenText: t('pages.repeater.visuallyHiddenItem', {\n index: index + 1\n })\n })\n }\n }\n\n const itemDisplayText = collection.fields.length\n ? collection.fields[0].getDisplayStringFromState(item, translator)\n : ''\n\n summaryList.rows.push({\n key: {\n text: `${title} ${index + 1}`\n },\n value: {\n text: itemDisplayText || t('pages.repeater.notProvided')\n },\n actions: {\n items\n }\n })\n })\n }\n\n return {\n ...this.viewModel,\n backLink: this.getBackLink(request, context, t),\n repeatTitle: title,\n pageTitle: t('pages.repeater.pageTitle', { count }),\n showTitle: true,\n context,\n errors,\n checkAnswers: [{ summaryList }],\n allowSaveAndExit: this.shouldShowSaveAndExit(request.server)\n }\n }\n\n getSummaryPath(request?: FormContextRequest) {\n const { path } = this\n\n const summaryPath = super.getSummaryPath()\n\n if (!request) {\n return summaryPath\n }\n\n const { query } = request\n\n return redirectPath(`${path}${summaryPath}`, {\n returnUrl: query.returnUrl\n })\n }\n}\n"],"mappings":"AAAA,SAASA,UAAU,QAAQ,QAAQ;AAGnC,OAAOC,IAAI,MAAM,YAAY;AAC7B,OAAOC,GAAG,MAAM,KAAK;AAErB,SAASC,aAAa;AACtB,SAASC,YAAY;AAGrB,SAASC,sBAAsB;AAc/B,SACEC,UAAU;AAMZ,OAAO,MAAMC,oBAAoB,SAASF,sBAAsB,CAAC;EAG/DG,mBAAmB,GAAG,qBAAqB;EAC3CC,kBAAkB,GAAG,aAAa;EAClCC,MAAM;EACNC,gBAAgB,GAAG,IAAI;EAEvBC,WAAWA,CAACC,KAAgB,EAAEC,OAAmB,EAAE;IACjD,KAAK,CAACD,KAAK,EAAEC,OAAO,CAAC;IAErB,IAAI,CAACJ,MAAM,GAAGI,OAAO,CAACJ,MAAM;IAE5B,MAAM;MAAEK,OAAO;MAAEC;IAAO,CAAC,GAAG,IAAI,CAACN,MAAM;IACvC,MAAMO,MAAM,GAAGf,GAAG,CAACgB,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC,CAAC,CAACC,QAAQ,CAAC,CAAC;IAE7C,IAAI,CAACC,UAAU,CAACC,UAAU,GAAG,IAAI,CAACD,UAAU,CAACC,UAAU,CAACC,MAAM,CAAC;MAAEN;IAAO,CAAC,CAAC;IAC1E,IAAI,CAACI,UAAU,CAACG,WAAW,GAAGtB,GAAG,CAACuB,MAAM,CAAkB,CAAC,CAACC,IAAI,CAAC;MAC/D,CAACX,OAAO,CAACY,IAAI,GAAGzB,GAAG,CAAC0B,KAAK,CAAC,CAAC,CACxBC,KAAK,CAAC,IAAI,CAACR,UAAU,CAACG,WAAW,CAACD,MAAM,CAAC;QAAEN;MAAO,CAAC,CAAC,CAAC,CACrDa,GAAG,CAACd,MAAM,CAACc,GAAG,CAAC,CACfC,GAAG,CAACf,MAAM,CAACe,GAAG,CAAC,CACfC,KAAK,CAAC,GAAGjB,OAAO,CAACkB,KAAK,OAAO,CAAC,CAC9Bb,QAAQ,CAAC;IACd,CAAC,CAAC;EACJ;EAEA,IAAIM,IAAIA,CAAA,EAAG;IACT,MAAM;MAAEhB;IAAO,CAAC,GAAG,IAAI;IACvB,OAAO,CAACA,MAAM,CAACK,OAAO,CAACY,IAAI,EAAE,GAAG,KAAK,CAACD,IAAI,CAAC;EAC7C;EAEAQ,aAAaA,CAACC,OAA4B,EAAE;IAC1C,MAAMC,MAAM,GAAG,KAAK,CAACF,aAAa,CAACC,OAAO,CAAC;;IAE3C;IACA,IAAIA,OAAO,EAAEE,OAAO,EAAE;MACpBD,MAAM,CAACnB,MAAM,GAAGkB,OAAO,CAACC,MAAM,CAACnB,MAAM,IAAIjB,UAAU,CAAC,CAAC;IACvD;IAEA,OAAOoC,MAAM;EACf;EAEAE,oBAAoBA,CAClBH,OAAuC,EACvCI,KAA0B,EAC1B;IACA,MAAM;MAAE7B;IAAO,CAAC,GAAG,IAAI;IAEvB,MAAM0B,MAAM,GAAG,IAAI,CAACF,aAAa,CAACC,OAAO,CAAC;IAC1C,MAAMK,IAAI,GAAG,IAAI,CAACC,gBAAgB,CAACF,KAAK,CAAC;IACzC,MAAMtB,MAAM,GAAG,IAAI,CAACyB,SAAS,CAACP,OAAO,CAAC;;IAEtC;IACA,IAAI,CAAClB,MAAM,EAAE;MACX,OAAO;QACL,GAAGmB,MAAM;QACT,CAAC1B,MAAM,CAACK,OAAO,CAACY,IAAI,GAAGa;MACzB,CAAC;IACH;;IAEA;IACA,MAAMG,IAAI,GAAG,IAAI,CAACC,eAAe,CAACJ,IAAI,EAAEvB,MAAM,CAAC;IAE/C,OAAO;MACL,GAAGmB,MAAM;MACT,GAAGO;IACL,CAAC;EACH;EAEAE,qBAAqBA,CACnBV,OAA2B,EAC3BI,KAA0B,EAC1BF,OAAoB,EACpB;IACA,MAAMpB,MAAM,GAAG,IAAI,CAACyB,SAAS,CAACP,OAAO,CAAC;IAEtC,IAAI,CAAClB,MAAM,EAAE;MACX,MAAMhB,IAAI,CAAC6C,UAAU,CAAC,kBAAkB,CAAC;IAC3C;IAEA,MAAMN,IAAI,GAAG,IAAI,CAACC,gBAAgB,CAACF,KAAK,CAAC;IACzC,MAAMI,IAAI,GAAG,IAAI,CAACC,eAAe,CAACJ,IAAI,EAAEvB,MAAM,CAAC;IAE/C,MAAM8B,SAAS,GAAG,KAAK,CAACF,qBAAqB,CAACV,OAAO,EAAEI,KAAK,EAAEF,OAAO,CAAC;IACtE,MAAMW,OAAwB,GAAG;MAAE,GAAGD,SAAS;MAAE9B;IAAO,CAAC;IACzD,MAAMgC,OAAO,GAAG,CAAC,GAAGT,IAAI,CAAC;IAEzB,IAAI,CAACG,IAAI,EAAE;MACT;MACAM,OAAO,CAACC,IAAI,CAACF,OAAO,CAAC;IACvB,CAAC,MAAM;MACL;MACAC,OAAO,CAACT,IAAI,CAACW,OAAO,CAACR,IAAI,CAAC,CAAC,GAAGK,OAAO;IACvC;IAEA,OAAO;MACL,CAAC,IAAI,CAACtC,MAAM,CAACK,OAAO,CAACY,IAAI,GAAGsB;IAC9B,CAAC;EACH;EAEAG,OAAOA,CAACjB,OAA2B,EAAEkB,CAAsB,EAAE;IAC3D,MAAMC,QAAQ,GAAG,IAAI,CAACC,cAAc,CAACpB,OAAO,CAAC;IAC7C,OAAO,KAAK,CAACiB,OAAO,CAACjB,OAAO,EAAEkB,CAAC,EAAEC,QAAQ,CAAC;EAC5C;EAEAV,eAAeA,CAACJ,IAAqB,EAAEvB,MAAe,EAAE;IACtD,OAAOuB,IAAI,CAACgB,IAAI,CAAEb,IAAI,IAAKA,IAAI,CAAC1B,MAAM,KAAKA,MAAM,CAAC;EACpD;EAEAwB,gBAAgBA,CAACF,KAA0B,EAAE;IAC3C,MAAM;MAAEZ;IAAK,CAAC,GAAG,IAAI,CAACjB,MAAM,CAACK,OAAO;IACpC,MAAM0C,MAAM,GAAGlB,KAAK,CAACZ,IAAI,CAAC;IAE1B,OAAOxB,aAAa,CAACsD,MAAM,CAAC,GAAGA,MAAM,GAAG,EAAE;EAC5C;EAEAC,mBAAmBA,CAAA,EAAG;IACpB,OAAO,OACLvB,OAAoB,EACpBwB,OAAoB,EACpBN,CAAsB,KACnB;MACH,MAAM;QAAEO;MAAK,CAAC,GAAG,IAAI;MACrB,MAAM;QAAEC;MAAM,CAAC,GAAG1B,OAAO;MACzB,MAAM;QAAEI;MAAM,CAAC,GAAGoB,OAAO;MAEzB,MAAM1C,MAAM,GAAG,IAAI,CAACyB,SAAS,CAACP,OAAO,CAAC;MACtC,MAAMK,IAAI,GAAG,IAAI,CAACC,gBAAgB,CAACF,KAAK,CAAC;MAEzC,IAAI,CAACtB,MAAM,EAAE;QACX,MAAM6C,WAAW,GAAG,IAAI,CAACP,cAAc,CAACpB,OAAO,CAAC;QAChD,MAAMmB,QAAQ,GAAGlD,YAAY,CAAC,GAAGwD,IAAI,IAAI5D,UAAU,CAAC,CAAC,EAAE,EAAE;UACvD+D,SAAS,EAAEF,KAAK,CAACE,SAAS;UAC1BC,KAAK,EAAEH,KAAK,CAACG;QACf,CAAC,CAAC;;QAEF;QACA,OAAO,KAAK,CAACZ,OAAO,CAACjB,OAAO,EAAEkB,CAAC,EAAEb,IAAI,CAACyB,MAAM,GAAGH,WAAW,GAAGR,QAAQ,CAAC;MACxE;MAEA,OAAO,KAAK,CAACI,mBAAmB,CAAC,CAAC,CAACvB,OAAO,EAAEwB,OAAO,EAAEN,CAAC,CAAC;IACzD,CAAC;EACH;EAEAa,8BAA8BA,CAAA,EAAG;IAC/B,OAAO,CACL/B,OAAoB,EACpBwB,OAAoB,EACpBN,CAAsB,KACnB;MACH,MAAM;QAAEO;MAAK,CAAC,GAAG,IAAI;MACrB,MAAM;QAAEC;MAAM,CAAC,GAAG1B,OAAO;MACzB,MAAM;QAAEI;MAAM,CAAC,GAAGoB,OAAO;MAEzB,MAAMnB,IAAI,GAAG,IAAI,CAACC,gBAAgB,CAACF,KAAK,CAAC;MAEzC,IAAI,CAACC,IAAI,CAACyB,MAAM,EAAE;QAChB,MAAMX,QAAQ,GAAGlD,YAAY,CAAC,GAAGwD,IAAI,IAAI5D,UAAU,CAAC,CAAC,EAAE,EAAE;UACvD+D,SAAS,EAAEF,KAAK,CAACE;QACnB,CAAC,CAAC;QAEF,OAAO,KAAK,CAACX,OAAO,CAACjB,OAAO,EAAEkB,CAAC,EAAEC,QAAQ,CAAC;MAC5C;MAEA,MAAMa,UAAU,GAAG,IAAI,CAACC,aAAa,CAACjC,OAAO,CAAC;MAC9C,MAAM;QAAEkC;MAAE,CAAC,GAAGF,UAAU;MAExB,MAAMG,SAAS,GAAG,IAAI,CAACC,uBAAuB,CAC5CpC,OAAO,EACPwB,OAAO,EACPnB,IAAI,EACJ2B,UACF,CAAC;MAED,OAAOd,CAAC,CAACmB,IAAI,CAAC,IAAI,CAAChE,mBAAmB,EAAE;QACtC,GAAG8D,SAAS;QACZD;MACF,CAAC,CAAC;IACJ,CAAC;EACH;EAEAI,+BAA+BA,CAAA,EAAG;IAChC,OAAO,CACLtC,OAA2B,EAC3BwB,OAAoB,EACpBN,CAAsB,KACnB;MACH,MAAM;QAAEO,IAAI;QAAElD;MAAO,CAAC,GAAG,IAAI;MAC7B,MAAM;QAAEmD;MAAM,CAAC,GAAG1B,OAAO;MACzB,MAAM;QAAEnB;MAAO,CAAC,GAAGN,MAAM;MACzB,MAAM;QAAE6B;MAAM,CAAC,GAAGoB,OAAO;MAEzB,MAAMnB,IAAI,GAAG,IAAI,CAACC,gBAAgB,CAACF,KAAK,CAAC;MAEzC,IAAI,CAACC,IAAI,CAACyB,MAAM,EAAE;QAChB,MAAMX,QAAQ,GAAGlD,YAAY,CAAC,GAAGwD,IAAI,IAAI5D,UAAU,CAAC,CAAC,EAAE,EAAE;UACvD+D,SAAS,EAAEF,KAAK,CAACE;QACnB,CAAC,CAAC;QAEF,OAAO,KAAK,CAACX,OAAO,CAACjB,OAAO,EAAEkB,CAAC,EAAEC,QAAQ,CAAC;MAC5C;MAEA,MAAMa,UAAU,GAAG,IAAI,CAACC,aAAa,CAACjC,OAAO,CAAC;MAC9C,MAAM;QAAEkC;MAAE,CAAC,GAAGF,UAAU;MAExB,MAAM;QAAEO;MAAO,CAAC,GAAG,IAAI,CAACxC,aAAa,CAACC,OAAO,CAAC;MAE9C,MAAMwC,WAAW,GACfD,MAAM,KAAKpE,UAAU,CAACsE,QAAQ,IAAIpC,IAAI,CAACyB,MAAM,GAAGjD,MAAM,CAACc,GAAG;MAE5D,MAAM+C,WAAW,GACdH,MAAM,KAAKpE,UAAU,CAACwE,UAAU,IAAItC,IAAI,CAACyB,MAAM,IAAIjD,MAAM,CAACe,GAAG,IAC7D2C,MAAM,KAAKpE,UAAU,CAACsE,QAAQ,IAAIpC,IAAI,CAACyB,MAAM,GAAGjD,MAAM,CAACe,GAAI;;MAE9D;MACA,IAAI4C,WAAW,IAAIE,WAAW,EAAE;QAC9B,MAAME,KAAK,GAAGF,WAAW,GAAG7D,MAAM,CAACe,GAAG,GAAGf,MAAM,CAACc,GAAG;QAEnD6B,OAAO,CAACqB,MAAM,GAAG,CACf;UACEpB,IAAI,EAAE,EAAE;UACRqB,IAAI,EAAE,EAAE;UACRtD,IAAI,EAAE,EAAE;UACRuD,IAAI,EAAEL,WAAW,GACbR,CAAC,CAAC,wBAAwB,EAAE;YAAEU;UAAM,CAAC,CAAC,GACtCV,CAAC,CAAC,uBAAuB,EAAE;YAAEU;UAAM,CAAC;QAC1C,CAAC,CACF;QAED,MAAMT,SAAS,GAAG,IAAI,CAACC,uBAAuB,CAC5CpC,OAAO,EACPwB,OAAO,EACPnB,IAAI,EACJ2B,UACF,CAAC;QAED,OAAOd,CAAC,CAACmB,IAAI,CAAC,IAAI,CAAChE,mBAAmB,EAAE;UACtC,GAAG8D,SAAS;UACZD;QACF,CAAC,CAAC;MACJ;MAEA,IAAIK,MAAM,KAAKpE,UAAU,CAACwE,UAAU,EAAE;QACpC,MAAMxB,QAAQ,GAAGlD,YAAY,CAAC,GAAGwD,IAAI,IAAI5D,UAAU,CAAC,CAAC,EAAE,EAAE;UACvD+D,SAAS,EAAEF,KAAK,CAACE;QACnB,CAAC,CAAC;QAEF,OAAO,KAAK,CAACX,OAAO,CAACjB,OAAO,EAAEkB,CAAC,EAAEC,QAAQ,CAAC;MAC5C;;MAEA;MACA,IAAIoB,MAAM,KAAKpE,UAAU,CAAC6E,WAAW,EAAE;QACrC,OAAO,IAAI,CAACC,iBAAiB,CAACjD,OAAO,EAAEwB,OAAO,EAAEN,CAAC,CAAC;MACpD;MAEA,MAAMC,QAAQ,GAAG,IAAI,CAAC+B,WAAW,CAAC1B,OAAO,CAAC;MAC1C,OAAO,KAAK,CAACP,OAAO,CAACjB,OAAO,EAAEkB,CAAC,EAAEC,QAAQ,CAAC;IAC5C,CAAC;EACH;EAEAgC,6BAA6BA,CAAA,EAAG;IAC9B,OAAO,CACLnD,OAAoB,EACpBwB,OAAoB,EACpBN,CAAsB,KACnB;MACH,MAAM;QAAEiB;MAAU,CAAC,GAAG,IAAI;MAC1B,MAAM;QAAE/B;MAAM,CAAC,GAAGoB,OAAO;MAEzB,MAAMnB,IAAI,GAAG,IAAI,CAACC,gBAAgB,CAACF,KAAK,CAAC;MACzC,MAAMtB,MAAM,GAAG,IAAI,CAACyB,SAAS,CAACP,OAAO,CAAC;MACtC,MAAMQ,IAAI,GAAG,IAAI,CAACC,eAAe,CAACJ,IAAI,EAAEvB,MAAM,CAAC;MAE/C,MAAMkD,UAAU,GAAG,IAAI,CAACC,aAAa,CAACjC,OAAO,CAAC;MAC9C,MAAM;QAAEkC;MAAE,CAAC,GAAGF,UAAU;MAExB,IAAI,CAACxB,IAAI,IAAIH,IAAI,CAACyB,MAAM,KAAK,CAAC,EAAE;QAC9B,MAAMhE,IAAI,CAACsF,QAAQ,CACjB5C,IAAI,GACA0B,CAAC,CAAC,wCAAwC,CAAC,GAC3CA,CAAC,CAAC,qCAAqC,CAC7C,CAAC;MACH;MAEA,MAAM;QAAEpC;MAAM,CAAC,GAAG,IAAI,CAACvB,MAAM,CAACK,OAAO;MAErC,OAAOsC,CAAC,CAACmB,IAAI,CAAC,IAAI,CAAC/D,kBAAkB,EAAE;QACrC,GAAG6D,SAAS;QACZX,OAAO;QACP6B,QAAQ,EAAE,IAAI,CAACC,WAAW,CAACtD,OAAO,EAAEwB,OAAO,EAAEU,CAAC,CAAC;QAC/CqB,SAAS,EAAErB,CAAC,CAAC,6BAA6B,CAAC;QAC3CsB,SAAS,EAAE,GAAG1D,KAAK,IAAIO,IAAI,CAACW,OAAO,CAACR,IAAI,CAAC,GAAG,CAAC,EAAE;QAC/CiD,aAAa,EAAE;UAAEV,IAAI,EAAEb,CAAC,CAAC,uBAAuB;QAAE,CAAC;QACnDwB,YAAY,EAAE;UAAEX,IAAI,EAAEb,CAAC,CAAC,uBAAuB;QAAE,CAAC;QAClDA;MACF,CAAmC,CAAC;IACtC,CAAC;EACH;EAEAyB,8BAA8BA,CAAA,EAAG;IAC/B,OAAO,OACL3D,OAA2B,EAC3BwB,OAAoB,EACpBN,CAAsB,KACnB;MACH,MAAM;QAAE3C;MAAO,CAAC,GAAG,IAAI;MACvB,MAAM;QAAE6B;MAAM,CAAC,GAAGoB,OAAO;MAEzB,MAAM;QAAEoC;MAAQ,CAAC,GAAG,IAAI,CAAC7D,aAAa,CAACC,OAAO,CAAC;MAE/C,MAAMK,IAAI,GAAG,IAAI,CAACC,gBAAgB,CAACF,KAAK,CAAC;MACzC,MAAMtB,MAAM,GAAG,IAAI,CAACyB,SAAS,CAACP,OAAO,CAAC;MACtC,MAAMQ,IAAI,GAAG,IAAI,CAACC,eAAe,CAACJ,IAAI,EAAEvB,MAAM,CAAC;MAE/C,MAAM;QAAEoD;MAAE,CAAC,GAAG,IAAI,CAACD,aAAa,CAACjC,OAAO,CAAC;MAEzC,IAAI,CAACQ,IAAI,IAAIH,IAAI,CAACyB,MAAM,KAAK,CAAC,EAAE;QAC9B,MAAMhE,IAAI,CAACsF,QAAQ,CACjB5C,IAAI,GACA0B,CAAC,CAAC,wCAAwC,CAAC,GAC3CA,CAAC,CAAC,qCAAqC,CAC7C,CAAC;MACH;;MAEA;MACA,IAAI0B,OAAO,EAAE;QACXvD,IAAI,CAACwD,MAAM,CAACxD,IAAI,CAACW,OAAO,CAACR,IAAI,CAAC,EAAE,CAAC,CAAC;QAElC,MAAMsD,MAAM,GAAG;UACb,CAACvF,MAAM,CAACK,OAAO,CAACY,IAAI,GAAGa;QACzB,CAAC;QAED,MAAM,IAAI,CAAC0D,UAAU,CAAC/D,OAAO,EAAEI,KAAK,EAAE0D,MAAM,CAAC;MAC/C;MAEA,OAAO,IAAI,CAAC7C,OAAO,CAACjB,OAAO,EAAEkB,CAAC,CAAC;IACjC,CAAC;EACH;EAEA8C,YAAYA,CACVhE,OAA2B,EAC3BwB,OAAoB,EACpBQ,UAAsB,EACH;IACnB,MAAM;MAAE5B;IAAM,CAAC,GAAGoB,OAAO;IACzB,MAAM;MAAEyC;IAAM,CAAC,GAAGjC,UAAU;IAE5B,MAAM3B,IAAI,GAAG,IAAI,CAACC,gBAAgB,CAACF,KAAK,CAAC;IACzC,MAAMtB,MAAM,GAAG,IAAI,CAACyB,SAAS,CAACP,OAAO,CAAC;IACtC,MAAMQ,IAAI,GAAG,IAAI,CAACC,eAAe,CAACJ,IAAI,EAAEvB,MAAM,CAAC;IAE/C,MAAMqD,SAAS,GAAG,KAAK,CAAC6B,YAAY,CAAChE,OAAO,EAAEwB,OAAO,EAAEQ,UAAU,CAAC;IAClE,MAAMkC,UAAU,GAAG1D,IAAI,GAAGH,IAAI,CAACW,OAAO,CAACR,IAAI,CAAC,GAAG,CAAC,GAAGH,IAAI,CAACyB,MAAM,GAAG,CAAC;IAClE,MAAMhC,KAAK,GACTmE,KAAK,CAAC,IAAI,CAACtF,OAAO,EAAE,aAAa,CAAC,IAAI,IAAI,CAACA,OAAO,CAACJ,MAAM,CAACK,OAAO,CAACkB,KAAK;IACzE,MAAMqE,aAAa,GAAG,GAAGrE,KAAK,IAAIoE,UAAU,EAAE;IAE9C,OAAO;MACL,GAAG/B,SAAS;MAEZiC,YAAY,EAAEjC,SAAS,CAACiC,YAAY,GAChC,GAAGjC,SAAS,CAACiC,YAAY,KAAKD,aAAa,EAAE,GAC7CA;IACN,CAAC;EACH;EAEA/B,uBAAuBA,CACrBpC,OAA2B,EAC3BwB,OAAoB,EACpBnB,IAAqB,EACrB2B,UAAsB,EACQ;IAC9B,MAAM;MAAE9C,UAAU;MAAE4D,IAAI;MAAEvE;IAAO,CAAC,GAAG,IAAI;IACzC,MAAM;MAAEmD;IAAM,CAAC,GAAG1B,OAAO;IACzB,MAAM;MAAEqE,aAAa;MAAExB;IAAO,CAAC,GAAGrB,OAAO;IAEzC,MAAM;MAAEU;IAAE,CAAC,GAAGF,UAAU;IAExB,MAAM;MAAElC;IAAM,CAAC,GAAGvB,MAAM,CAACK,OAAO;IAEhC,MAAM0F,WAAwB,GAAG;MAC/BC,OAAO,EAAE,kCAAkC;MAC3CC,IAAI,EAAE;IACR,CAAC;IAED,IAAI5B,KAAK,GAAG,CAAC;IAEb,IAAI6B,KAAK,CAACC,OAAO,CAACrE,IAAI,CAAC,EAAE;MACvBuC,KAAK,GAAGvC,IAAI,CAACyB,MAAM;MAEnB,MAAMH,WAAW,GAAG,IAAI,CAACP,cAAc,CAACpB,OAAO,CAAC;MAEhDK,IAAI,CAACsE,OAAO,CAAC,CAACnE,IAAI,EAAEoE,KAAK,KAAK;QAC5B,MAAMlF,KAA0B,GAAG,EAAE;;QAErC;QACA,IAAI,CAAC2E,aAAa,EAAE;UAClB3E,KAAK,CAACqB,IAAI,CAAC;YACT+B,IAAI,EAAE7E,YAAY,CAAC,GAAG6E,IAAI,IAAItC,IAAI,CAAC1B,MAAM,EAAE,EAAE;cAC3C8C,SAAS,EAAEF,KAAK,CAACE,SAAS,IAAI,IAAI,CAACiD,OAAO,CAAClD,WAAW;YACxD,CAAC,CAAC;YACFoB,IAAI,EAAEb,CAAC,CAAC,uBAAuB,CAAC;YAChCqC,OAAO,EAAE,8BAA8B;YACvCO,kBAAkB,EAAE5C,CAAC,CAAC,mCAAmC,EAAE;cACzD0C,KAAK,EAAEA,KAAK,GAAG;YACjB,CAAC;UACH,CAAC,CAAC;UAEF,IAAIhC,KAAK,GAAG,CAAC,EAAE;YACblD,KAAK,CAACqB,IAAI,CAAC;cACT+B,IAAI,EAAE7E,YAAY,CAAC,GAAG6E,IAAI,IAAItC,IAAI,CAAC1B,MAAM,iBAAiB,EAAE;gBAC1D8C,SAAS,EAAEF,KAAK,CAACE;cACnB,CAAC,CAAC;cACFmB,IAAI,EAAEb,CAAC,CAAC,uBAAuB,CAAC;cAChCqC,OAAO,EAAE,8BAA8B;cACvCO,kBAAkB,EAAE5C,CAAC,CAAC,mCAAmC,EAAE;gBACzD0C,KAAK,EAAEA,KAAK,GAAG;cACjB,CAAC;YACH,CAAC,CAAC;UACJ;QACF;QAEA,MAAMG,eAAe,GAAG7F,UAAU,CAAC8F,MAAM,CAAClD,MAAM,GAC5C5C,UAAU,CAAC8F,MAAM,CAAC,CAAC,CAAC,CAACC,yBAAyB,CAACzE,IAAI,EAAEwB,UAAU,CAAC,GAChE,EAAE;QAENsC,WAAW,CAACE,IAAI,CAACzD,IAAI,CAAC;UACpBmE,GAAG,EAAE;YACHnC,IAAI,EAAE,GAAGjD,KAAK,IAAI8E,KAAK,GAAG,CAAC;UAC7B,CAAC;UACDO,KAAK,EAAE;YACLpC,IAAI,EAAEgC,eAAe,IAAI7C,CAAC,CAAC,4BAA4B;UACzD,CAAC;UACDkD,OAAO,EAAE;YACP1F;UACF;QACF,CAAC,CAAC;MACJ,CAAC,CAAC;IACJ;IAEA,OAAO;MACL,GAAG,IAAI,CAACyC,SAAS;MACjBkB,QAAQ,EAAE,IAAI,CAACC,WAAW,CAACtD,OAAO,EAAEwB,OAAO,EAAEU,CAAC,CAAC;MAC/CmD,WAAW,EAAEvF,KAAK;MAClByD,SAAS,EAAErB,CAAC,CAAC,0BAA0B,EAAE;QAAEU;MAAM,CAAC,CAAC;MACnD0C,SAAS,EAAE,IAAI;MACf9D,OAAO;MACPqB,MAAM;MACN0C,YAAY,EAAE,CAAC;QAAEjB;MAAY,CAAC,CAAC;MAC/B9F,gBAAgB,EAAE,IAAI,CAACgH,qBAAqB,CAACxF,OAAO,CAACyF,MAAM;IAC7D,CAAC;EACH;EAEArE,cAAcA,CAACpB,OAA4B,EAAE;IAC3C,MAAM;MAAEyB;IAAK,CAAC,GAAG,IAAI;IAErB,MAAME,WAAW,GAAG,KAAK,CAACP,cAAc,CAAC,CAAC;IAE1C,IAAI,CAACpB,OAAO,EAAE;MACZ,OAAO2B,WAAW;IACpB;IAEA,MAAM;MAAED;IAAM,CAAC,GAAG1B,OAAO;IAEzB,OAAO/B,YAAY,CAAC,GAAGwD,IAAI,GAAGE,WAAW,EAAE,EAAE;MAC3CC,SAAS,EAAEF,KAAK,CAACE;IACnB,CAAC,CAAC;EACJ;AACF","ignoreList":[]}
1
+ {"version":3,"file":"RepeatPageController.js","names":["randomUUID","Boom","Joi","isRepeatState","redirectPath","QuestionPageController","FormAction","RepeatPageController","listSummaryViewName","listDeleteViewName","repeat","allowSaveAndExit","constructor","model","pageDef","options","schema","itemId","string","uuid","required","collection","formSchema","append","stateSchema","object","keys","name","array","items","min","max","label","title","getFormParams","request","params","payload","getFormDataFromState","state","list","getListFromState","getItemId","item","getItemFromList","getStateFromValidForm","badRequest","itemState","updated","newList","push","indexOf","proceed","h","nextPath","getSummaryPath","find","values","makeGetRouteHandler","context","path","query","summaryPath","returnUrl","force","length","makeGetListSummaryRouteHandler","translator","getTranslator","t","viewModel","getListSummaryViewModel","view","makePostListSummaryRouteHandler","action","hasErrorMin","Continue","hasErrorMax","AddAnother","count","errors","href","text","SaveAndExit","handleSaveAndExit","getNextPath","makeGetItemDeleteRouteHandler","notFound","backLink","getBackLink","pageTitle","itemTitle","buttonConfirm","buttonCancel","makePostItemDeleteRouteHandler","confirm","splice","update","mergeState","getViewModel","tPage","itemNumber","repeatCaption","sectionTitle","isForceAccess","summaryList","classes","rows","Array","isArray","forEach","index","getHref","visuallyHiddenText","itemDisplayText","fields","getDisplayStringFromState","key","value","actions","repeatTitle","showTitle","checkAnswers","shouldShowSaveAndExit","server"],"sources":["../../../../../src/server/plugins/engine/pageControllers/RepeatPageController.ts"],"sourcesContent":["import { randomUUID } from 'crypto'\n\nimport { type PageRepeat, type Repeat } from '@defra/forms-model'\nimport Boom from '@hapi/boom'\nimport Joi from 'joi'\n\nimport { isRepeatState } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { redirectPath } from '~/src/server/plugins/engine/helpers.js'\nimport { type Translator } from '~/src/server/plugins/engine/i18n/types.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'\nimport {\n type FormContext,\n type FormContextRequest,\n type FormPageViewModel,\n type FormPayload,\n type FormSubmissionState,\n type ItemDeletePageViewModel,\n type RepeatItemState,\n type RepeatListState,\n type RepeaterSummaryPageViewModel,\n type SummaryList,\n type SummaryListAction\n} from '~/src/server/plugins/engine/types.js'\nimport {\n FormAction,\n type FormRequest,\n type FormRequestPayload,\n type FormResponseToolkit\n} from '~/src/server/routes/types.js'\n\nexport class RepeatPageController extends QuestionPageController {\n declare pageDef: PageRepeat\n\n listSummaryViewName = 'repeat-list-summary'\n listDeleteViewName = 'item-delete'\n repeat: Repeat\n allowSaveAndExit = true\n\n constructor(model: FormModel, pageDef: PageRepeat) {\n super(model, pageDef)\n\n this.repeat = pageDef.repeat\n\n const { options, schema } = this.repeat\n const itemId = Joi.string().uuid().required()\n\n this.collection.formSchema = this.collection.formSchema.append({ itemId })\n this.collection.stateSchema = Joi.object<RepeatItemState>().keys({\n [options.name]: Joi.array()\n .items(this.collection.stateSchema.append({ itemId }))\n .min(schema.min)\n .max(schema.max)\n .label(`${options.title} list`)\n .required()\n })\n }\n\n get keys() {\n const { repeat } = this\n return [repeat.options.name, ...super.keys]\n }\n\n getFormParams(request?: FormContextRequest) {\n const params = super.getFormParams(request)\n\n // Apply an itemId to the form payload\n if (request?.payload) {\n params.itemId = request.params.itemId ?? randomUUID()\n }\n\n return params\n }\n\n getFormDataFromState(\n request: FormContextRequest | undefined,\n state: FormSubmissionState\n ) {\n const { repeat } = this\n\n const params = this.getFormParams(request)\n const list = this.getListFromState(state)\n const itemId = this.getItemId(request)\n\n // Create payload with repeater list state\n if (!itemId) {\n return {\n ...params,\n [repeat.options.name]: list\n }\n }\n\n // Create payload with repeater item state\n const item = this.getItemFromList(list, itemId)\n\n return {\n ...params,\n ...item\n }\n }\n\n getStateFromValidForm(\n request: FormContextRequest,\n state: FormSubmissionState,\n payload: FormPayload\n ) {\n const itemId = this.getItemId(request)\n\n if (!itemId) {\n throw Boom.badRequest('No item ID found')\n }\n\n const list = this.getListFromState(state)\n const item = this.getItemFromList(list, itemId)\n\n const itemState = super.getStateFromValidForm(request, state, payload)\n const updated: RepeatItemState = { ...itemState, itemId }\n const newList = [...list]\n\n if (!item) {\n // Adding a new item\n newList.push(updated)\n } else {\n // Update an existing item\n newList[list.indexOf(item)] = updated\n }\n\n return {\n [this.repeat.options.name]: newList\n }\n }\n\n proceed(request: FormContextRequest, h: FormResponseToolkit) {\n const nextPath = this.getSummaryPath(request)\n return super.proceed(request, h, nextPath)\n }\n\n getItemFromList(list: RepeatListState, itemId?: string) {\n return list.find((item) => item.itemId === itemId)\n }\n\n getListFromState(state: FormSubmissionState) {\n const { name } = this.repeat.options\n const values = state[name]\n\n return isRepeatState(values) ? values : []\n }\n\n makeGetRouteHandler() {\n return async (\n request: FormRequest,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { path } = this\n const { query } = request\n const { state } = context\n\n const itemId = this.getItemId(request)\n const list = this.getListFromState(state)\n\n if (!itemId) {\n const summaryPath = this.getSummaryPath(request)\n const nextPath = redirectPath(`${path}/${randomUUID()}`, {\n returnUrl: query.returnUrl,\n force: query.force\n })\n\n // Only redirect to new item when list is empty\n return super.proceed(request, h, list.length ? summaryPath : nextPath)\n }\n\n return super.makeGetRouteHandler()(request, context, h)\n }\n }\n\n makeGetListSummaryRouteHandler() {\n return (\n request: FormRequest,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { path } = this\n const { query } = request\n const { state } = context\n\n const list = this.getListFromState(state)\n\n if (!list.length) {\n const nextPath = redirectPath(`${path}/${randomUUID()}`, {\n returnUrl: query.returnUrl\n })\n\n return super.proceed(request, h, nextPath)\n }\n\n const translator = this.getTranslator(request)\n const { t } = translator\n\n const viewModel = this.getListSummaryViewModel(\n request,\n context,\n list,\n translator\n )\n\n return h.view(this.listSummaryViewName, {\n ...viewModel,\n t\n })\n }\n }\n\n makePostListSummaryRouteHandler() {\n return (\n request: FormRequestPayload,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { path, repeat } = this\n const { query } = request\n const { schema } = repeat\n const { state } = context\n\n const list = this.getListFromState(state)\n\n if (!list.length) {\n const nextPath = redirectPath(`${path}/${randomUUID()}`, {\n returnUrl: query.returnUrl\n })\n\n return super.proceed(request, h, nextPath)\n }\n\n const translator = this.getTranslator(request)\n const { t } = translator\n\n const { action } = this.getFormParams(request)\n\n const hasErrorMin =\n action === FormAction.Continue && list.length < schema.min\n\n const hasErrorMax =\n (action === FormAction.AddAnother && list.length >= schema.max) ||\n (action === FormAction.Continue && list.length > schema.max)\n\n // Show error if repeat limits apply\n if (hasErrorMin || hasErrorMax) {\n const count = hasErrorMax ? schema.max : schema.min\n\n context.errors = [\n {\n path: [],\n href: '',\n name: '',\n text: hasErrorMax\n ? t('pages.repeater.tooMany', { count })\n : t('pages.repeater.tooFew', { count })\n }\n ]\n\n const viewModel = this.getListSummaryViewModel(\n request,\n context,\n list,\n translator\n )\n\n return h.view(this.listSummaryViewName, {\n ...viewModel,\n t\n })\n }\n\n if (action === FormAction.AddAnother) {\n const nextPath = redirectPath(`${path}/${randomUUID()}`, {\n returnUrl: query.returnUrl\n })\n\n return super.proceed(request, h, nextPath)\n }\n\n // Check if this is a save-and-exit action\n if (action === FormAction.SaveAndExit) {\n return this.handleSaveAndExit(request, context, h)\n }\n\n const nextPath = this.getNextPath(context)\n return super.proceed(request, h, nextPath)\n }\n }\n\n makeGetItemDeleteRouteHandler() {\n return (\n request: FormRequest,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { viewModel } = this\n const { state } = context\n\n const list = this.getListFromState(state)\n const itemId = this.getItemId(request)\n const item = this.getItemFromList(list, itemId)\n\n const translator = this.getTranslator(request)\n const { t } = translator\n\n if (!item || list.length === 1) {\n throw Boom.notFound(\n item\n ? t('pages.repeater.lastItemCannotBeRemoved')\n : t('pages.repeater.itemToRemoveNotFound')\n )\n }\n\n const { title } = this.repeat.options\n\n return h.view(this.listDeleteViewName, {\n ...viewModel,\n context,\n backLink: this.getBackLink(request, context, t),\n pageTitle: t('pages.repeater.removeAnswer'),\n itemTitle: `${title} ${list.indexOf(item) + 1}`,\n buttonConfirm: { text: t('pages.repeater.remove') },\n buttonCancel: { text: t('pages.repeater.cancel') },\n t\n } satisfies ItemDeletePageViewModel)\n }\n }\n\n makePostItemDeleteRouteHandler() {\n return async (\n request: FormRequestPayload,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { repeat } = this\n const { state } = context\n\n const { confirm } = this.getFormParams(request)\n\n const list = this.getListFromState(state)\n const itemId = this.getItemId(request)\n const item = this.getItemFromList(list, itemId)\n\n const { t } = this.getTranslator(request)\n\n if (!item || list.length === 1) {\n throw Boom.notFound(\n item\n ? t('pages.repeater.lastItemCannotBeRemoved')\n : t('pages.repeater.itemToRemoveNotFound')\n )\n }\n\n // Remove the item from the list\n if (confirm) {\n list.splice(list.indexOf(item), 1)\n\n const update = {\n [repeat.options.name]: list\n }\n\n await this.mergeState(request, state, update)\n }\n\n return this.proceed(request, h)\n }\n }\n\n getViewModel(\n request: FormContextRequest,\n context: FormContext,\n translator: Translator\n ): FormPageViewModel {\n const { state } = context\n const { tPage } = translator\n\n const list = this.getListFromState(state)\n const itemId = this.getItemId(request)\n const item = this.getItemFromList(list, itemId)\n\n const viewModel = super.getViewModel(request, context, translator)\n const itemNumber = item ? list.indexOf(item) + 1 : list.length + 1\n const title =\n tPage(this.pageDef, 'repeatTitle') || this.pageDef.repeat.options.title\n const repeatCaption = `${title} ${itemNumber}`\n\n return {\n ...viewModel,\n\n sectionTitle: viewModel.sectionTitle\n ? `${viewModel.sectionTitle}: ${repeatCaption}`\n : repeatCaption\n }\n }\n\n getListSummaryViewModel(\n request: FormContextRequest,\n context: FormContext,\n list: RepeatListState,\n translator: Translator\n ): RepeaterSummaryPageViewModel {\n const { collection, href, repeat } = this\n const { query } = request\n const { isForceAccess, errors } = context\n\n const { t, tPage } = translator\n\n const title = tPage(this.pageDef, 'repeatTitle') || repeat.options.title\n\n const summaryList: SummaryList = {\n classes: 'govuk-summary-list--long-actions',\n rows: []\n }\n\n let count = 0\n\n if (Array.isArray(list)) {\n count = list.length\n\n const summaryPath = this.getSummaryPath(request)\n\n list.forEach((item, index) => {\n const items: SummaryListAction[] = []\n\n // Remove summary list actions from previews\n if (!isForceAccess) {\n items.push({\n href: redirectPath(`${href}/${item.itemId}`, {\n returnUrl: query.returnUrl ?? this.getHref(summaryPath)\n }),\n text: t('pages.repeater.change'),\n classes: 'govuk-link--no-visited-state',\n visuallyHiddenText: t('pages.repeater.visuallyHiddenItem', {\n index: index + 1\n })\n })\n\n if (count > 1) {\n items.push({\n href: redirectPath(`${href}/${item.itemId}/confirm-delete`, {\n returnUrl: query.returnUrl\n }),\n text: t('pages.repeater.remove'),\n classes: 'govuk-link--no-visited-state',\n visuallyHiddenText: t('pages.repeater.visuallyHiddenItem', {\n index: index + 1\n })\n })\n }\n }\n\n const itemDisplayText = collection.fields.length\n ? collection.fields[0].getDisplayStringFromState(item, translator)\n : ''\n\n summaryList.rows.push({\n key: {\n text: `${title} ${index + 1}`\n },\n value: {\n text: itemDisplayText || t('pages.repeater.notProvided')\n },\n actions: {\n items\n }\n })\n })\n }\n\n return {\n ...this.viewModel,\n backLink: this.getBackLink(request, context, t),\n repeatTitle: title,\n pageTitle: t('pages.repeater.pageTitle', { count }),\n showTitle: true,\n context,\n errors,\n checkAnswers: [{ summaryList }],\n allowSaveAndExit: this.shouldShowSaveAndExit(request.server)\n }\n }\n\n getSummaryPath(request?: FormContextRequest) {\n const { path } = this\n\n const summaryPath = super.getSummaryPath()\n\n if (!request) {\n return summaryPath\n }\n\n const { query } = request\n\n return redirectPath(`${path}${summaryPath}`, {\n returnUrl: query.returnUrl\n })\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -307,7 +307,7 @@ export async function submitForm(context, formMetadata, request, summaryViewMode
307
307
  if (submitResponse === undefined) {
308
308
  throw Boom.badRequest('Unexpected empty response from submit api');
309
309
  }
310
- await model.services.outputService.submit(context, request, model, emailAddress, items, submitResponse, formMetadata);
310
+ await model.services.outputService.submit(context, request, model, emailAddress, items, submitResponse, translator.language, formMetadata);
311
311
  } catch (err) {
312
312
  if (paymentWasCaptured) {
313
313
  throw new PaymentSubmissionError(context.referenceNumber, formMetadata.contact?.online?.url);
@@ -360,7 +360,10 @@ function submitData(model, items, retrievalKey, sessionId, translator, reference
360
360
  retrievalKey,
361
361
  referenceNumber,
362
362
  main: buildMainRecords(items, translator),
363
- repeaters: buildRepeaterRecords(items, translator)
363
+ repeaters: buildRepeaterRecords(items, translator),
364
+ // Only populate the language property if the form is multi-language enabled
365
+ // @ts-expect-error - dynamic language property
366
+ language: model.def.metadata?.translations?.cy ? translator.language : undefined
364
367
  };
365
368
  return submit(payload);
366
369
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SummaryPageController.js","names":["hasComponentsEvenIfNoNext","Boom","StatusCodes","COMPONENT_STATE_ERROR","PAYMENT_EXPIRED_NOTIFICATION","ComponentCollection","PaymentField","checkEmailAddressForLiveFormSubmission","checkFormStatus","createError","getCacheService","SummaryViewModel","QuestionPageController","InvalidComponentStateError","PaymentErrorTypes","PaymentPreAuthError","PaymentSubmissionError","buildMainRecords","buildRepeaterRecords","DEFAULT_PAYMENT_HELP_URL","formatCurrency","formatPaymentDate","FormAction","SummaryPageController","allowSaveAndExit","findPaymentField","model","pages","flatMap","page","collection","fields","find","field","constructor","pageDef","viewName","components","getSummaryViewModel","request","context","translator","t","viewModel","payload","errors","state","paymentField","resolvedAmount","resolveAmount","options","paymentState","getPaymentStateFromState","amount","paymentDetails","buildPaymentDetails","paymentRequired","preAuth","status","paymentPreAuthorized","getViewModel","backLink","getBackLink","feedbackLink","phaseTag","shouldShowSaveAndExit","server","rows","key","text","value","description","reference","createdAt","push","title","summaryList","makeGetRouteHandler","h","getTranslator","query","paymentComplete","handleFormSubmit","reconcilePaymentState","hasMissingNotificationEmail","view","cacheService","resetComponentStates","name","makePostRouteHandler","action","SaveAndExit","handleSaveAndExit","params","formsService","services","getFormMetadata","formMetadata","slug","notificationEmail","isPreview","submitForm","error","handleSubmissionError","setConfirmationState","confirmed","formId","referenceNumber","clearState","proceed","getStatusPath","handleInvalidComponentStateError","handlePaymentPreAuthError","handlePaymentSubmissionError","govukError","component","userMessage","yar","flash","getStateKeys","path","shouldResetState","errorType","PaymentExpired","PaymentIncomplete","redirect","getHref","code","SEE_OTHER","redirectPath","undefined","helpUrl","helpLink","contactUsLink","helpLinkHtml","postRouteOptions","ext","onPreHandler","method","continue","summaryViewModel","emailAddress","finaliseComponents","paymentWasCaptured","hasPaymentBeenCaptured","formStatus","logTags","logger","info","items","getFormSubmissionData","details","submitResponse","submitData","id","badRequest","outputService","submit","err","contact","online","url","capture","metadata","allFields","Set","relevantPages","filter","onSubmit","retrievalKey","sessionId","formSubmissionService","main","repeaters","map","href","flat","paymentItems","getPaymentFieldItems","label","summaryLabel","getDisplayStringFromState"],"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'\nimport { StatusCodes } from 'http-status-codes'\n\nimport {\n COMPONENT_STATE_ERROR,\n PAYMENT_EXPIRED_NOTIFICATION\n} from '~/src/server/constants.js'\nimport { ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'\nimport { PaymentField } from '~/src/server/plugins/engine/components/PaymentField.js'\nimport {\n checkEmailAddressForLiveFormSubmission,\n checkFormStatus,\n createError,\n getCacheService\n} from '~/src/server/plugins/engine/helpers.js'\nimport { type Translator } from '~/src/server/plugins/engine/i18n/types.js'\nimport {\n SummaryViewModel,\n type FormModel\n} from '~/src/server/plugins/engine/models/index.js'\nimport {\n type Detail,\n type DetailItem,\n type DetailItemField\n} from '~/src/server/plugins/engine/models/types.js'\nimport { QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'\nimport {\n InvalidComponentStateError,\n PaymentErrorTypes,\n PaymentPreAuthError,\n PaymentSubmissionError\n} from '~/src/server/plugins/engine/pageControllers/errors.js'\nimport {\n buildMainRecords,\n buildRepeaterRecords\n} from '~/src/server/plugins/engine/pageControllers/helpers/submission.js'\nimport {\n type FormConfirmationState,\n type FormContext,\n type FormContextRequest\n} from '~/src/server/plugins/engine/types.js'\nimport {\n DEFAULT_PAYMENT_HELP_URL,\n formatCurrency,\n formatPaymentDate\n} from '~/src/server/plugins/payment/helper.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 /**\n * Finds the PaymentField component across all pages in the model.\n * Payment pages are skipped in the normal page walk\n */\n private findPaymentField(): PaymentField | undefined {\n return this.model.pages\n .flatMap((page) => page.collection.fields)\n .find((field): field is PaymentField => field instanceof PaymentField)\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 translator: Translator\n ): SummaryViewModel {\n const { t } = translator\n\n const viewModel = new SummaryViewModel(request, this, context, translator)\n\n const { payload, errors, state } = context\n\n const paymentField = this.findPaymentField()\n\n if (paymentField) {\n const resolvedAmount = PaymentField.resolveAmount(\n paymentField.options,\n this.model,\n state\n )\n const paymentState = paymentField.getPaymentStateFromState(state)\n\n if (paymentState?.amount === resolvedAmount) {\n viewModel.paymentState = paymentState\n viewModel.paymentDetails = this.buildPaymentDetails(\n paymentField,\n paymentState,\n t\n )\n }\n\n if (resolvedAmount > 0) {\n viewModel.paymentRequired = true\n }\n if (\n paymentState &&\n paymentState.preAuth?.status === 'success' &&\n paymentState.amount === resolvedAmount\n ) {\n viewModel.paymentPreAuthorized = true\n }\n }\n\n const components = this.collection.getViewModel({\n payload,\n errors,\n translator\n })\n\n viewModel.backLink = this.getBackLink(request, context, t)\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 private buildPaymentDetails(\n paymentField: PaymentField,\n paymentState: NonNullable<\n ReturnType<PaymentField['getPaymentStateFromState']>\n >,\n t: (key: string, opts?: Record<string, unknown>) => string\n ) {\n const rows = [\n {\n key: { text: t('pages.summary.paymentFor') },\n value: { text: paymentState.description }\n },\n {\n key: { text: t('pages.summary.totalAmount') },\n value: { text: formatCurrency(paymentState.amount) }\n },\n {\n key: { text: t('pages.summary.reference') },\n value: { text: paymentState.reference }\n }\n ]\n\n if (paymentState.preAuth?.createdAt) {\n rows.push({\n key: { text: t('pages.summary.dateOfPayment') },\n value: { text: formatPaymentDate(paymentState.preAuth.createdAt) }\n })\n }\n\n return {\n title: { text: t('pages.summary.paymentDetailsTitle') },\n summaryList: { rows }\n }\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 translator = this.getTranslator(request)\n const { t } = translator\n\n // After GOV.UK Pay callback, auto-submit the form instead of\n // showing CYA again. The payment is already pre-authorized.\n if (request.query.paymentComplete === 'true') {\n return this.handleFormSubmit(\n request as unknown as FormRequestPayload,\n context,\n h\n )\n }\n\n await this.reconcilePaymentState(request, context)\n\n const viewModel = this.getSummaryViewModel(request, context, translator)\n\n viewModel.hasMissingNotificationEmail =\n await this.hasMissingNotificationEmail(request, context)\n\n viewModel.t = t\n return h.view(viewName, viewModel)\n }\n }\n\n /**\n * Checks if the resolved payment amount has changed since pre-auth\n * and invalidates stale payment state if so.\n */\n private async reconcilePaymentState(\n request: FormRequest,\n context: FormContext\n ) {\n const paymentField = this.findPaymentField()\n\n if (!paymentField) {\n return\n }\n\n const resolvedAmount = PaymentField.resolveAmount(\n paymentField.options,\n this.model,\n context.state\n )\n const paymentState = paymentField.getPaymentStateFromState(context.state)\n\n if (paymentState && paymentState.amount !== resolvedAmount) {\n const cacheService = getCacheService(request.server)\n await cacheService.resetComponentStates(request, [paymentField.name])\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 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 const formMetadata = await getFormMetadata(params.slug)\n const { notificationEmail } = formMetadata\n const { isPreview } = checkFormStatus(request.params)\n\n checkEmailAddressForLiveFormSubmission(notificationEmail, isPreview)\n\n if (notificationEmail) {\n const translator = this.getTranslator(request)\n const viewModel = this.getSummaryViewModel(request, context, translator)\n\n try {\n await submitForm(\n context,\n formMetadata,\n request,\n viewModel,\n model,\n notificationEmail,\n translator\n )\n } catch (error) {\n return this.handleSubmissionError(error, request, h)\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 await cacheService.clearState(request)\n\n return this.proceed(request, h, this.getStatusPath())\n }\n\n /**\n * Handles errors during form submission\n */\n private async handleSubmissionError(\n error: unknown,\n request: FormRequestPayload,\n h: FormResponseToolkit\n ) {\n if (error instanceof InvalidComponentStateError) {\n return this.handleInvalidComponentStateError(error, request, h)\n }\n\n if (error instanceof PaymentPreAuthError) {\n return this.handlePaymentPreAuthError(error, request, h)\n }\n\n if (error instanceof PaymentSubmissionError) {\n return this.handlePaymentSubmissionError(error, request, h)\n }\n\n throw error\n }\n\n /**\n * Handles InvalidComponentStateError during submission\n */\n private async handleInvalidComponentStateError(\n error: InvalidComponentStateError,\n request: FormRequestPayload,\n h: FormResponseToolkit\n ) {\n const cacheService = getCacheService(request.server)\n\n const govukError = createError(error.component.name, error.userMessage)\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 /**\n * Handles PaymentPreAuthError during submission\n */\n private async handlePaymentPreAuthError(\n error: PaymentPreAuthError,\n request: FormRequestPayload,\n h: FormResponseToolkit\n ) {\n const cacheService = getCacheService(request.server)\n\n if (error.shouldResetState) {\n await cacheService.resetComponentStates(request, error.getStateKeys())\n\n if (error.errorType === PaymentErrorTypes.PaymentExpired) {\n request.yar.flash(PAYMENT_EXPIRED_NOTIFICATION, true, true)\n return this.proceed(request, h, error.component.page?.path)\n }\n }\n\n // Payment page is skipped in the page walk, so proceed() would redirect\n // to an earlier page. For PaymentIncomplete, redirect directly to the\n // payment page using its href.\n if (\n error.errorType === PaymentErrorTypes.PaymentIncomplete &&\n error.component.page\n ) {\n return h\n .redirect(error.component.page.getHref(error.component.page.path))\n .code(StatusCodes.SEE_OTHER)\n }\n\n const govukError = createError(error.component.name, error.userMessage)\n request.yar.flash(COMPONENT_STATE_ERROR, govukError, true)\n\n const redirectPath = error.shouldResetState\n ? error.component.page?.path\n : undefined\n\n return this.proceed(request, h, redirectPath)\n }\n\n /**\n * Handles PaymentSubmissionError during submission\n */\n private handlePaymentSubmissionError(\n error: PaymentSubmissionError,\n request: FormRequestPayload,\n h: FormResponseToolkit\n ) {\n const { t } = this.getTranslator(request)\n\n const helpUrl = error.helpLink ?? DEFAULT_PAYMENT_HELP_URL\n const contactUsLink = `<a href=\"${helpUrl}\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"govuk-link\">${t('pages.summary.contactUsLinkText')}</a>`\n const helpLinkHtml = t('pages.summary.submissionFailedContactSuffix', {\n contactUsLink\n })\n\n const govukError = createError(\n 'submission',\n `${t('pages.summary.submissionFailed')}${helpLinkHtml}.`\n )\n\n request.yar.flash(COMPONENT_STATE_ERROR, govukError, true)\n\n return this.proceed(request, h)\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 formMetadata: FormMetadata,\n request: FormRequestPayload,\n summaryViewModel: SummaryViewModel,\n model: FormModel,\n emailAddress: string,\n translator: Translator\n) {\n await finaliseComponents(request, formMetadata, context, model)\n\n const paymentWasCaptured = hasPaymentBeenCaptured(context, model)\n\n const formStatus = checkFormStatus(request.params)\n const logTags = ['submit', 'submissionApi']\n\n request.logger.info(logTags, 'Preparing email', formStatus)\n\n const items = getFormSubmissionData(\n summaryViewModel.context,\n summaryViewModel.details,\n translator,\n model\n )\n\n try {\n request.logger.info(logTags, 'Submitting data')\n const submitResponse = await submitData(\n model,\n items,\n emailAddress,\n request.yar.id,\n translator,\n summaryViewModel.context.referenceNumber\n )\n\n if (submitResponse === undefined) {\n throw Boom.badRequest('Unexpected empty response from submit api')\n }\n\n await model.services.outputService.submit(\n context,\n request,\n model,\n emailAddress,\n items,\n submitResponse,\n formMetadata\n )\n } catch (err) {\n if (paymentWasCaptured) {\n throw new PaymentSubmissionError(\n context.referenceNumber,\n formMetadata.contact?.online?.url\n )\n }\n throw err\n }\n}\n\n/**\n * Checks if any payment component has been captured\n */\nfunction hasPaymentBeenCaptured(\n context: FormContext,\n model: FormModel\n): boolean {\n for (const page of model.pages) {\n for (const field of page.collection.fields) {\n if (field instanceof PaymentField) {\n const paymentState = field.getPaymentStateFromState(context.state)\n if (paymentState?.capture?.status === 'success') {\n return true\n }\n }\n }\n }\n return false\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 model: FormModel\n) {\n // Get fields from relevant pages (normal components)\n // plus PaymentField from all pages (payment page is skipped in the page walk)\n const allFields = new Set([\n ...context.relevantPages.flatMap((page) => page.collection.fields),\n ...model.pages\n .flatMap((page) => page.collection.fields)\n .filter((field) => field instanceof PaymentField)\n ])\n\n for (const component of allFields) {\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 translator: Translator,\n referenceNumber: string\n) {\n const { formSubmissionService } = model.services\n const { submit } = formSubmissionService\n\n const payload: SubmitPayload = {\n sessionId,\n retrievalKey,\n referenceNumber,\n main: buildMainRecords(items, translator),\n repeaters: buildRepeaterRecords(items, translator)\n }\n return submit(payload)\n}\n\nexport function getFormSubmissionData(\n context: FormContext,\n details: Detail[],\n translator: Translator,\n model: FormModel\n) {\n const items = context.relevantPages\n .map(({ href }) =>\n details.flatMap(({ items }) =>\n items.filter(({ page }) => page.href === href)\n )\n )\n .flat()\n\n const paymentItems = getPaymentFieldItems(context, translator, model)\n\n return [...items, ...paymentItems]\n}\n\n/**\n * Gets DetailItems for PaymentField components\n * PaymentField is excluded from summaryDetails for UI but needs to be in submission data\n */\nfunction getPaymentFieldItems(\n context: FormContext,\n translator: Translator,\n model: FormModel\n): DetailItemField[] {\n const items: DetailItemField[] = []\n\n for (const page of model.pages) {\n for (const field of page.collection.fields) {\n if (field instanceof PaymentField) {\n items.push({\n name: field.name,\n page,\n title: field.title,\n label: field.summaryLabel,\n field,\n state: context.state,\n href: page.href,\n value: field.getDisplayStringFromState(context.state, translator)\n })\n }\n }\n }\n\n return items\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"SummaryPageController.js","names":["hasComponentsEvenIfNoNext","Boom","StatusCodes","COMPONENT_STATE_ERROR","PAYMENT_EXPIRED_NOTIFICATION","ComponentCollection","PaymentField","checkEmailAddressForLiveFormSubmission","checkFormStatus","createError","getCacheService","SummaryViewModel","QuestionPageController","InvalidComponentStateError","PaymentErrorTypes","PaymentPreAuthError","PaymentSubmissionError","buildMainRecords","buildRepeaterRecords","DEFAULT_PAYMENT_HELP_URL","formatCurrency","formatPaymentDate","FormAction","SummaryPageController","allowSaveAndExit","findPaymentField","model","pages","flatMap","page","collection","fields","find","field","constructor","pageDef","viewName","components","getSummaryViewModel","request","context","translator","t","viewModel","payload","errors","state","paymentField","resolvedAmount","resolveAmount","options","paymentState","getPaymentStateFromState","amount","paymentDetails","buildPaymentDetails","paymentRequired","preAuth","status","paymentPreAuthorized","getViewModel","backLink","getBackLink","feedbackLink","phaseTag","shouldShowSaveAndExit","server","rows","key","text","value","description","reference","createdAt","push","title","summaryList","makeGetRouteHandler","h","getTranslator","query","paymentComplete","handleFormSubmit","reconcilePaymentState","hasMissingNotificationEmail","view","cacheService","resetComponentStates","name","makePostRouteHandler","action","SaveAndExit","handleSaveAndExit","params","formsService","services","getFormMetadata","formMetadata","slug","notificationEmail","isPreview","submitForm","error","handleSubmissionError","setConfirmationState","confirmed","formId","referenceNumber","clearState","proceed","getStatusPath","handleInvalidComponentStateError","handlePaymentPreAuthError","handlePaymentSubmissionError","govukError","component","userMessage","yar","flash","getStateKeys","path","shouldResetState","errorType","PaymentExpired","PaymentIncomplete","redirect","getHref","code","SEE_OTHER","redirectPath","undefined","helpUrl","helpLink","contactUsLink","helpLinkHtml","postRouteOptions","ext","onPreHandler","method","continue","summaryViewModel","emailAddress","finaliseComponents","paymentWasCaptured","hasPaymentBeenCaptured","formStatus","logTags","logger","info","items","getFormSubmissionData","details","submitResponse","submitData","id","badRequest","outputService","submit","language","err","contact","online","url","capture","metadata","allFields","Set","relevantPages","filter","onSubmit","retrievalKey","sessionId","formSubmissionService","main","repeaters","def","translations","cy","map","href","flat","paymentItems","getPaymentFieldItems","label","summaryLabel","getDisplayStringFromState"],"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'\nimport { StatusCodes } from 'http-status-codes'\n\nimport {\n COMPONENT_STATE_ERROR,\n PAYMENT_EXPIRED_NOTIFICATION\n} from '~/src/server/constants.js'\nimport { ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'\nimport { PaymentField } from '~/src/server/plugins/engine/components/PaymentField.js'\nimport {\n checkEmailAddressForLiveFormSubmission,\n checkFormStatus,\n createError,\n getCacheService\n} from '~/src/server/plugins/engine/helpers.js'\nimport { type Translator } from '~/src/server/plugins/engine/i18n/types.js'\nimport {\n SummaryViewModel,\n type FormModel\n} from '~/src/server/plugins/engine/models/index.js'\nimport {\n type Detail,\n type DetailItem,\n type DetailItemField\n} from '~/src/server/plugins/engine/models/types.js'\nimport { QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'\nimport {\n InvalidComponentStateError,\n PaymentErrorTypes,\n PaymentPreAuthError,\n PaymentSubmissionError\n} from '~/src/server/plugins/engine/pageControllers/errors.js'\nimport {\n buildMainRecords,\n buildRepeaterRecords\n} from '~/src/server/plugins/engine/pageControllers/helpers/submission.js'\nimport {\n type FormConfirmationState,\n type FormContext,\n type FormContextRequest\n} from '~/src/server/plugins/engine/types.js'\nimport {\n DEFAULT_PAYMENT_HELP_URL,\n formatCurrency,\n formatPaymentDate\n} from '~/src/server/plugins/payment/helper.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 /**\n * Finds the PaymentField component across all pages in the model.\n * Payment pages are skipped in the normal page walk\n */\n private findPaymentField(): PaymentField | undefined {\n return this.model.pages\n .flatMap((page) => page.collection.fields)\n .find((field): field is PaymentField => field instanceof PaymentField)\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 translator: Translator\n ): SummaryViewModel {\n const { t } = translator\n\n const viewModel = new SummaryViewModel(request, this, context, translator)\n\n const { payload, errors, state } = context\n\n const paymentField = this.findPaymentField()\n\n if (paymentField) {\n const resolvedAmount = PaymentField.resolveAmount(\n paymentField.options,\n this.model,\n state\n )\n const paymentState = paymentField.getPaymentStateFromState(state)\n\n if (paymentState?.amount === resolvedAmount) {\n viewModel.paymentState = paymentState\n viewModel.paymentDetails = this.buildPaymentDetails(\n paymentField,\n paymentState,\n t\n )\n }\n\n if (resolvedAmount > 0) {\n viewModel.paymentRequired = true\n }\n if (\n paymentState &&\n paymentState.preAuth?.status === 'success' &&\n paymentState.amount === resolvedAmount\n ) {\n viewModel.paymentPreAuthorized = true\n }\n }\n\n const components = this.collection.getViewModel({\n payload,\n errors,\n translator\n })\n\n viewModel.backLink = this.getBackLink(request, context, t)\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 private buildPaymentDetails(\n paymentField: PaymentField,\n paymentState: NonNullable<\n ReturnType<PaymentField['getPaymentStateFromState']>\n >,\n t: (key: string, opts?: Record<string, unknown>) => string\n ) {\n const rows = [\n {\n key: { text: t('pages.summary.paymentFor') },\n value: { text: paymentState.description }\n },\n {\n key: { text: t('pages.summary.totalAmount') },\n value: { text: formatCurrency(paymentState.amount) }\n },\n {\n key: { text: t('pages.summary.reference') },\n value: { text: paymentState.reference }\n }\n ]\n\n if (paymentState.preAuth?.createdAt) {\n rows.push({\n key: { text: t('pages.summary.dateOfPayment') },\n value: { text: formatPaymentDate(paymentState.preAuth.createdAt) }\n })\n }\n\n return {\n title: { text: t('pages.summary.paymentDetailsTitle') },\n summaryList: { rows }\n }\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 translator = this.getTranslator(request)\n const { t } = translator\n\n // After GOV.UK Pay callback, auto-submit the form instead of\n // showing CYA again. The payment is already pre-authorized.\n if (request.query.paymentComplete === 'true') {\n return this.handleFormSubmit(\n request as unknown as FormRequestPayload,\n context,\n h\n )\n }\n\n await this.reconcilePaymentState(request, context)\n\n const viewModel = this.getSummaryViewModel(request, context, translator)\n\n viewModel.hasMissingNotificationEmail =\n await this.hasMissingNotificationEmail(request, context)\n\n viewModel.t = t\n return h.view(viewName, viewModel)\n }\n }\n\n /**\n * Checks if the resolved payment amount has changed since pre-auth\n * and invalidates stale payment state if so.\n */\n private async reconcilePaymentState(\n request: FormRequest,\n context: FormContext\n ) {\n const paymentField = this.findPaymentField()\n\n if (!paymentField) {\n return\n }\n\n const resolvedAmount = PaymentField.resolveAmount(\n paymentField.options,\n this.model,\n context.state\n )\n const paymentState = paymentField.getPaymentStateFromState(context.state)\n\n if (paymentState && paymentState.amount !== resolvedAmount) {\n const cacheService = getCacheService(request.server)\n await cacheService.resetComponentStates(request, [paymentField.name])\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 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 const formMetadata = await getFormMetadata(params.slug)\n const { notificationEmail } = formMetadata\n const { isPreview } = checkFormStatus(request.params)\n\n checkEmailAddressForLiveFormSubmission(notificationEmail, isPreview)\n\n if (notificationEmail) {\n const translator = this.getTranslator(request)\n const viewModel = this.getSummaryViewModel(request, context, translator)\n\n try {\n await submitForm(\n context,\n formMetadata,\n request,\n viewModel,\n model,\n notificationEmail,\n translator\n )\n } catch (error) {\n return this.handleSubmissionError(error, request, h)\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 await cacheService.clearState(request)\n\n return this.proceed(request, h, this.getStatusPath())\n }\n\n /**\n * Handles errors during form submission\n */\n private async handleSubmissionError(\n error: unknown,\n request: FormRequestPayload,\n h: FormResponseToolkit\n ) {\n if (error instanceof InvalidComponentStateError) {\n return this.handleInvalidComponentStateError(error, request, h)\n }\n\n if (error instanceof PaymentPreAuthError) {\n return this.handlePaymentPreAuthError(error, request, h)\n }\n\n if (error instanceof PaymentSubmissionError) {\n return this.handlePaymentSubmissionError(error, request, h)\n }\n\n throw error\n }\n\n /**\n * Handles InvalidComponentStateError during submission\n */\n private async handleInvalidComponentStateError(\n error: InvalidComponentStateError,\n request: FormRequestPayload,\n h: FormResponseToolkit\n ) {\n const cacheService = getCacheService(request.server)\n\n const govukError = createError(error.component.name, error.userMessage)\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 /**\n * Handles PaymentPreAuthError during submission\n */\n private async handlePaymentPreAuthError(\n error: PaymentPreAuthError,\n request: FormRequestPayload,\n h: FormResponseToolkit\n ) {\n const cacheService = getCacheService(request.server)\n\n if (error.shouldResetState) {\n await cacheService.resetComponentStates(request, error.getStateKeys())\n\n if (error.errorType === PaymentErrorTypes.PaymentExpired) {\n request.yar.flash(PAYMENT_EXPIRED_NOTIFICATION, true, true)\n return this.proceed(request, h, error.component.page?.path)\n }\n }\n\n // Payment page is skipped in the page walk, so proceed() would redirect\n // to an earlier page. For PaymentIncomplete, redirect directly to the\n // payment page using its href.\n if (\n error.errorType === PaymentErrorTypes.PaymentIncomplete &&\n error.component.page\n ) {\n return h\n .redirect(error.component.page.getHref(error.component.page.path))\n .code(StatusCodes.SEE_OTHER)\n }\n\n const govukError = createError(error.component.name, error.userMessage)\n request.yar.flash(COMPONENT_STATE_ERROR, govukError, true)\n\n const redirectPath = error.shouldResetState\n ? error.component.page?.path\n : undefined\n\n return this.proceed(request, h, redirectPath)\n }\n\n /**\n * Handles PaymentSubmissionError during submission\n */\n private handlePaymentSubmissionError(\n error: PaymentSubmissionError,\n request: FormRequestPayload,\n h: FormResponseToolkit\n ) {\n const { t } = this.getTranslator(request)\n\n const helpUrl = error.helpLink ?? DEFAULT_PAYMENT_HELP_URL\n const contactUsLink = `<a href=\"${helpUrl}\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"govuk-link\">${t('pages.summary.contactUsLinkText')}</a>`\n const helpLinkHtml = t('pages.summary.submissionFailedContactSuffix', {\n contactUsLink\n })\n\n const govukError = createError(\n 'submission',\n `${t('pages.summary.submissionFailed')}${helpLinkHtml}.`\n )\n\n request.yar.flash(COMPONENT_STATE_ERROR, govukError, true)\n\n return this.proceed(request, h)\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 formMetadata: FormMetadata,\n request: FormRequestPayload,\n summaryViewModel: SummaryViewModel,\n model: FormModel,\n emailAddress: string,\n translator: Translator\n) {\n await finaliseComponents(request, formMetadata, context, model)\n\n const paymentWasCaptured = hasPaymentBeenCaptured(context, model)\n\n const formStatus = checkFormStatus(request.params)\n const logTags = ['submit', 'submissionApi']\n\n request.logger.info(logTags, 'Preparing email', formStatus)\n\n const items = getFormSubmissionData(\n summaryViewModel.context,\n summaryViewModel.details,\n translator,\n model\n )\n\n try {\n request.logger.info(logTags, 'Submitting data')\n const submitResponse = await submitData(\n model,\n items,\n emailAddress,\n request.yar.id,\n translator,\n summaryViewModel.context.referenceNumber\n )\n\n if (submitResponse === undefined) {\n throw Boom.badRequest('Unexpected empty response from submit api')\n }\n\n await model.services.outputService.submit(\n context,\n request,\n model,\n emailAddress,\n items,\n submitResponse,\n translator.language,\n formMetadata\n )\n } catch (err) {\n if (paymentWasCaptured) {\n throw new PaymentSubmissionError(\n context.referenceNumber,\n formMetadata.contact?.online?.url\n )\n }\n throw err\n }\n}\n\n/**\n * Checks if any payment component has been captured\n */\nfunction hasPaymentBeenCaptured(\n context: FormContext,\n model: FormModel\n): boolean {\n for (const page of model.pages) {\n for (const field of page.collection.fields) {\n if (field instanceof PaymentField) {\n const paymentState = field.getPaymentStateFromState(context.state)\n if (paymentState?.capture?.status === 'success') {\n return true\n }\n }\n }\n }\n return false\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 model: FormModel\n) {\n // Get fields from relevant pages (normal components)\n // plus PaymentField from all pages (payment page is skipped in the page walk)\n const allFields = new Set([\n ...context.relevantPages.flatMap((page) => page.collection.fields),\n ...model.pages\n .flatMap((page) => page.collection.fields)\n .filter((field) => field instanceof PaymentField)\n ])\n\n for (const component of allFields) {\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 translator: Translator,\n referenceNumber: string\n) {\n const { formSubmissionService } = model.services\n const { submit } = formSubmissionService\n\n const payload: SubmitPayload = {\n sessionId,\n retrievalKey,\n referenceNumber,\n main: buildMainRecords(items, translator),\n repeaters: buildRepeaterRecords(items, translator),\n // Only populate the language property if the form is multi-language enabled\n // @ts-expect-error - dynamic language property\n language: model.def.metadata?.translations?.cy\n ? translator.language\n : undefined\n }\n return submit(payload)\n}\n\nexport function getFormSubmissionData(\n context: FormContext,\n details: Detail[],\n translator: Translator,\n model: FormModel\n) {\n const items = context.relevantPages\n .map(({ href }) =>\n details.flatMap(({ items }) =>\n items.filter(({ page }) => page.href === href)\n )\n )\n .flat()\n\n const paymentItems = getPaymentFieldItems(context, translator, model)\n\n return [...items, ...paymentItems]\n}\n\n/**\n * Gets DetailItems for PaymentField components\n * PaymentField is excluded from summaryDetails for UI but needs to be in submission data\n */\nfunction getPaymentFieldItems(\n context: FormContext,\n translator: Translator,\n model: FormModel\n): DetailItemField[] {\n const items: DetailItemField[] = []\n\n for (const page of model.pages) {\n for (const field of page.collection.fields) {\n if (field instanceof PaymentField) {\n items.push({\n name: field.name,\n page,\n title: field.title,\n label: field.summaryLabel,\n field,\n state: context.state,\n href: page.href,\n value: field.getDisplayStringFromState(context.state, translator)\n })\n }\n }\n }\n\n return items\n}\n"],"mappings":"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","ignoreList":[]}
@@ -8,4 +8,4 @@ import { type FormRequestPayload } from '../../../../server/routes/types.js';
8
8
  * Can be disabled by not providing notifyTemplateId in config
9
9
  * Can be overridden by providing a custom outputService in the services config
10
10
  */
11
- export declare function submit(context: FormContext, request: FormRequestPayload, model: FormModel, emailAddress: string, items: DetailItem[], submitResponse: SubmitResponsePayload, formMetadata?: FormMetadata): Promise<void>;
11
+ export declare function submit(context: FormContext, request: FormRequestPayload, model: FormModel, emailAddress: string, items: DetailItem[], submitResponse: SubmitResponsePayload, _language: string, formMetadata?: FormMetadata): Promise<void>;
@@ -11,7 +11,7 @@ const templateId = config.get('notifyTemplateId');
11
11
  * Can be disabled by not providing notifyTemplateId in config
12
12
  * Can be overridden by providing a custom outputService in the services config
13
13
  */
14
- export async function submit(context, request, model, emailAddress, items, submitResponse, formMetadata) {
14
+ export async function submit(context, request, model, emailAddress, items, submitResponse, _language, formMetadata) {
15
15
  if (!templateId) {
16
16
  return Promise.resolve();
17
17
  }
@@ -1 +1 @@
1
- {"version":3,"file":"notifyService.js","names":["getErrorMessage","config","escapeMarkdown","checkFormStatus","getFormatter","sendNotification","templateId","get","submit","context","request","model","emailAddress","items","submitResponse","formMetadata","Promise","resolve","logTags","formStatus","params","logger","info","formName","name","subject","isPreview","outputAudience","def","output","audience","outputVersion","version","outputFormatter","body","Buffer","from","toString","personalisation","err","error"],"sources":["../../../../../src/server/plugins/engine/services/notifyService.ts"],"sourcesContent":["import {\n getErrorMessage,\n type FormMetadata,\n type SubmitResponsePayload\n} from '@defra/forms-model'\n\nimport { config } from '~/src/config/index.js'\nimport { escapeMarkdown } from '~/src/server/plugins/engine/components/helpers/index.js'\nimport { checkFormStatus } from '~/src/server/plugins/engine/helpers.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type DetailItem } from '~/src/server/plugins/engine/models/types.js'\nimport { getFormatter } from '~/src/server/plugins/engine/outputFormatters/index.js'\nimport { type FormContext } from '~/src/server/plugins/engine/types.js'\nimport { type FormRequestPayload } from '~/src/server/routes/types.js'\nimport { sendNotification } from '~/src/server/utils/notify.js'\n\nconst templateId = config.get('notifyTemplateId')\n\n/**\n * Optional GOV.UK Notify service for consumers who want email notifications\n * Can be disabled by not providing notifyTemplateId in config\n * Can be overridden by providing a custom outputService in the services config\n */\nexport async function submit(\n context: FormContext,\n request: FormRequestPayload,\n model: FormModel,\n emailAddress: string,\n items: DetailItem[],\n submitResponse: SubmitResponsePayload,\n formMetadata?: FormMetadata\n) {\n if (!templateId) {\n return Promise.resolve()\n }\n\n const logTags = ['submit', 'email']\n const formStatus = checkFormStatus(request.params)\n\n // Get submission email personalisation\n request.logger.info(logTags, 'Getting personalisation data')\n\n const formName = escapeMarkdown(model.name)\n const subject = formStatus.isPreview\n ? `TEST FORM SUBMISSION: ${formName}`\n : `Form submission: ${formName}`\n\n const outputAudience = model.def.output?.audience ?? 'human'\n const outputVersion = model.def.output?.version ?? '1'\n\n const outputFormatter = getFormatter(outputAudience, outputVersion)\n let body = outputFormatter(\n context,\n items,\n model,\n submitResponse,\n formStatus,\n formMetadata\n )\n\n // GOV.UK Notify transforms quotes into curly quotes, so we can't just send the raw payload\n // This is logic specific to Notify, so we include the logic here rather than in the formatter\n if (outputAudience === 'machine') {\n body = Buffer.from(body).toString('base64')\n }\n\n request.logger.info(logTags, 'Sending email')\n\n try {\n // Send submission email\n await sendNotification({\n templateId,\n emailAddress,\n personalisation: {\n subject,\n body\n }\n })\n\n request.logger.info(logTags, 'Email sent successfully')\n } catch (err) {\n request.logger.error(\n err,\n `[emailSendFailed] Error sending notification email - templateId: ${templateId} - recipient: ${emailAddress} - ${getErrorMessage(err)}`\n )\n\n throw err\n }\n}\n"],"mappings":"AAAA,SACEA,eAAe,QAGV,oBAAoB;AAE3B,SAASC,MAAM;AACf,SAASC,cAAc;AACvB,SAASC,eAAe;AAGxB,SAASC,YAAY;AAGrB,SAASC,gBAAgB;AAEzB,MAAMC,UAAU,GAAGL,MAAM,CAACM,GAAG,CAAC,kBAAkB,CAAC;;AAEjD;AACA;AACA;AACA;AACA;AACA,OAAO,eAAeC,MAAMA,CAC1BC,OAAoB,EACpBC,OAA2B,EAC3BC,KAAgB,EAChBC,YAAoB,EACpBC,KAAmB,EACnBC,cAAqC,EACrCC,YAA2B,EAC3B;EACA,IAAI,CAACT,UAAU,EAAE;IACf,OAAOU,OAAO,CAACC,OAAO,CAAC,CAAC;EAC1B;EAEA,MAAMC,OAAO,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC;EACnC,MAAMC,UAAU,GAAGhB,eAAe,CAACO,OAAO,CAACU,MAAM,CAAC;;EAElD;EACAV,OAAO,CAACW,MAAM,CAACC,IAAI,CAACJ,OAAO,EAAE,8BAA8B,CAAC;EAE5D,MAAMK,QAAQ,GAAGrB,cAAc,CAACS,KAAK,CAACa,IAAI,CAAC;EAC3C,MAAMC,OAAO,GAAGN,UAAU,CAACO,SAAS,GAChC,yBAAyBH,QAAQ,EAAE,GACnC,oBAAoBA,QAAQ,EAAE;EAElC,MAAMI,cAAc,GAAGhB,KAAK,CAACiB,GAAG,CAACC,MAAM,EAAEC,QAAQ,IAAI,OAAO;EAC5D,MAAMC,aAAa,GAAGpB,KAAK,CAACiB,GAAG,CAACC,MAAM,EAAEG,OAAO,IAAI,GAAG;EAEtD,MAAMC,eAAe,GAAG7B,YAAY,CAACuB,cAAc,EAAEI,aAAa,CAAC;EACnE,IAAIG,IAAI,GAAGD,eAAe,CACxBxB,OAAO,EACPI,KAAK,EACLF,KAAK,EACLG,cAAc,EACdK,UAAU,EACVJ,YACF,CAAC;;EAED;EACA;EACA,IAAIY,cAAc,KAAK,SAAS,EAAE;IAChCO,IAAI,GAAGC,MAAM,CAACC,IAAI,CAACF,IAAI,CAAC,CAACG,QAAQ,CAAC,QAAQ,CAAC;EAC7C;EAEA3B,OAAO,CAACW,MAAM,CAACC,IAAI,CAACJ,OAAO,EAAE,eAAe,CAAC;EAE7C,IAAI;IACF;IACA,MAAMb,gBAAgB,CAAC;MACrBC,UAAU;MACVM,YAAY;MACZ0B,eAAe,EAAE;QACfb,OAAO;QACPS;MACF;IACF,CAAC,CAAC;IAEFxB,OAAO,CAACW,MAAM,CAACC,IAAI,CAACJ,OAAO,EAAE,yBAAyB,CAAC;EACzD,CAAC,CAAC,OAAOqB,GAAG,EAAE;IACZ7B,OAAO,CAACW,MAAM,CAACmB,KAAK,CAClBD,GAAG,EACH,oEAAoEjC,UAAU,iBAAiBM,YAAY,MAAMZ,eAAe,CAACuC,GAAG,CAAC,EACvI,CAAC;IAED,MAAMA,GAAG;EACX;AACF","ignoreList":[]}
1
+ {"version":3,"file":"notifyService.js","names":["getErrorMessage","config","escapeMarkdown","checkFormStatus","getFormatter","sendNotification","templateId","get","submit","context","request","model","emailAddress","items","submitResponse","_language","formMetadata","Promise","resolve","logTags","formStatus","params","logger","info","formName","name","subject","isPreview","outputAudience","def","output","audience","outputVersion","version","outputFormatter","body","Buffer","from","toString","personalisation","err","error"],"sources":["../../../../../src/server/plugins/engine/services/notifyService.ts"],"sourcesContent":["import {\n getErrorMessage,\n type FormMetadata,\n type SubmitResponsePayload\n} from '@defra/forms-model'\n\nimport { config } from '~/src/config/index.js'\nimport { escapeMarkdown } from '~/src/server/plugins/engine/components/helpers/index.js'\nimport { checkFormStatus } from '~/src/server/plugins/engine/helpers.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type DetailItem } from '~/src/server/plugins/engine/models/types.js'\nimport { getFormatter } from '~/src/server/plugins/engine/outputFormatters/index.js'\nimport { type FormContext } from '~/src/server/plugins/engine/types.js'\nimport { type FormRequestPayload } from '~/src/server/routes/types.js'\nimport { sendNotification } from '~/src/server/utils/notify.js'\n\nconst templateId = config.get('notifyTemplateId')\n\n/**\n * Optional GOV.UK Notify service for consumers who want email notifications\n * Can be disabled by not providing notifyTemplateId in config\n * Can be overridden by providing a custom outputService in the services config\n */\nexport async function submit(\n context: FormContext,\n request: FormRequestPayload,\n model: FormModel,\n emailAddress: string,\n items: DetailItem[],\n submitResponse: SubmitResponsePayload,\n _language: string,\n formMetadata?: FormMetadata\n) {\n if (!templateId) {\n return Promise.resolve()\n }\n\n const logTags = ['submit', 'email']\n const formStatus = checkFormStatus(request.params)\n\n // Get submission email personalisation\n request.logger.info(logTags, 'Getting personalisation data')\n\n const formName = escapeMarkdown(model.name)\n const subject = formStatus.isPreview\n ? `TEST FORM SUBMISSION: ${formName}`\n : `Form submission: ${formName}`\n\n const outputAudience = model.def.output?.audience ?? 'human'\n const outputVersion = model.def.output?.version ?? '1'\n\n const outputFormatter = getFormatter(outputAudience, outputVersion)\n let body = outputFormatter(\n context,\n items,\n model,\n submitResponse,\n formStatus,\n formMetadata\n )\n\n // GOV.UK Notify transforms quotes into curly quotes, so we can't just send the raw payload\n // This is logic specific to Notify, so we include the logic here rather than in the formatter\n if (outputAudience === 'machine') {\n body = Buffer.from(body).toString('base64')\n }\n\n request.logger.info(logTags, 'Sending email')\n\n try {\n // Send submission email\n await sendNotification({\n templateId,\n emailAddress,\n personalisation: {\n subject,\n body\n }\n })\n\n request.logger.info(logTags, 'Email sent successfully')\n } catch (err) {\n request.logger.error(\n err,\n `[emailSendFailed] Error sending notification email - templateId: ${templateId} - recipient: ${emailAddress} - ${getErrorMessage(err)}`\n )\n\n throw err\n }\n}\n"],"mappings":"AAAA,SACEA,eAAe,QAGV,oBAAoB;AAE3B,SAASC,MAAM;AACf,SAASC,cAAc;AACvB,SAASC,eAAe;AAGxB,SAASC,YAAY;AAGrB,SAASC,gBAAgB;AAEzB,MAAMC,UAAU,GAAGL,MAAM,CAACM,GAAG,CAAC,kBAAkB,CAAC;;AAEjD;AACA;AACA;AACA;AACA;AACA,OAAO,eAAeC,MAAMA,CAC1BC,OAAoB,EACpBC,OAA2B,EAC3BC,KAAgB,EAChBC,YAAoB,EACpBC,KAAmB,EACnBC,cAAqC,EACrCC,SAAiB,EACjBC,YAA2B,EAC3B;EACA,IAAI,CAACV,UAAU,EAAE;IACf,OAAOW,OAAO,CAACC,OAAO,CAAC,CAAC;EAC1B;EAEA,MAAMC,OAAO,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC;EACnC,MAAMC,UAAU,GAAGjB,eAAe,CAACO,OAAO,CAACW,MAAM,CAAC;;EAElD;EACAX,OAAO,CAACY,MAAM,CAACC,IAAI,CAACJ,OAAO,EAAE,8BAA8B,CAAC;EAE5D,MAAMK,QAAQ,GAAGtB,cAAc,CAACS,KAAK,CAACc,IAAI,CAAC;EAC3C,MAAMC,OAAO,GAAGN,UAAU,CAACO,SAAS,GAChC,yBAAyBH,QAAQ,EAAE,GACnC,oBAAoBA,QAAQ,EAAE;EAElC,MAAMI,cAAc,GAAGjB,KAAK,CAACkB,GAAG,CAACC,MAAM,EAAEC,QAAQ,IAAI,OAAO;EAC5D,MAAMC,aAAa,GAAGrB,KAAK,CAACkB,GAAG,CAACC,MAAM,EAAEG,OAAO,IAAI,GAAG;EAEtD,MAAMC,eAAe,GAAG9B,YAAY,CAACwB,cAAc,EAAEI,aAAa,CAAC;EACnE,IAAIG,IAAI,GAAGD,eAAe,CACxBzB,OAAO,EACPI,KAAK,EACLF,KAAK,EACLG,cAAc,EACdM,UAAU,EACVJ,YACF,CAAC;;EAED;EACA;EACA,IAAIY,cAAc,KAAK,SAAS,EAAE;IAChCO,IAAI,GAAGC,MAAM,CAACC,IAAI,CAACF,IAAI,CAAC,CAACG,QAAQ,CAAC,QAAQ,CAAC;EAC7C;EAEA5B,OAAO,CAACY,MAAM,CAACC,IAAI,CAACJ,OAAO,EAAE,eAAe,CAAC;EAE7C,IAAI;IACF;IACA,MAAMd,gBAAgB,CAAC;MACrBC,UAAU;MACVM,YAAY;MACZ2B,eAAe,EAAE;QACfb,OAAO;QACPS;MACF;IACF,CAAC,CAAC;IAEFzB,OAAO,CAACY,MAAM,CAACC,IAAI,CAACJ,OAAO,EAAE,yBAAyB,CAAC;EACzD,CAAC,CAAC,OAAOqB,GAAG,EAAE;IACZ9B,OAAO,CAACY,MAAM,CAACmB,KAAK,CAClBD,GAAG,EACH,oEAAoElC,UAAU,iBAAiBM,YAAY,MAAMZ,eAAe,CAACwC,GAAG,CAAC,EACvI,CAAC;IAED,MAAMA,GAAG;EACX;AACF","ignoreList":[]}
@@ -41,5 +41,5 @@ export interface RouteConfig {
41
41
  getLanguage?: GetLanguageHandler;
42
42
  }
43
43
  export interface OutputService {
44
- submit: (context: FormContext, request: FormRequestPayload, model: FormModel, emailAddress: string, items: DetailItem[], submitResponse: SubmitResponsePayload, formMetadata?: FormMetadata) => Promise<void>;
44
+ submit: (context: FormContext, request: FormRequestPayload, model: FormModel, emailAddress: string, items: DetailItem[], submitResponse: SubmitResponsePayload, language: string, formMetadata?: FormMetadata) => Promise<void>;
45
45
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","names":[],"sources":["../../src/server/types.ts"],"sourcesContent":["import {\n type FormDefinition,\n type FormMetadata,\n type SubmitPayload,\n type SubmitResponsePayload\n} from '@defra/forms-model'\nimport { type Server } from '@hapi/hapi'\n\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type DetailItem } from '~/src/server/plugins/engine/models/types.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport {\n type FormContext,\n type GetLanguageHandler,\n type OnRequestCallback,\n type PluginOptions,\n type PreparePageEventRequestOptions\n} from '~/src/server/plugins/engine/types.js'\nimport { type PaymentService } from '~/src/server/plugins/payment/service.js'\nimport {\n type FormRequestPayload,\n type FormStatus\n} from '~/src/server/routes/types.js'\nimport { type CacheService } from '~/src/server/services/cacheService.js'\n\nexport interface FormsService {\n getFormMetadata: (slug: string) => Promise<FormMetadata>\n getFormMetadataById: (id: string) => Promise<FormMetadata>\n getFormDefinition: (\n id: string,\n state: FormStatus\n ) => Promise<FormDefinition | undefined>\n getFormSecret: (formId: string, secretName: string) => Promise<string>\n}\n\nexport interface FormSubmissionService {\n persistFiles: (\n files: { fileId: string; initiatedRetrievalKey: string }[],\n persistedRetrievalKey: string\n ) => Promise<object>\n submit: (data: SubmitPayload) => Promise<SubmitResponsePayload | undefined>\n}\n\nexport interface Services {\n formsService: FormsService\n formSubmissionService: FormSubmissionService\n outputService: OutputService\n paymentService?: PaymentService\n}\n\nexport interface RouteConfig {\n formFileName?: string\n formFilePath?: string\n enforceCsrf?: boolean\n services?: Services\n controllers?: Record<string, typeof PageController>\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n onRequest?: OnRequestCallback\n saveAndExit?: PluginOptions['saveAndExit']\n cacheServiceCreator?: (server: Server) => CacheService\n ordnanceSurveyApiKey?: string\n ordnanceSurveyApiSecret?: string\n getLanguage?: GetLanguageHandler\n}\n\nexport interface OutputService {\n submit: (\n context: FormContext,\n request: FormRequestPayload,\n model: FormModel,\n emailAddress: string,\n items: DetailItem[],\n submitResponse: SubmitResponsePayload,\n formMetadata?: FormMetadata\n ) => Promise<void>\n}\n"],"mappings":"","ignoreList":[]}
1
+ {"version":3,"file":"types.js","names":[],"sources":["../../src/server/types.ts"],"sourcesContent":["import {\n type FormDefinition,\n type FormMetadata,\n type SubmitPayload,\n type SubmitResponsePayload\n} from '@defra/forms-model'\nimport { type Server } from '@hapi/hapi'\n\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type DetailItem } from '~/src/server/plugins/engine/models/types.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport {\n type FormContext,\n type GetLanguageHandler,\n type OnRequestCallback,\n type PluginOptions,\n type PreparePageEventRequestOptions\n} from '~/src/server/plugins/engine/types.js'\nimport { type PaymentService } from '~/src/server/plugins/payment/service.js'\nimport {\n type FormRequestPayload,\n type FormStatus\n} from '~/src/server/routes/types.js'\nimport { type CacheService } from '~/src/server/services/cacheService.js'\n\nexport interface FormsService {\n getFormMetadata: (slug: string) => Promise<FormMetadata>\n getFormMetadataById: (id: string) => Promise<FormMetadata>\n getFormDefinition: (\n id: string,\n state: FormStatus\n ) => Promise<FormDefinition | undefined>\n getFormSecret: (formId: string, secretName: string) => Promise<string>\n}\n\nexport interface FormSubmissionService {\n persistFiles: (\n files: { fileId: string; initiatedRetrievalKey: string }[],\n persistedRetrievalKey: string\n ) => Promise<object>\n submit: (data: SubmitPayload) => Promise<SubmitResponsePayload | undefined>\n}\n\nexport interface Services {\n formsService: FormsService\n formSubmissionService: FormSubmissionService\n outputService: OutputService\n paymentService?: PaymentService\n}\n\nexport interface RouteConfig {\n formFileName?: string\n formFilePath?: string\n enforceCsrf?: boolean\n services?: Services\n controllers?: Record<string, typeof PageController>\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n onRequest?: OnRequestCallback\n saveAndExit?: PluginOptions['saveAndExit']\n cacheServiceCreator?: (server: Server) => CacheService\n ordnanceSurveyApiKey?: string\n ordnanceSurveyApiSecret?: string\n getLanguage?: GetLanguageHandler\n}\n\nexport interface OutputService {\n submit: (\n context: FormContext,\n request: FormRequestPayload,\n model: FormModel,\n emailAddress: string,\n items: DetailItem[],\n submitResponse: SubmitResponsePayload,\n language: string,\n formMetadata?: FormMetadata\n ) => Promise<void>\n}\n"],"mappings":"","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defra/forms-engine-plugin",
3
- "version": "5.0.0-alpha.5",
3
+ "version": "5.0.0-alpha.6",
4
4
  "description": "Defra forms engine",
5
5
  "type": "module",
6
6
  "files": [
@@ -87,7 +87,7 @@
87
87
  },
88
88
  "license": "SEE LICENSE IN LICENSE",
89
89
  "dependencies": {
90
- "@defra/forms-model": "^3.0.687",
90
+ "@defra/forms-model": "^3.0.689",
91
91
  "@defra/hapi-tracing": "^1.29.0",
92
92
  "@defra/interactive-map": "^0.0.33-alpha",
93
93
  "@elastic/ecs-pino-format": "^1.5.0",
@@ -5,3 +5,4 @@ export const EXTERNAL_STATE_PAYLOAD = 'EXTERNAL_STATE_PAYLOAD'
5
5
  export const EXTERNAL_STATE_APPENDAGE = 'EXTERNAL_STATE_APPENDAGE'
6
6
  export const COMPONENT_STATE_ERROR = 'COMPONENT_STATE_ERROR'
7
7
  export const PAYMENT_EXPIRED_NOTIFICATION = 'PAYMENT_EXPIRED_NOTIFICATION'
8
+ export const EN_GB = 'en-GB'
@@ -28,6 +28,7 @@ export class ComponentBase {
28
28
 
29
29
  isFormComponent = false
30
30
  model: FormModel
31
+ def: ComponentDef
31
32
 
32
33
  /** joi schemas based on a component defined in the form JSON. This validates a user's answer and is generated from {@link ComponentDef} */
33
34
  formSchema: ComponentSchema = joi.string()
@@ -45,6 +46,7 @@ export class ComponentBase {
45
46
  this.type = def.type
46
47
  this.name = def.name
47
48
  this.title = def.title
49
+ this.def = def
48
50
 
49
51
  if ('schema' in def) {
50
52
  this.schema = def.schema
@@ -308,12 +308,11 @@ export class ComponentCollection {
308
308
  labelOverrides[subField.name] = patchedSchema
309
309
  }
310
310
  } else {
311
+ const fieldDef = field.def
311
312
  const translatedLabel =
312
- translator.tComponent(
313
- field as unknown as ComponentDef,
314
- 'shortDescription'
315
- ) ||
316
- translator.tComponent(field as unknown as ComponentDef, 'title')
313
+ translator.tComponent(fieldDef, 'errorDescription') ||
314
+ translator.tComponent(fieldDef, 'shortDescription') ||
315
+ translator.tComponent(fieldDef, 'title')
317
316
  const messagesOverride =
318
317
  field.getValidationMessagesOverride(translator)
319
318
  let patchedSchema = field.formSchema
@@ -2,7 +2,6 @@ import {
2
2
  ComponentType,
3
3
  hasFormComponents,
4
4
  isFormType,
5
- type ComponentDef,
6
5
  type DeclarationFieldComponent,
7
6
  type Item
8
7
  } from '@defra/forms-model'
@@ -148,7 +147,7 @@ export class DeclarationField extends FormComponent {
148
147
  'components.declarationField.defaultLabel'
149
148
  )
150
149
  } = this
151
- const content = tComponent(this as unknown as ComponentDef, 'content')
150
+ const content = tComponent(this.def, 'content')
152
151
 
153
152
  const viewModel = super.getViewModel(context)
154
153
  let { fieldset, label } = viewModel
@@ -1,5 +1,4 @@
1
1
  import {
2
- type ComponentDef,
3
2
  type FormComponentsDef,
4
3
  type FormMetadata,
5
4
  type Item
@@ -178,15 +177,14 @@ export class FormComponent extends ComponentBase {
178
177
  const isRequired = !('required' in options) || options.required !== false
179
178
  const hideOptional = 'optionalText' in options && options.optionalText
180
179
 
181
- const resolvedTitle =
182
- tComponent(this as unknown as ComponentDef, 'title') || title
180
+ const resolvedTitle = tComponent(this.def, 'title') || title
183
181
  const optionalTag =
184
182
  !isRequired && !hideOptional ? ` ${t('common.optional')}` : ''
185
183
  const label = `${resolvedTitle}${optionalTag}`
186
184
 
187
185
  if (hint) {
188
186
  viewModel.hint = {
189
- text: tComponent(this as unknown as ComponentDef, 'hint') || hint
187
+ text: tComponent(this.def, 'hint') || hint
190
188
  }
191
189
  }
192
190
 
@@ -1,4 +1,4 @@
1
- import { type ComponentDef, type MarkdownComponent } from '@defra/forms-model'
1
+ import { type MarkdownComponent } from '@defra/forms-model'
2
2
 
3
3
  import { ComponentBase } from './ComponentBase.js'
4
4
  import { type RenderContext } from './types.js'
@@ -28,8 +28,7 @@ export class Markdown extends ComponentBase {
28
28
 
29
29
  return {
30
30
  ...viewModel,
31
- content:
32
- tComponent(this as unknown as ComponentDef, 'content') || content,
31
+ content: tComponent(this.def, 'content') || content,
33
32
  headerStartLevel: this.headerStartLevel
34
33
  }
35
34
  }
@@ -22,6 +22,7 @@ export type FormDefinitionTranslations = Record<
22
22
  hint: string
23
23
  content: string
24
24
  shortDescription: string
25
+ errorDescription: string
25
26
  paymentDescription: string
26
27
  }>
27
28
  >
@@ -212,12 +212,12 @@ function ItemRepeat(
212
212
  translator: Translator
213
213
  ): DetailItemRepeat {
214
214
  const { collection, repeat } = page
215
- const { name, title } = repeat.options
216
-
215
+ const { name } = repeat.options
216
+ const { t, tPage } = translator
217
+ const title = tPage(page.pageDef, 'repeatTitle') || repeat.options.title
217
218
  const values = page.getListFromState(state)
218
219
  const count = values.length
219
- const value =
220
- count === 0 ? '' : translator.t('pages.repeater.pageTitle', { count })
220
+ const value = count === 0 ? '' : t('pages.repeater.pageTitle', { count })
221
221
 
222
222
  return {
223
223
  name,
@@ -3,6 +3,7 @@ import {
3
3
  type SubmitResponsePayload
4
4
  } from '@defra/forms-model'
5
5
 
6
+ import { EN_GB } from '../../../../constants.js'
6
7
  import {
7
8
  getFormVersion,
8
9
  type checkFormStatus
@@ -42,18 +43,14 @@ export function format(
42
43
  formSlug: formMetadata?.slug ?? '',
43
44
  status: formStatus.state,
44
45
  isPreview: formStatus.isPreview,
45
- notificationEmail: formMetadata?.notificationEmail ?? ''
46
+ notificationEmail: formMetadata?.notificationEmail ?? '',
47
+ language: context.translator?.language ?? EN_GB
46
48
  }
47
49
 
48
50
  if (versionMetadata) {
49
51
  meta.versionMetadata = versionMetadata
50
52
  }
51
53
 
52
- const definitionLanguage = model.def.metadata?.language
53
- if (typeof definitionLanguage === 'string') {
54
- meta.language = definitionLanguage
55
- }
56
-
57
54
  const main = Object.fromEntries(
58
55
  Object.entries(v2Main).map(([key, value]) => {
59
56
  if (value === undefined) {
@@ -406,9 +406,9 @@ export class RepeatPageController extends QuestionPageController {
406
406
  const { query } = request
407
407
  const { isForceAccess, errors } = context
408
408
 
409
- const { t } = translator
409
+ const { t, tPage } = translator
410
410
 
411
- const { title } = repeat.options
411
+ const title = tPage(this.pageDef, 'repeatTitle') || repeat.options.title
412
412
 
413
413
  const summaryList: SummaryList = {
414
414
  classes: 'govuk-summary-list--long-actions',