@defra/forms-engine-plugin 4.0.43 → 4.0.44

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 (142) hide show
  1. package/.public/stylesheets/application.min.css +1 -1
  2. package/.public/stylesheets/application.min.css.map +1 -1
  3. package/.server/client/stylesheets/_payment-field.scss +8 -0
  4. package/.server/client/stylesheets/application.scss +2 -0
  5. package/.server/index.js +3 -1
  6. package/.server/index.js.map +1 -1
  7. package/.server/server/constants.d.ts +1 -0
  8. package/.server/server/constants.js +1 -0
  9. package/.server/server/constants.js.map +1 -1
  10. package/.server/server/forms/payment-test.yaml +42 -0
  11. package/.server/server/forms/register-as-a-unicorn-breeder.yaml +14 -0
  12. package/.server/server/plugins/engine/components/FormComponent.d.ts +1 -0
  13. package/.server/server/plugins/engine/components/FormComponent.js +1 -0
  14. package/.server/server/plugins/engine/components/FormComponent.js.map +1 -1
  15. package/.server/server/plugins/engine/components/PaymentField.d.ts +135 -0
  16. package/.server/server/plugins/engine/components/PaymentField.js +228 -0
  17. package/.server/server/plugins/engine/components/PaymentField.js.map +1 -0
  18. package/.server/server/plugins/engine/components/PaymentField.types.d.ts +21 -0
  19. package/.server/server/plugins/engine/components/PaymentField.types.js +2 -0
  20. package/.server/server/plugins/engine/components/PaymentField.types.js.map +1 -0
  21. package/.server/server/plugins/engine/components/UkAddressField.d.ts +1 -1
  22. package/.server/server/plugins/engine/components/UkAddressField.js +3 -1
  23. package/.server/server/plugins/engine/components/UkAddressField.js.map +1 -1
  24. package/.server/server/plugins/engine/components/helpers/components.d.ts +1 -1
  25. package/.server/server/plugins/engine/components/helpers/components.js +3 -0
  26. package/.server/server/plugins/engine/components/helpers/components.js.map +1 -1
  27. package/.server/server/plugins/engine/components/index.d.ts +1 -0
  28. package/.server/server/plugins/engine/components/index.js +1 -0
  29. package/.server/server/plugins/engine/components/index.js.map +1 -1
  30. package/.server/server/plugins/engine/helpers.d.ts +1 -0
  31. package/.server/server/plugins/engine/models/SummaryViewModel.d.ts +3 -0
  32. package/.server/server/plugins/engine/models/SummaryViewModel.js +7 -0
  33. package/.server/server/plugins/engine/models/SummaryViewModel.js.map +1 -1
  34. package/.server/server/plugins/engine/outputFormatters/human/v1.js +34 -1
  35. package/.server/server/plugins/engine/outputFormatters/human/v1.js.map +1 -1
  36. package/.server/server/plugins/engine/outputFormatters/machine/v2.d.ts +22 -0
  37. package/.server/server/plugins/engine/outputFormatters/machine/v2.js +43 -1
  38. package/.server/server/plugins/engine/outputFormatters/machine/v2.js.map +1 -1
  39. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +29 -8
  40. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
  41. package/.server/server/plugins/engine/pageControllers/StartPageController.d.ts +2 -0
  42. package/.server/server/plugins/engine/pageControllers/SummaryPageController.d.ts +17 -0
  43. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +173 -51
  44. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
  45. package/.server/server/plugins/engine/pageControllers/errors.d.ts +31 -0
  46. package/.server/server/plugins/engine/pageControllers/errors.js +59 -2
  47. package/.server/server/plugins/engine/pageControllers/errors.js.map +1 -1
  48. package/.server/server/plugins/engine/pageControllers/helpers/submission.d.ts +27 -0
  49. package/.server/server/plugins/engine/pageControllers/helpers/submission.js +77 -0
  50. package/.server/server/plugins/engine/pageControllers/helpers/submission.js.map +1 -0
  51. package/.server/server/plugins/engine/plugin.js +4 -1
  52. package/.server/server/plugins/engine/plugin.js.map +1 -1
  53. package/.server/server/plugins/engine/routes/index.js +8 -4
  54. package/.server/server/plugins/engine/routes/index.js.map +1 -1
  55. package/.server/server/plugins/engine/routes/payment-helper.d.ts +14 -0
  56. package/.server/server/plugins/engine/routes/payment-helper.js +41 -0
  57. package/.server/server/plugins/engine/routes/payment-helper.js.map +1 -0
  58. package/.server/server/plugins/engine/routes/payment-helper.test.js +81 -0
  59. package/.server/server/plugins/engine/routes/payment-helper.test.js.map +1 -0
  60. package/.server/server/plugins/engine/routes/payment.d.ts +8 -0
  61. package/.server/server/plugins/engine/routes/payment.js +140 -0
  62. package/.server/server/plugins/engine/routes/payment.js.map +1 -0
  63. package/.server/server/plugins/engine/routes/payment.test.js +187 -0
  64. package/.server/server/plugins/engine/routes/payment.test.js.map +1 -0
  65. package/.server/server/plugins/engine/services/localFormsService.js +6 -0
  66. package/.server/server/plugins/engine/services/localFormsService.js.map +1 -1
  67. package/.server/server/plugins/engine/types/schema.js +7 -0
  68. package/.server/server/plugins/engine/types/schema.js.map +1 -1
  69. package/.server/server/plugins/engine/types.d.ts +19 -1
  70. package/.server/server/plugins/engine/types.js +4 -0
  71. package/.server/server/plugins/engine/types.js.map +1 -1
  72. package/.server/server/plugins/engine/validationHelpers.d.ts +1 -1
  73. package/.server/server/plugins/engine/validationHelpers.js.map +1 -1
  74. package/.server/server/plugins/engine/views/components/paymentfield.html +42 -0
  75. package/.server/server/plugins/engine/views/index.html +9 -1
  76. package/.server/server/plugins/engine/views/partials/form.html +20 -5
  77. package/.server/server/plugins/engine/views/summary.html +17 -1
  78. package/.server/server/plugins/nunjucks/filters/field.d.ts +1 -1
  79. package/.server/server/plugins/payment/helper.d.ts +30 -0
  80. package/.server/server/plugins/payment/helper.js +49 -0
  81. package/.server/server/plugins/payment/helper.js.map +1 -0
  82. package/.server/server/plugins/payment/helper.test.js +37 -0
  83. package/.server/server/plugins/payment/helper.test.js.map +1 -0
  84. package/.server/server/plugins/payment/service.d.ts +40 -0
  85. package/.server/server/plugins/payment/service.js +129 -0
  86. package/.server/server/plugins/payment/service.js.map +1 -0
  87. package/.server/server/plugins/payment/service.test.js +162 -0
  88. package/.server/server/plugins/payment/service.test.js.map +1 -0
  89. package/.server/server/plugins/payment/types.d.ts +172 -0
  90. package/.server/server/plugins/payment/types.js +78 -0
  91. package/.server/server/plugins/payment/types.js.map +1 -0
  92. package/.server/server/types.d.ts +2 -0
  93. package/.server/server/types.js.map +1 -1
  94. package/.server/typings/hapi/index.d.js.map +1 -1
  95. package/README.md +12 -9
  96. package/package.json +2 -2
  97. package/src/client/stylesheets/_payment-field.scss +8 -0
  98. package/src/client/stylesheets/application.scss +2 -0
  99. package/src/index.ts +5 -1
  100. package/src/server/constants.js +1 -0
  101. package/src/server/forms/payment-test.yaml +42 -0
  102. package/src/server/forms/register-as-a-unicorn-breeder.yaml +14 -0
  103. package/src/server/plugins/engine/components/FormComponent.ts +1 -0
  104. package/src/server/plugins/engine/components/PaymentField.test.ts +611 -0
  105. package/src/server/plugins/engine/components/PaymentField.ts +367 -0
  106. package/src/server/plugins/engine/components/PaymentField.types.ts +21 -0
  107. package/src/server/plugins/engine/components/UkAddressField.ts +2 -1
  108. package/src/server/plugins/engine/components/helpers/components.ts +5 -0
  109. package/src/server/plugins/engine/components/index.ts +1 -0
  110. package/src/server/plugins/engine/models/SummaryViewModel.ts +8 -0
  111. package/src/server/plugins/engine/outputFormatters/human/v1.payment.test.ts +147 -0
  112. package/src/server/plugins/engine/outputFormatters/human/v1.test.ts +105 -103
  113. package/src/server/plugins/engine/outputFormatters/human/v1.ts +61 -2
  114. package/src/server/plugins/engine/outputFormatters/machine/v2.payment.test.ts +115 -0
  115. package/src/server/plugins/engine/outputFormatters/machine/v2.ts +60 -1
  116. package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +32 -6
  117. package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +247 -72
  118. package/src/server/plugins/engine/pageControllers/errors.test.ts +13 -1
  119. package/src/server/plugins/engine/pageControllers/errors.ts +79 -4
  120. package/src/server/plugins/engine/pageControllers/helpers/submission.test.ts +299 -0
  121. package/src/server/plugins/engine/pageControllers/helpers/submission.ts +110 -0
  122. package/src/server/plugins/engine/plugin.ts +11 -6
  123. package/src/server/plugins/engine/routes/index.ts +17 -16
  124. package/src/server/plugins/engine/routes/payment-helper.js +39 -0
  125. package/src/server/plugins/engine/routes/payment-helper.test.js +90 -0
  126. package/src/server/plugins/engine/routes/payment.js +151 -0
  127. package/src/server/plugins/engine/routes/payment.test.js +180 -0
  128. package/src/server/plugins/engine/services/localFormsService.js +7 -0
  129. package/src/server/plugins/engine/types/schema.ts +9 -0
  130. package/src/server/plugins/engine/types.ts +24 -1
  131. package/src/server/plugins/engine/validationHelpers.ts +1 -1
  132. package/src/server/plugins/engine/views/components/paymentfield.html +42 -0
  133. package/src/server/plugins/engine/views/index.html +9 -1
  134. package/src/server/plugins/engine/views/partials/form.html +20 -5
  135. package/src/server/plugins/engine/views/summary.html +17 -1
  136. package/src/server/plugins/payment/helper.js +56 -0
  137. package/src/server/plugins/payment/helper.test.js +52 -0
  138. package/src/server/plugins/payment/service.js +171 -0
  139. package/src/server/plugins/payment/service.test.js +205 -0
  140. package/src/server/plugins/payment/types.js +77 -0
  141. package/src/server/types.ts +2 -0
  142. package/src/typings/hapi/index.d.ts +1 -0
@@ -0,0 +1,187 @@
1
+ import { StatusCodes } from 'http-status-codes';
2
+ import { createServer } from "../../../index.js";
3
+ import { getPaymentContext } from "./payment-helper.js";
4
+ import { renderResponse } from "../../../../../test/helpers/component-helpers.js";
5
+ jest.mock("./payment-helper.js");
6
+ describe('Payment routes', () => {
7
+ /** @type {Server} */
8
+ let server;
9
+ beforeAll(async () => {
10
+ server = await createServer();
11
+ await server.initialize();
12
+ });
13
+ beforeEach(() => {
14
+ jest.resetAllMocks();
15
+ });
16
+ describe('Return route /payment-callback', () => {
17
+ const uuid = '06a5b11e-e3e0-48a2-8ac3-56c0fcb6c20d';
18
+ const options = {
19
+ method: 'get',
20
+ url: `/payment-callback?uuid=${uuid}`
21
+ };
22
+ const paymentSessionData = {
23
+ uuid,
24
+ formId: 'form-id',
25
+ reference: 'form-ref-123',
26
+ paymentId: 'payment-id',
27
+ amount: 123,
28
+ description: 'Payment desc',
29
+ isLivePayment: false,
30
+ componentName: 'my-component',
31
+ returnUrl: 'http://host.com/return-url',
32
+ failureUrl: 'http://host.com/failure-url'
33
+ };
34
+ const sessionKey = 'session-key';
35
+ test.each([{
36
+ status: 'capturable',
37
+ finalUrl: 'http://host.com/return-url'
38
+ }, {
39
+ status: 'success',
40
+ finalUrl: 'http://host.com/return-url'
41
+ }, {
42
+ status: 'cancelled',
43
+ finalUrl: 'http://host.com/failure-url'
44
+ }, {
45
+ status: 'failed',
46
+ finalUrl: 'http://host.com/failure-url'
47
+ }, {
48
+ status: 'error',
49
+ finalUrl: 'http://host.com/failure-url'
50
+ }, {
51
+ status: 'created',
52
+ finalUrl: '/next-url'
53
+ }, {
54
+ status: 'started',
55
+ finalUrl: '/next-url'
56
+ }, {
57
+ status: 'submitted',
58
+ finalUrl: '/next-url'
59
+ }])('should handle payment status of $row.status', async row => {
60
+ const paymentStatus = {
61
+ paymentId: 'new-payment-id',
62
+ amount: 125,
63
+ _links: {
64
+ next_url: {
65
+ href: '/next-url',
66
+ method: 'get'
67
+ },
68
+ self: {
69
+ href: '/self',
70
+ method: 'get'
71
+ }
72
+ },
73
+ state: (/** @type {PaymentResponseState} */{
74
+ status: row.status,
75
+ finished: true
76
+ })
77
+ };
78
+ jest.mocked(getPaymentContext).mockResolvedValueOnce({
79
+ session: paymentSessionData,
80
+ sessionKey,
81
+ paymentStatus
82
+ });
83
+ const {
84
+ response
85
+ } = await renderResponse(server, options);
86
+ expect(response.statusCode).toBe(StatusCodes.SEE_OTHER);
87
+ expect(response.headers.location).toBe(row.finalUrl);
88
+ });
89
+ it('should throw if nextUrl is missing', async () => {
90
+ const paymentStatus = {
91
+ paymentId: 'new-payment-id',
92
+ _links: {
93
+ next_url: {},
94
+ self: {
95
+ href: '/self',
96
+ method: 'get'
97
+ }
98
+ },
99
+ state: (/** @type {PaymentResponseState} */{
100
+ status: 'created',
101
+ finished: true
102
+ })
103
+ };
104
+ jest.mocked(getPaymentContext).mockResolvedValueOnce({
105
+ session: paymentSessionData,
106
+ sessionKey,
107
+ // @ts-expect-error - deliberate missing element from object
108
+ paymentStatus
109
+ });
110
+ const {
111
+ response
112
+ } = await renderResponse(server, options);
113
+ expect(response.statusCode).toBe(StatusCodes.BAD_REQUEST);
114
+ // @ts-expect-error - error object
115
+ expect(response.result?.message).toBe("Payment in state 'created' but no next_url available");
116
+ });
117
+ it('should throw if invalid status', async () => {
118
+ const paymentStatus = {
119
+ paymentId: 'new-payment-id',
120
+ _links: {
121
+ next_url: {
122
+ href: '/next-url',
123
+ method: 'get'
124
+ },
125
+ self: {
126
+ href: '/self',
127
+ method: 'get'
128
+ }
129
+ },
130
+ state: {
131
+ status: 'invalid',
132
+ finished: true
133
+ }
134
+ };
135
+ jest.mocked(getPaymentContext).mockResolvedValueOnce({
136
+ session: paymentSessionData,
137
+ sessionKey,
138
+ // @ts-expect-error - deliberate invalid value which doesnt meet type
139
+ paymentStatus
140
+ });
141
+ const {
142
+ response
143
+ } = await renderResponse(server, options);
144
+ expect(response.statusCode).toBe(StatusCodes.INTERNAL_SERVER_ERROR);
145
+ // @ts-expect-error - error object
146
+ expect(response.result?.message).toBe('Unknown payment status: invalid');
147
+ });
148
+ it('should handle payment with email from GOV.UK Pay response', async () => {
149
+ const paymentStatus = {
150
+ paymentId: 'new-payment-id',
151
+ payment_id: 'new-payment-id',
152
+ amount: 125,
153
+ email: 'payer@example.com',
154
+ _links: {
155
+ next_url: {
156
+ href: '/next-url',
157
+ method: 'get'
158
+ },
159
+ self: {
160
+ href: '/self',
161
+ method: 'get'
162
+ }
163
+ },
164
+ state: (/** @type {PaymentResponseState} */{
165
+ status: 'success',
166
+ finished: true
167
+ })
168
+ };
169
+ jest.mocked(getPaymentContext).mockResolvedValueOnce({
170
+ session: paymentSessionData,
171
+ sessionKey,
172
+ paymentStatus
173
+ });
174
+ const {
175
+ response
176
+ } = await renderResponse(server, options);
177
+ expect(response.statusCode).toBe(StatusCodes.SEE_OTHER);
178
+ expect(response.headers.location).toBe('http://host.com/return-url');
179
+ });
180
+ });
181
+ });
182
+
183
+ /**
184
+ * @import { Server } from '@hapi/hapi'
185
+ * @import { PaymentResponseState } from '~/src/server/plugins/payment/types.js'
186
+ */
187
+ //# sourceMappingURL=payment.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payment.test.js","names":["StatusCodes","createServer","getPaymentContext","renderResponse","jest","mock","describe","server","beforeAll","initialize","beforeEach","resetAllMocks","uuid","options","method","url","paymentSessionData","formId","reference","paymentId","amount","description","isLivePayment","componentName","returnUrl","failureUrl","sessionKey","test","each","status","finalUrl","row","paymentStatus","_links","next_url","href","self","state","finished","mocked","mockResolvedValueOnce","session","response","expect","statusCode","toBe","SEE_OTHER","headers","location","it","BAD_REQUEST","result","message","INTERNAL_SERVER_ERROR","payment_id","email"],"sources":["../../../../../src/server/plugins/engine/routes/payment.test.js"],"sourcesContent":["import { StatusCodes } from 'http-status-codes'\n\nimport { createServer } from '~/src/server/index.js'\nimport { getPaymentContext } from '~/src/server/plugins/engine/routes/payment-helper.js'\nimport { renderResponse } from '~/test/helpers/component-helpers.js'\n\njest.mock('~/src/server/plugins/engine/routes/payment-helper.js')\n\ndescribe('Payment routes', () => {\n /** @type {Server} */\n let server\n\n beforeAll(async () => {\n server = await createServer()\n await server.initialize()\n })\n\n beforeEach(() => {\n jest.resetAllMocks()\n })\n\n describe('Return route /payment-callback', () => {\n const uuid = '06a5b11e-e3e0-48a2-8ac3-56c0fcb6c20d'\n const options = {\n method: 'get',\n url: `/payment-callback?uuid=${uuid}`\n }\n\n const paymentSessionData = {\n uuid,\n formId: 'form-id',\n reference: 'form-ref-123',\n paymentId: 'payment-id',\n amount: 123,\n description: 'Payment desc',\n isLivePayment: false,\n componentName: 'my-component',\n returnUrl: 'http://host.com/return-url',\n failureUrl: 'http://host.com/failure-url'\n }\n const sessionKey = 'session-key'\n\n test.each([\n { status: 'capturable', finalUrl: 'http://host.com/return-url' },\n { status: 'success', finalUrl: 'http://host.com/return-url' },\n { status: 'cancelled', finalUrl: 'http://host.com/failure-url' },\n { status: 'failed', finalUrl: 'http://host.com/failure-url' },\n { status: 'error', finalUrl: 'http://host.com/failure-url' },\n { status: 'created', finalUrl: '/next-url' },\n { status: 'started', finalUrl: '/next-url' },\n { status: 'submitted', finalUrl: '/next-url' }\n ])('should handle payment status of $row.status', async (row) => {\n const paymentStatus = {\n paymentId: 'new-payment-id',\n amount: 125,\n _links: {\n next_url: {\n href: '/next-url',\n method: 'get'\n },\n self: {\n href: '/self',\n method: 'get'\n }\n },\n state: /** @type {PaymentResponseState} */ ({\n status: row.status,\n finished: true\n })\n }\n jest.mocked(getPaymentContext).mockResolvedValueOnce({\n session: paymentSessionData,\n sessionKey,\n paymentStatus\n })\n const { response } = await renderResponse(server, options)\n\n expect(response.statusCode).toBe(StatusCodes.SEE_OTHER)\n expect(response.headers.location).toBe(row.finalUrl)\n })\n\n it('should throw if nextUrl is missing', async () => {\n const paymentStatus = {\n paymentId: 'new-payment-id',\n _links: {\n next_url: {},\n self: {\n href: '/self',\n method: 'get'\n }\n },\n state: /** @type {PaymentResponseState} */ ({\n status: 'created',\n finished: true\n })\n }\n jest.mocked(getPaymentContext).mockResolvedValueOnce({\n session: paymentSessionData,\n sessionKey,\n // @ts-expect-error - deliberate missing element from object\n paymentStatus\n })\n const { response } = await renderResponse(server, options)\n\n expect(response.statusCode).toBe(StatusCodes.BAD_REQUEST)\n // @ts-expect-error - error object\n expect(response.result?.message).toBe(\n \"Payment in state 'created' but no next_url available\"\n )\n })\n\n it('should throw if invalid status', async () => {\n const paymentStatus = {\n paymentId: 'new-payment-id',\n _links: {\n next_url: {\n href: '/next-url',\n method: 'get'\n },\n self: {\n href: '/self',\n method: 'get'\n }\n },\n state: {\n status: 'invalid',\n finished: true\n }\n }\n jest.mocked(getPaymentContext).mockResolvedValueOnce({\n session: paymentSessionData,\n sessionKey,\n // @ts-expect-error - deliberate invalid value which doesnt meet type\n paymentStatus\n })\n const { response } = await renderResponse(server, options)\n\n expect(response.statusCode).toBe(StatusCodes.INTERNAL_SERVER_ERROR)\n // @ts-expect-error - error object\n expect(response.result?.message).toBe('Unknown payment status: invalid')\n })\n\n it('should handle payment with email from GOV.UK Pay response', async () => {\n const paymentStatus = {\n paymentId: 'new-payment-id',\n payment_id: 'new-payment-id',\n amount: 125,\n email: 'payer@example.com',\n _links: {\n next_url: {\n href: '/next-url',\n method: 'get'\n },\n self: {\n href: '/self',\n method: 'get'\n }\n },\n state: /** @type {PaymentResponseState} */ ({\n status: 'success',\n finished: true\n })\n }\n jest.mocked(getPaymentContext).mockResolvedValueOnce({\n session: paymentSessionData,\n sessionKey,\n paymentStatus\n })\n const { response } = await renderResponse(server, options)\n\n expect(response.statusCode).toBe(StatusCodes.SEE_OTHER)\n expect(response.headers.location).toBe('http://host.com/return-url')\n })\n })\n})\n\n/**\n * @import { Server } from '@hapi/hapi'\n * @import { PaymentResponseState } from '~/src/server/plugins/payment/types.js'\n */\n"],"mappings":"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","ignoreList":[]}
@@ -57,6 +57,12 @@ export const formsService = async () => {
57
57
  title: 'Simple Form',
58
58
  slug: 'simple-form'
59
59
  });
60
+ await loader.addForm('src/server/forms/payment-test.yaml', {
61
+ ...metadata,
62
+ id: 'b2c3d4e5-f6a7-8901-bcde-f01234567890',
63
+ title: 'Payment Test Form',
64
+ slug: 'payment-test'
65
+ });
60
66
  return loader.toFormsService();
61
67
  };
62
68
  //# sourceMappingURL=localFormsService.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"localFormsService.js","names":["config","FileFormService","now","Date","user","id","displayName","author","createdAt","createdBy","updatedAt","updatedBy","metadata","organisation","teamName","teamEmail","submissionGuidance","notificationEmail","get","live","formsService","loader","addForm","title","slug","toFormsService"],"sources":["../../../../../src/server/plugins/engine/services/localFormsService.js"],"sourcesContent":["import { config } from '~/src/config/index.js'\nimport { FileFormService } from '~/src/server/utils/file-form-service.js'\n\n// Create shared form metadata\nconst now = new Date()\nconst user = { id: 'user', displayName: 'Username' }\nconst author = {\n createdAt: now,\n createdBy: user,\n updatedAt: now,\n updatedBy: user\n}\nconst metadata = {\n organisation: 'Defra',\n teamName: 'Team name',\n teamEmail: 'team@defra.gov.uk',\n submissionGuidance: \"Thanks for your submission, we'll be in touch\",\n notificationEmail: config.get('submissionEmailAddress'),\n ...author,\n live: author\n}\n\n/**\n * Return an function rather than the service directly. This is to prevent consumer applications\n * blowing up as they won't have these files on disk. We can defer the execution until when it's\n * needed, i.e. the createServer function of the devtool.\n */\nexport const formsService = async () => {\n // Instantiate the file loader form service\n const loader = new FileFormService()\n\n // Add a Yaml form\n await loader.addForm('src/server/forms/register-as-a-unicorn-breeder.yaml', {\n ...metadata,\n id: '641aeafd-13dd-40fa-9186-001703800efb',\n title: 'Register as a unicorn breeder',\n slug: 'register-as-a-unicorn-breeder'\n })\n\n await loader.addForm('src/server/forms/page-events.yaml', {\n ...metadata,\n id: '511db05e-ebbd-42e8-8270-5fe93f5c9762',\n title: 'Page events demo',\n slug: 'page-events-demo'\n })\n\n await loader.addForm('src/server/forms/components.json', {\n ...metadata,\n id: '6a872d3b-13f9e-804ce3e-4830-5c45fb32',\n title: 'Components',\n slug: 'components'\n })\n\n await loader.addForm('src/server/forms/simple-form.yaml', {\n ...metadata,\n id: 'a1b2c3d4-e5f6-7890-abcd-ef0123456789',\n title: 'Simple Form',\n slug: 'simple-form'\n })\n\n return loader.toFormsService()\n}\n"],"mappings":"AAAA,SAASA,MAAM;AACf,SAASC,eAAe;;AAExB;AACA,MAAMC,GAAG,GAAG,IAAIC,IAAI,CAAC,CAAC;AACtB,MAAMC,IAAI,GAAG;EAAEC,EAAE,EAAE,MAAM;EAAEC,WAAW,EAAE;AAAW,CAAC;AACpD,MAAMC,MAAM,GAAG;EACbC,SAAS,EAAEN,GAAG;EACdO,SAAS,EAAEL,IAAI;EACfM,SAAS,EAAER,GAAG;EACdS,SAAS,EAAEP;AACb,CAAC;AACD,MAAMQ,QAAQ,GAAG;EACfC,YAAY,EAAE,OAAO;EACrBC,QAAQ,EAAE,WAAW;EACrBC,SAAS,EAAE,mBAAmB;EAC9BC,kBAAkB,EAAE,+CAA+C;EACnEC,iBAAiB,EAAEjB,MAAM,CAACkB,GAAG,CAAC,wBAAwB,CAAC;EACvD,GAAGX,MAAM;EACTY,IAAI,EAAEZ;AACR,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMa,YAAY,GAAG,MAAAA,CAAA,KAAY;EACtC;EACA,MAAMC,MAAM,GAAG,IAAIpB,eAAe,CAAC,CAAC;;EAEpC;EACA,MAAMoB,MAAM,CAACC,OAAO,CAAC,qDAAqD,EAAE;IAC1E,GAAGV,QAAQ;IACXP,EAAE,EAAE,sCAAsC;IAC1CkB,KAAK,EAAE,+BAA+B;IACtCC,IAAI,EAAE;EACR,CAAC,CAAC;EAEF,MAAMH,MAAM,CAACC,OAAO,CAAC,mCAAmC,EAAE;IACxD,GAAGV,QAAQ;IACXP,EAAE,EAAE,sCAAsC;IAC1CkB,KAAK,EAAE,kBAAkB;IACzBC,IAAI,EAAE;EACR,CAAC,CAAC;EAEF,MAAMH,MAAM,CAACC,OAAO,CAAC,kCAAkC,EAAE;IACvD,GAAGV,QAAQ;IACXP,EAAE,EAAE,sCAAsC;IAC1CkB,KAAK,EAAE,YAAY;IACnBC,IAAI,EAAE;EACR,CAAC,CAAC;EAEF,MAAMH,MAAM,CAACC,OAAO,CAAC,mCAAmC,EAAE;IACxD,GAAGV,QAAQ;IACXP,EAAE,EAAE,sCAAsC;IAC1CkB,KAAK,EAAE,aAAa;IACpBC,IAAI,EAAE;EACR,CAAC,CAAC;EAEF,OAAOH,MAAM,CAACI,cAAc,CAAC,CAAC;AAChC,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"localFormsService.js","names":["config","FileFormService","now","Date","user","id","displayName","author","createdAt","createdBy","updatedAt","updatedBy","metadata","organisation","teamName","teamEmail","submissionGuidance","notificationEmail","get","live","formsService","loader","addForm","title","slug","toFormsService"],"sources":["../../../../../src/server/plugins/engine/services/localFormsService.js"],"sourcesContent":["import { config } from '~/src/config/index.js'\nimport { FileFormService } from '~/src/server/utils/file-form-service.js'\n\n// Create shared form metadata\nconst now = new Date()\nconst user = { id: 'user', displayName: 'Username' }\nconst author = {\n createdAt: now,\n createdBy: user,\n updatedAt: now,\n updatedBy: user\n}\nconst metadata = {\n organisation: 'Defra',\n teamName: 'Team name',\n teamEmail: 'team@defra.gov.uk',\n submissionGuidance: \"Thanks for your submission, we'll be in touch\",\n notificationEmail: config.get('submissionEmailAddress'),\n ...author,\n live: author\n}\n\n/**\n * Return an function rather than the service directly. This is to prevent consumer applications\n * blowing up as they won't have these files on disk. We can defer the execution until when it's\n * needed, i.e. the createServer function of the devtool.\n */\nexport const formsService = async () => {\n // Instantiate the file loader form service\n const loader = new FileFormService()\n\n // Add a Yaml form\n await loader.addForm('src/server/forms/register-as-a-unicorn-breeder.yaml', {\n ...metadata,\n id: '641aeafd-13dd-40fa-9186-001703800efb',\n title: 'Register as a unicorn breeder',\n slug: 'register-as-a-unicorn-breeder'\n })\n\n await loader.addForm('src/server/forms/page-events.yaml', {\n ...metadata,\n id: '511db05e-ebbd-42e8-8270-5fe93f5c9762',\n title: 'Page events demo',\n slug: 'page-events-demo'\n })\n\n await loader.addForm('src/server/forms/components.json', {\n ...metadata,\n id: '6a872d3b-13f9e-804ce3e-4830-5c45fb32',\n title: 'Components',\n slug: 'components'\n })\n\n await loader.addForm('src/server/forms/simple-form.yaml', {\n ...metadata,\n id: 'a1b2c3d4-e5f6-7890-abcd-ef0123456789',\n title: 'Simple Form',\n slug: 'simple-form'\n })\n\n await loader.addForm('src/server/forms/payment-test.yaml', {\n ...metadata,\n id: 'b2c3d4e5-f6a7-8901-bcde-f01234567890',\n title: 'Payment Test Form',\n slug: 'payment-test'\n })\n\n return loader.toFormsService()\n}\n"],"mappings":"AAAA,SAASA,MAAM;AACf,SAASC,eAAe;;AAExB;AACA,MAAMC,GAAG,GAAG,IAAIC,IAAI,CAAC,CAAC;AACtB,MAAMC,IAAI,GAAG;EAAEC,EAAE,EAAE,MAAM;EAAEC,WAAW,EAAE;AAAW,CAAC;AACpD,MAAMC,MAAM,GAAG;EACbC,SAAS,EAAEN,GAAG;EACdO,SAAS,EAAEL,IAAI;EACfM,SAAS,EAAER,GAAG;EACdS,SAAS,EAAEP;AACb,CAAC;AACD,MAAMQ,QAAQ,GAAG;EACfC,YAAY,EAAE,OAAO;EACrBC,QAAQ,EAAE,WAAW;EACrBC,SAAS,EAAE,mBAAmB;EAC9BC,kBAAkB,EAAE,+CAA+C;EACnEC,iBAAiB,EAAEjB,MAAM,CAACkB,GAAG,CAAC,wBAAwB,CAAC;EACvD,GAAGX,MAAM;EACTY,IAAI,EAAEZ;AACR,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMa,YAAY,GAAG,MAAAA,CAAA,KAAY;EACtC;EACA,MAAMC,MAAM,GAAG,IAAIpB,eAAe,CAAC,CAAC;;EAEpC;EACA,MAAMoB,MAAM,CAACC,OAAO,CAAC,qDAAqD,EAAE;IAC1E,GAAGV,QAAQ;IACXP,EAAE,EAAE,sCAAsC;IAC1CkB,KAAK,EAAE,+BAA+B;IACtCC,IAAI,EAAE;EACR,CAAC,CAAC;EAEF,MAAMH,MAAM,CAACC,OAAO,CAAC,mCAAmC,EAAE;IACxD,GAAGV,QAAQ;IACXP,EAAE,EAAE,sCAAsC;IAC1CkB,KAAK,EAAE,kBAAkB;IACzBC,IAAI,EAAE;EACR,CAAC,CAAC;EAEF,MAAMH,MAAM,CAACC,OAAO,CAAC,kCAAkC,EAAE;IACvD,GAAGV,QAAQ;IACXP,EAAE,EAAE,sCAAsC;IAC1CkB,KAAK,EAAE,YAAY;IACnBC,IAAI,EAAE;EACR,CAAC,CAAC;EAEF,MAAMH,MAAM,CAACC,OAAO,CAAC,mCAAmC,EAAE;IACxD,GAAGV,QAAQ;IACXP,EAAE,EAAE,sCAAsC;IAC1CkB,KAAK,EAAE,aAAa;IACpBC,IAAI,EAAE;EACR,CAAC,CAAC;EAEF,MAAMH,MAAM,CAACC,OAAO,CAAC,oCAAoC,EAAE;IACzD,GAAGV,QAAQ;IACXP,EAAE,EAAE,sCAAsC;IAC1CkB,KAAK,EAAE,mBAAmB;IAC1BC,IAAI,EAAE;EACR,CAAC,CAAC;EAEF,OAAOH,MAAM,CAACI,cAAc,CAAC,CAAC;AAChC,CAAC","ignoreList":[]}
@@ -17,6 +17,13 @@ export const formAdapterSubmissionMessageMetaSchema = Joi.object().keys({
17
17
  export const formAdapterSubmissionMessageDataSchema = Joi.object().keys({
18
18
  main: Joi.object(),
19
19
  repeaters: Joi.object(),
20
+ payment: Joi.object().keys({
21
+ paymentId: Joi.string().required(),
22
+ reference: Joi.string().required(),
23
+ amount: Joi.number().required(),
24
+ description: Joi.string().required(),
25
+ createdAt: Joi.string().required()
26
+ }).optional(),
20
27
  files: Joi.object().pattern(Joi.string(), Joi.array().items(Joi.object().keys({
21
28
  fileName: Joi.string().required(),
22
29
  fileId: Joi.string().required(),
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","names":["FormStatus","formVersionMetadataSchema","idSchema","notificationEmailAddressSchema","slugSchema","titleSchema","Joi","FormAdapterSubmissionSchemaVersion","formAdapterSubmissionMessageMetaSchema","object","keys","schemaVersion","string","valid","Object","values","timestamp","date","required","referenceNumber","formName","formId","formSlug","status","isPreview","boolean","notificationEmail","versionMetadata","optional","custom","pattern","any","unknown","description","formAdapterSubmissionMessageDataSchema","main","repeaters","files","array","items","fileName","fileId","userDownloadLink","formAdapterSubmissionMessageResultSchema","formAdapterSubmissionMessagePayloadSchema","meta","data","result"],"sources":["../../../../../src/server/plugins/engine/types/schema.ts"],"sourcesContent":["import {\n FormStatus,\n formVersionMetadataSchema,\n idSchema,\n notificationEmailAddressSchema,\n slugSchema,\n titleSchema\n} from '@defra/forms-model'\nimport Joi from 'joi'\n\nimport { FormAdapterSubmissionSchemaVersion } from '~/src/server/plugins/engine/types/enums.js'\nimport {\n type FormAdapterSubmissionMessageData,\n type FormAdapterSubmissionMessageMeta,\n type FormAdapterSubmissionMessagePayload,\n type FormAdapterSubmissionMessageResult\n} from '~/src/server/plugins/engine/types.js'\n\nexport const formAdapterSubmissionMessageMetaSchema =\n Joi.object<FormAdapterSubmissionMessageMeta>().keys({\n schemaVersion: Joi.string().valid(\n ...Object.values(FormAdapterSubmissionSchemaVersion)\n ),\n timestamp: Joi.date().required(),\n referenceNumber: Joi.string().required(),\n formName: titleSchema,\n formId: idSchema,\n formSlug: slugSchema,\n status: Joi.string()\n .valid(...Object.values(FormStatus))\n .required(),\n isPreview: Joi.boolean().required(),\n notificationEmail: notificationEmailAddressSchema.required(),\n versionMetadata: formVersionMetadataSchema.optional(),\n custom: Joi.object()\n .pattern(/^/, Joi.any())\n .unknown()\n .optional()\n .description('Custom properties for the message')\n })\n\nexport const formAdapterSubmissionMessageDataSchema =\n Joi.object<FormAdapterSubmissionMessageData>().keys({\n main: Joi.object(),\n repeaters: Joi.object(),\n files: Joi.object().pattern(\n Joi.string(),\n Joi.array().items(\n Joi.object().keys({\n fileName: Joi.string().required(),\n fileId: Joi.string().required(),\n userDownloadLink: Joi.string().required()\n })\n )\n )\n })\n\nexport const formAdapterSubmissionMessageResultSchema =\n Joi.object<FormAdapterSubmissionMessageResult>().keys({\n files: Joi.object()\n .keys({\n main: Joi.string().required(),\n repeaters: Joi.object()\n })\n .required()\n })\n\nexport const formAdapterSubmissionMessagePayloadSchema =\n Joi.object<FormAdapterSubmissionMessagePayload>().keys({\n meta: formAdapterSubmissionMessageMetaSchema.required(),\n data: formAdapterSubmissionMessageDataSchema.required(),\n result: formAdapterSubmissionMessageResultSchema.required()\n })\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"schema.js","names":["FormStatus","formVersionMetadataSchema","idSchema","notificationEmailAddressSchema","slugSchema","titleSchema","Joi","FormAdapterSubmissionSchemaVersion","formAdapterSubmissionMessageMetaSchema","object","keys","schemaVersion","string","valid","Object","values","timestamp","date","required","referenceNumber","formName","formId","formSlug","status","isPreview","boolean","notificationEmail","versionMetadata","optional","custom","pattern","any","unknown","description","formAdapterSubmissionMessageDataSchema","main","repeaters","payment","paymentId","reference","amount","number","createdAt","files","array","items","fileName","fileId","userDownloadLink","formAdapterSubmissionMessageResultSchema","formAdapterSubmissionMessagePayloadSchema","meta","data","result"],"sources":["../../../../../src/server/plugins/engine/types/schema.ts"],"sourcesContent":["import {\n FormStatus,\n formVersionMetadataSchema,\n idSchema,\n notificationEmailAddressSchema,\n slugSchema,\n titleSchema\n} from '@defra/forms-model'\nimport Joi from 'joi'\n\nimport { FormAdapterSubmissionSchemaVersion } from '~/src/server/plugins/engine/types/enums.js'\nimport {\n type FormAdapterSubmissionMessageData,\n type FormAdapterSubmissionMessageMeta,\n type FormAdapterSubmissionMessagePayload,\n type FormAdapterSubmissionMessageResult\n} from '~/src/server/plugins/engine/types.js'\n\nexport const formAdapterSubmissionMessageMetaSchema =\n Joi.object<FormAdapterSubmissionMessageMeta>().keys({\n schemaVersion: Joi.string().valid(\n ...Object.values(FormAdapterSubmissionSchemaVersion)\n ),\n timestamp: Joi.date().required(),\n referenceNumber: Joi.string().required(),\n formName: titleSchema,\n formId: idSchema,\n formSlug: slugSchema,\n status: Joi.string()\n .valid(...Object.values(FormStatus))\n .required(),\n isPreview: Joi.boolean().required(),\n notificationEmail: notificationEmailAddressSchema.required(),\n versionMetadata: formVersionMetadataSchema.optional(),\n custom: Joi.object()\n .pattern(/^/, Joi.any())\n .unknown()\n .optional()\n .description('Custom properties for the message')\n })\n\nexport const formAdapterSubmissionMessageDataSchema =\n Joi.object<FormAdapterSubmissionMessageData>().keys({\n main: Joi.object(),\n repeaters: Joi.object(),\n payment: Joi.object()\n .keys({\n paymentId: Joi.string().required(),\n reference: Joi.string().required(),\n amount: Joi.number().required(),\n description: Joi.string().required(),\n createdAt: Joi.string().required()\n })\n .optional(),\n files: Joi.object().pattern(\n Joi.string(),\n Joi.array().items(\n Joi.object().keys({\n fileName: Joi.string().required(),\n fileId: Joi.string().required(),\n userDownloadLink: Joi.string().required()\n })\n )\n )\n })\n\nexport const formAdapterSubmissionMessageResultSchema =\n Joi.object<FormAdapterSubmissionMessageResult>().keys({\n files: Joi.object()\n .keys({\n main: Joi.string().required(),\n repeaters: Joi.object()\n })\n .required()\n })\n\nexport const formAdapterSubmissionMessagePayloadSchema =\n Joi.object<FormAdapterSubmissionMessagePayload>().keys({\n meta: formAdapterSubmissionMessageMetaSchema.required(),\n data: formAdapterSubmissionMessageDataSchema.required(),\n result: formAdapterSubmissionMessageResultSchema.required()\n })\n"],"mappings":"AAAA,SACEA,UAAU,EACVC,yBAAyB,EACzBC,QAAQ,EACRC,8BAA8B,EAC9BC,UAAU,EACVC,WAAW,QACN,oBAAoB;AAC3B,OAAOC,GAAG,MAAM,KAAK;AAErB,SAASC,kCAAkC;AAQ3C,OAAO,MAAMC,sCAAsC,GACjDF,GAAG,CAACG,MAAM,CAAmC,CAAC,CAACC,IAAI,CAAC;EAClDC,aAAa,EAAEL,GAAG,CAACM,MAAM,CAAC,CAAC,CAACC,KAAK,CAC/B,GAAGC,MAAM,CAACC,MAAM,CAACR,kCAAkC,CACrD,CAAC;EACDS,SAAS,EAAEV,GAAG,CAACW,IAAI,CAAC,CAAC,CAACC,QAAQ,CAAC,CAAC;EAChCC,eAAe,EAAEb,GAAG,CAACM,MAAM,CAAC,CAAC,CAACM,QAAQ,CAAC,CAAC;EACxCE,QAAQ,EAAEf,WAAW;EACrBgB,MAAM,EAAEnB,QAAQ;EAChBoB,QAAQ,EAAElB,UAAU;EACpBmB,MAAM,EAAEjB,GAAG,CAACM,MAAM,CAAC,CAAC,CACjBC,KAAK,CAAC,GAAGC,MAAM,CAACC,MAAM,CAACf,UAAU,CAAC,CAAC,CACnCkB,QAAQ,CAAC,CAAC;EACbM,SAAS,EAAElB,GAAG,CAACmB,OAAO,CAAC,CAAC,CAACP,QAAQ,CAAC,CAAC;EACnCQ,iBAAiB,EAAEvB,8BAA8B,CAACe,QAAQ,CAAC,CAAC;EAC5DS,eAAe,EAAE1B,yBAAyB,CAAC2B,QAAQ,CAAC,CAAC;EACrDC,MAAM,EAAEvB,GAAG,CAACG,MAAM,CAAC,CAAC,CACjBqB,OAAO,CAAC,GAAG,EAAExB,GAAG,CAACyB,GAAG,CAAC,CAAC,CAAC,CACvBC,OAAO,CAAC,CAAC,CACTJ,QAAQ,CAAC,CAAC,CACVK,WAAW,CAAC,mCAAmC;AACpD,CAAC,CAAC;AAEJ,OAAO,MAAMC,sCAAsC,GACjD5B,GAAG,CAACG,MAAM,CAAmC,CAAC,CAACC,IAAI,CAAC;EAClDyB,IAAI,EAAE7B,GAAG,CAACG,MAAM,CAAC,CAAC;EAClB2B,SAAS,EAAE9B,GAAG,CAACG,MAAM,CAAC,CAAC;EACvB4B,OAAO,EAAE/B,GAAG,CAACG,MAAM,CAAC,CAAC,CAClBC,IAAI,CAAC;IACJ4B,SAAS,EAAEhC,GAAG,CAACM,MAAM,CAAC,CAAC,CAACM,QAAQ,CAAC,CAAC;IAClCqB,SAAS,EAAEjC,GAAG,CAACM,MAAM,CAAC,CAAC,CAACM,QAAQ,CAAC,CAAC;IAClCsB,MAAM,EAAElC,GAAG,CAACmC,MAAM,CAAC,CAAC,CAACvB,QAAQ,CAAC,CAAC;IAC/Be,WAAW,EAAE3B,GAAG,CAACM,MAAM,CAAC,CAAC,CAACM,QAAQ,CAAC,CAAC;IACpCwB,SAAS,EAAEpC,GAAG,CAACM,MAAM,CAAC,CAAC,CAACM,QAAQ,CAAC;EACnC,CAAC,CAAC,CACDU,QAAQ,CAAC,CAAC;EACbe,KAAK,EAAErC,GAAG,CAACG,MAAM,CAAC,CAAC,CAACqB,OAAO,CACzBxB,GAAG,CAACM,MAAM,CAAC,CAAC,EACZN,GAAG,CAACsC,KAAK,CAAC,CAAC,CAACC,KAAK,CACfvC,GAAG,CAACG,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;IAChBoC,QAAQ,EAAExC,GAAG,CAACM,MAAM,CAAC,CAAC,CAACM,QAAQ,CAAC,CAAC;IACjC6B,MAAM,EAAEzC,GAAG,CAACM,MAAM,CAAC,CAAC,CAACM,QAAQ,CAAC,CAAC;IAC/B8B,gBAAgB,EAAE1C,GAAG,CAACM,MAAM,CAAC,CAAC,CAACM,QAAQ,CAAC;EAC1C,CAAC,CACH,CACF;AACF,CAAC,CAAC;AAEJ,OAAO,MAAM+B,wCAAwC,GACnD3C,GAAG,CAACG,MAAM,CAAqC,CAAC,CAACC,IAAI,CAAC;EACpDiC,KAAK,EAAErC,GAAG,CAACG,MAAM,CAAC,CAAC,CAChBC,IAAI,CAAC;IACJyB,IAAI,EAAE7B,GAAG,CAACM,MAAM,CAAC,CAAC,CAACM,QAAQ,CAAC,CAAC;IAC7BkB,SAAS,EAAE9B,GAAG,CAACG,MAAM,CAAC;EACxB,CAAC,CAAC,CACDS,QAAQ,CAAC;AACd,CAAC,CAAC;AAEJ,OAAO,MAAMgC,yCAAyC,GACpD5C,GAAG,CAACG,MAAM,CAAsC,CAAC,CAACC,IAAI,CAAC;EACrDyC,IAAI,EAAE3C,sCAAsC,CAACU,QAAQ,CAAC,CAAC;EACvDkC,IAAI,EAAElB,sCAAsC,CAAChB,QAAQ,CAAC,CAAC;EACvDmC,MAAM,EAAEJ,wCAAwC,CAAC/B,QAAQ,CAAC;AAC5D,CAAC,CAAC","ignoreList":[]}
@@ -3,7 +3,7 @@ import { type PluginProperties, type Request, type ResponseObject } from '@hapi/
3
3
  import { type JoiExpression, type ValidationErrorItem } from 'joi';
4
4
  import { type UkAddressState } from '~/src/server/plugins/engine/components/UkAddressField.js';
5
5
  import { type Component } from '~/src/server/plugins/engine/components/helpers/components.js';
6
- import { type FileUploadField } from '~/src/server/plugins/engine/components/index.js';
6
+ import { type FileUploadField, type PaymentField } from '~/src/server/plugins/engine/components/index.js';
7
7
  import { type BackLink, type ComponentText, type ComponentViewModel, type DatePartsState, type EastingNorthingState, type LatLongState, type MonthYearState } from '~/src/server/plugins/engine/components/types.js';
8
8
  import { type FormModel } from '~/src/server/plugins/engine/models/index.js';
9
9
  import { type DetailItemField } from '~/src/server/plugins/engine/models/types.js';
@@ -240,6 +240,8 @@ export interface FormPageViewModel extends PageViewModelBase {
240
240
  errors?: FormSubmissionError[];
241
241
  hasMissingNotificationEmail?: boolean;
242
242
  allowSaveAndExit: boolean;
243
+ showSubmitButton?: boolean;
244
+ showPaymentExpiredNotification?: boolean;
243
245
  }
244
246
  export interface RepeaterSummaryPageViewModel extends PageViewModelBase {
245
247
  context: FormContext;
@@ -274,6 +276,8 @@ export interface ExternalArgs {
274
276
  controller: QuestionPageController;
275
277
  sourceUrl: string;
276
278
  actionArgs: Record<string, string>;
279
+ isLive: boolean;
280
+ isPreview: boolean;
277
281
  }
278
282
  export interface PostcodeLookupExternalArgs extends ExternalArgs {
279
283
  component: UkAddressFieldComponent;
@@ -328,6 +332,13 @@ export interface FormAdapterFile {
328
332
  fileId: string;
329
333
  userDownloadLink: string;
330
334
  }
335
+ export interface FormAdapterPayment {
336
+ paymentId: string;
337
+ reference: string;
338
+ amount: number;
339
+ description: string;
340
+ createdAt: string;
341
+ }
331
342
  export interface FormAdapterSubmissionMessageResult {
332
343
  files: {
333
344
  main: string;
@@ -340,11 +351,18 @@ export interface FormAdapterSubmissionMessageResult {
340
351
  export type FileUploadFieldDetailitem = Omit<DetailItemField, 'field'> & {
341
352
  field: FileUploadField;
342
353
  };
354
+ /**
355
+ * A detail item specifically for payments
356
+ */
357
+ export type PaymentFieldDetailItem = Omit<DetailItemField, 'field'> & {
358
+ field: PaymentField;
359
+ };
343
360
  export type RichFormValue = FormValue | FormPayload | DatePartsState | MonthYearState | UkAddressState | EastingNorthingState | LatLongState;
344
361
  export interface FormAdapterSubmissionMessageData {
345
362
  main: Record<string, RichFormValue | null>;
346
363
  repeaters: Record<string, Record<string, RichFormValue>[]>;
347
364
  files: Record<string, FormAdapterFile[]>;
365
+ payment?: FormAdapterPayment;
348
366
  }
349
367
  export interface FormAdapterSubmissionMessagePayload {
350
368
  meta: FormAdapterSubmissionMessageMeta;
@@ -43,4 +43,8 @@ export { FileStatus, UploadStatus } from "./types/enums.js";
43
43
  /**
44
44
  * A detail item specifically for files
45
45
  */
46
+
47
+ /**
48
+ * A detail item specifically for payments
49
+ */
46
50
  //# sourceMappingURL=types.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","names":["FileStatus","UploadStatus"],"sources":["../../../../src/server/plugins/engine/types.ts"],"sourcesContent":["import {\n type ComponentDef,\n type Event,\n type FormVersionMetadata,\n type Item,\n type List,\n type Page,\n type UkAddressFieldComponent\n} from '@defra/forms-model'\nimport {\n type PluginProperties,\n type Request,\n type ResponseObject\n} from '@hapi/hapi'\nimport { type JoiExpression, type ValidationErrorItem } from 'joi'\n\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { type UkAddressState } from '~/src/server/plugins/engine/components/UkAddressField.js'\nimport { type Component } from '~/src/server/plugins/engine/components/helpers/components.js'\nimport { type FileUploadField } from '~/src/server/plugins/engine/components/index.js'\nimport {\n type BackLink,\n type ComponentText,\n type ComponentViewModel,\n type DatePartsState,\n type EastingNorthingState,\n type LatLongState,\n type MonthYearState\n} from '~/src/server/plugins/engine/components/types.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type DetailItemField } from '~/src/server/plugins/engine/models/types.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers/pages.js'\nimport { type QuestionPageController } from '~/src/server/plugins/engine/pageControllers/index.js'\nimport {\n type FileStatus,\n type FormAdapterSubmissionSchemaVersion,\n type UploadStatus\n} from '~/src/server/plugins/engine/types/enums.js'\nimport { type ViewContext } from '~/src/server/plugins/nunjucks/types.js'\nimport {\n type FormAction,\n type FormRequest,\n type FormRequestPayload,\n type FormResponseToolkit,\n type FormStatus\n} from '~/src/server/routes/types.js'\nimport { type CacheService } from '~/src/server/services/cacheService.js'\nimport { type RequestOptions } from '~/src/server/services/httpService.js'\nimport { type Services } from '~/src/server/types.js'\n\nexport type AnyFormRequest = FormRequest | FormRequestPayload\nexport type AnyRequest = Request | AnyFormRequest\n\n/**\n * Form submission state stores the following in Redis:\n * Props containing user's submitted values as `{ [inputId]: value }` or as `{ [sectionName]: { [inputName]: value } }`\n * a) . e.g:\n * ```ts\n * {\n * _C9PRHmsgt: 'Ben',\n * WfLk9McjzX: 'Music',\n * IK7jkUFCBL: 'Royal Academy of Music'\n * }\n * ```\n *\n * b)\n * ```ts\n * {\n * checkBeforeYouStart: { ukPassport: true },\n * applicantDetails: {\n * numberOfApplicants: 1,\n * phoneNumber: '77777777',\n * emailAddress: 'aaa@aaa.com'\n * },\n * applicantOneDetails: {\n * firstName: 'a',\n * middleName: 'a',\n * lastName: 'a',\n * address: { addressLine1: 'a', addressLine2: 'a', town: 'a', postcode: 'a' }\n * }\n * }\n * ```\n */\n\n/**\n * Form submission state\n */\nexport type FormSubmissionState = {\n upload?: Record<string, TempFileState>\n} & FormState\n\nexport interface FormSubmissionError\n extends Pick<ValidationErrorItem, 'context' | 'path'> {\n href: string // e.g: '#dateField__day'\n name: string // e.g: 'dateField__day'\n text: string // e.g: 'Date field must be a real date'\n}\n\nexport interface FormConfirmationState {\n confirmed?: true\n formId?: string\n referenceNumber?: string\n}\n\nexport interface FormPayloadParams {\n action?: FormAction\n confirm?: true\n crumb?: string\n itemId?: string\n}\n\n/**\n * Form POST for question pages\n * (after Joi has converted value types)\n */\nexport type FormPayload = FormPayloadParams & Partial<Record<string, FormValue>>\n\nexport type FormValue =\n | Item['value']\n | Item['value'][]\n | UploadState\n | RepeatListState\n | undefined\n\nexport type FormState = Partial<Record<string, FormStateValue>>\nexport type FormStateValue = Exclude<FormValue, undefined> | null\n\nexport interface FormValidationResult<\n ValueType extends FormPayload | FormSubmissionState\n> {\n value: ValueType\n errors: FormSubmissionError[] | undefined\n}\n\nexport interface FormContext {\n /**\n * Evaluation form state only (filtered by visited paths),\n * with values formatted for condition evaluation using\n * {@link FormComponent.getContextValueFromState}\n */\n evaluationState: FormState\n\n /**\n * Relevant form state only (filtered by visited paths)\n */\n relevantState: FormState\n\n /**\n * Relevant pages only (filtered by visited paths)\n */\n relevantPages: PageControllerClass[]\n\n /**\n * Form submission payload (single page)\n */\n payload: FormPayload\n\n /**\n * Form submission state (entire form)\n */\n state: FormSubmissionState\n\n /**\n * Validation errors (entire form)\n */\n errors?: FormSubmissionError[]\n\n /**\n * Visited paths evaluated from form state\n */\n paths: string[]\n\n /**\n * Preview URL direct access is allowed\n */\n isForceAccess: boolean\n\n /**\n * Miscellaneous extra data from event responses\n */\n data: object\n\n pageDefMap: Map<string, Page>\n listDefMap: Map<string, List>\n componentDefMap: Map<string, ComponentDef>\n pageMap: Map<string, PageControllerClass>\n componentMap: Map<string, Component>\n referenceNumber: string\n submittedVersionNumber?: number\n}\n\nexport type FormContextRequest = (\n | {\n method: 'get'\n payload?: undefined\n }\n | {\n method: 'post'\n payload: FormPayload\n }\n | {\n method: FormRequest['method']\n payload?: object | undefined\n }\n) &\n Pick<\n FormRequest,\n 'app' | 'method' | 'params' | 'path' | 'query' | 'url' | 'server'\n >\n\nexport interface UploadInitiateResponse {\n uploadId: string\n uploadUrl: string\n statusUrl: string\n}\n\nexport {\n FileStatus,\n UploadStatus\n} from '~/src/server/plugins/engine/types/enums.js'\n\nexport type UploadState = FileState[]\n\nexport type FileUpload = {\n fileId: string\n filename: string\n contentLength: number\n} & (\n | {\n fileStatus: FileStatus.complete | FileStatus.rejected | FileStatus.pending\n errorMessage?: string\n }\n | {\n fileStatus: FileStatus.complete\n errorMessage?: undefined\n }\n)\n\nexport interface FileUploadMetadata {\n retrievalKey: string\n}\n\nexport type UploadStatusResponse =\n | {\n uploadStatus: UploadStatus.initiated\n metadata: FileUploadMetadata\n form: { file?: undefined }\n }\n | {\n uploadStatus: UploadStatus.pending | UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles?: number\n }\n | {\n uploadStatus: UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles: 0\n }\n\nexport type UploadStatusFileResponse = Exclude<\n UploadStatusResponse,\n { uploadStatus: UploadStatus.initiated }\n>\n\nexport interface FileState {\n uploadId: string\n status: UploadStatusFileResponse\n}\n\nexport interface TempFileState {\n upload?: UploadInitiateResponse\n files: UploadState\n}\n\nexport interface RepeatItemState extends FormPayload {\n itemId: string\n}\n\nexport type RepeatListState = RepeatItemState[]\n\nexport interface CheckAnswers {\n title?: ComponentText\n summaryList: SummaryList\n}\n\nexport interface SummaryList {\n classes?: string\n rows: SummaryListRow[]\n}\n\nexport interface SummaryListRow {\n key: ComponentText\n value: ComponentText\n actions?: { items: SummaryListAction[] }\n}\n\nexport type SummaryListAction = ComponentText & {\n href: string\n visuallyHiddenText: string\n}\n\nexport interface PageViewModelBase extends Partial<ViewContext> {\n page: PageController\n name?: string\n pageTitle: string\n sectionTitle?: string\n showTitle: boolean\n isStartPage: boolean\n backLink?: BackLink\n feedbackLink?: string\n serviceUrl: string\n phaseTag?: string\n}\n\nexport interface ItemDeletePageViewModel extends PageViewModelBase {\n context: FormContext\n itemTitle: string\n confirmation?: ComponentText\n buttonConfirm: ComponentText\n buttonCancel: ComponentText\n}\n\nexport interface FormPageViewModel extends PageViewModelBase {\n components: ComponentViewModel[]\n context: FormContext\n errors?: FormSubmissionError[]\n hasMissingNotificationEmail?: boolean\n allowSaveAndExit: boolean\n}\n\nexport interface RepeaterSummaryPageViewModel extends PageViewModelBase {\n context: FormContext\n errors?: FormSubmissionError[]\n checkAnswers: CheckAnswers[]\n repeatTitle: string\n allowSaveAndExit: boolean\n}\n\nexport interface FeaturedFormPageViewModel extends FormPageViewModel {\n formAction?: string\n formComponent: ComponentViewModel\n componentsBefore: ComponentViewModel[]\n uploadId: string | undefined\n proxyUrl: string | null\n}\n\nexport type PageViewModel =\n | PageViewModelBase\n | ItemDeletePageViewModel\n | FormPageViewModel\n | RepeaterSummaryPageViewModel\n | FeaturedFormPageViewModel\n\nexport type GlobalFunction = (value: unknown) => unknown\nexport type FilterFunction = (value: unknown) => unknown\nexport interface ErrorMessageTemplate {\n type: string\n template: JoiExpression\n}\n\nexport interface ErrorMessageTemplateList {\n baseErrors: ErrorMessageTemplate[]\n advancedSettingsErrors: ErrorMessageTemplate[]\n}\n\nexport type PreparePageEventRequestOptions = (\n options: RequestOptions,\n event: Event,\n page: PageControllerClass,\n context: FormContext\n) => void\n\nexport type OnRequestCallback = (\n request: AnyFormRequest,\n h: FormResponseToolkit,\n context: FormContext\n) =>\n | ResponseObject\n | FormResponseToolkit['continue']\n | Promise<ResponseObject | FormResponseToolkit['continue']>\n\nexport type SaveAndExitHandler = (\n request: FormRequestPayload,\n h: FormResponseToolkit,\n context: FormContext\n) => ResponseObject\n\nexport interface ExternalArgs {\n component: ComponentDef\n controller: QuestionPageController\n sourceUrl: string\n actionArgs: Record<string, string>\n}\n\nexport interface PostcodeLookupExternalArgs extends ExternalArgs {\n component: UkAddressFieldComponent\n actionArgs: { step: string }\n}\n\nexport interface ExternalStateAppendage {\n component: string\n data: FormStateValue | FormState\n}\n\nexport interface PluginOptions {\n model?: FormModel\n services?: Services\n controllers?: Record<string, typeof PageController>\n cache?: CacheService | string\n globals?: Record<string, GlobalFunction>\n filters?: Record<string, FilterFunction>\n saveAndExit?: SaveAndExitHandler\n pluginPath?: string\n nunjucks: {\n baseLayoutPath: string\n paths: string[]\n }\n viewContext: PluginProperties['forms-engine-plugin']['viewContext']\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n onRequest?: OnRequestCallback\n baseUrl: string // base URL of the application, protocol and hostname e.g. \"https://myapp.com\"\n ordnanceSurveyApiKey?: string\n ordnanceSurveyApiSecret?: string\n}\n\nexport interface FormAdapterSubmissionMessageMeta {\n schemaVersion: FormAdapterSubmissionSchemaVersion\n timestamp: Date\n referenceNumber: string\n formName: string\n formId: string\n formSlug: string\n status: FormStatus\n isPreview: boolean\n notificationEmail: string\n versionMetadata?: FormVersionMetadata\n custom?: Record<string, unknown>\n}\n\nexport type FormAdapterSubmissionMessageMetaSerialised = Omit<\n FormAdapterSubmissionMessageMeta,\n 'schemaVersion' | 'timestamp' | 'status'\n> & {\n schemaVersion: number\n status: string\n timestamp: string\n}\nexport interface FormAdapterFile {\n fileName: string\n fileId: string\n userDownloadLink: string\n}\n\nexport interface FormAdapterSubmissionMessageResult {\n files: {\n main: string\n repeaters: Record<string, string>\n }\n}\n\n/**\n * A detail item specifically for files\n */\nexport type FileUploadFieldDetailitem = Omit<DetailItemField, 'field'> & {\n field: FileUploadField\n}\nexport type RichFormValue =\n | FormValue\n | FormPayload\n | DatePartsState\n | MonthYearState\n | UkAddressState\n | EastingNorthingState\n | LatLongState\n\nexport interface FormAdapterSubmissionMessageData {\n main: Record<string, RichFormValue | null>\n repeaters: Record<string, Record<string, RichFormValue>[]>\n files: Record<string, FormAdapterFile[]>\n}\n\nexport interface FormAdapterSubmissionMessagePayload {\n meta: FormAdapterSubmissionMessageMeta\n data: FormAdapterSubmissionMessageData\n result: FormAdapterSubmissionMessageResult\n}\n\nexport interface FormAdapterSubmissionMessage\n extends FormAdapterSubmissionMessagePayload {\n messageId: string\n recordCreatedAt: Date\n}\n\nexport interface FormAdapterSubmissionService {\n handleFormSubmission: (\n submissionMessage: FormAdapterSubmissionMessage\n ) => unknown\n}\n"],"mappings":"AAsDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAyBA;AACA;AACA;AACA;;AAsGA,SACEA,UAAU,EACVC,YAAY;;AAoPd;AACA;AACA","ignoreList":[]}
1
+ {"version":3,"file":"types.js","names":["FileStatus","UploadStatus"],"sources":["../../../../src/server/plugins/engine/types.ts"],"sourcesContent":["import {\n type ComponentDef,\n type Event,\n type FormVersionMetadata,\n type Item,\n type List,\n type Page,\n type UkAddressFieldComponent\n} from '@defra/forms-model'\nimport {\n type PluginProperties,\n type Request,\n type ResponseObject\n} from '@hapi/hapi'\nimport { type JoiExpression, type ValidationErrorItem } from 'joi'\n\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { type UkAddressState } from '~/src/server/plugins/engine/components/UkAddressField.js'\nimport { type Component } from '~/src/server/plugins/engine/components/helpers/components.js'\nimport {\n type FileUploadField,\n type PaymentField\n} from '~/src/server/plugins/engine/components/index.js'\nimport {\n type BackLink,\n type ComponentText,\n type ComponentViewModel,\n type DatePartsState,\n type EastingNorthingState,\n type LatLongState,\n type MonthYearState\n} from '~/src/server/plugins/engine/components/types.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type DetailItemField } from '~/src/server/plugins/engine/models/types.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers/pages.js'\nimport { type QuestionPageController } from '~/src/server/plugins/engine/pageControllers/index.js'\nimport {\n type FileStatus,\n type FormAdapterSubmissionSchemaVersion,\n type UploadStatus\n} from '~/src/server/plugins/engine/types/enums.js'\nimport { type ViewContext } from '~/src/server/plugins/nunjucks/types.js'\nimport {\n type FormAction,\n type FormRequest,\n type FormRequestPayload,\n type FormResponseToolkit,\n type FormStatus\n} from '~/src/server/routes/types.js'\nimport { type CacheService } from '~/src/server/services/cacheService.js'\nimport { type RequestOptions } from '~/src/server/services/httpService.js'\nimport { type Services } from '~/src/server/types.js'\n\nexport type AnyFormRequest = FormRequest | FormRequestPayload\nexport type AnyRequest = Request | AnyFormRequest\n\n/**\n * Form submission state stores the following in Redis:\n * Props containing user's submitted values as `{ [inputId]: value }` or as `{ [sectionName]: { [inputName]: value } }`\n * a) . e.g:\n * ```ts\n * {\n * _C9PRHmsgt: 'Ben',\n * WfLk9McjzX: 'Music',\n * IK7jkUFCBL: 'Royal Academy of Music'\n * }\n * ```\n *\n * b)\n * ```ts\n * {\n * checkBeforeYouStart: { ukPassport: true },\n * applicantDetails: {\n * numberOfApplicants: 1,\n * phoneNumber: '77777777',\n * emailAddress: 'aaa@aaa.com'\n * },\n * applicantOneDetails: {\n * firstName: 'a',\n * middleName: 'a',\n * lastName: 'a',\n * address: { addressLine1: 'a', addressLine2: 'a', town: 'a', postcode: 'a' }\n * }\n * }\n * ```\n */\n\n/**\n * Form submission state\n */\nexport type FormSubmissionState = {\n upload?: Record<string, TempFileState>\n} & FormState\n\nexport interface FormSubmissionError\n extends Pick<ValidationErrorItem, 'context' | 'path'> {\n href: string // e.g: '#dateField__day'\n name: string // e.g: 'dateField__day'\n text: string // e.g: 'Date field must be a real date'\n}\n\nexport interface FormConfirmationState {\n confirmed?: true\n formId?: string\n referenceNumber?: string\n}\n\nexport interface FormPayloadParams {\n action?: FormAction\n confirm?: true\n crumb?: string\n itemId?: string\n}\n\n/**\n * Form POST for question pages\n * (after Joi has converted value types)\n */\nexport type FormPayload = FormPayloadParams & Partial<Record<string, FormValue>>\n\nexport type FormValue =\n | Item['value']\n | Item['value'][]\n | UploadState\n | RepeatListState\n | undefined\n\nexport type FormState = Partial<Record<string, FormStateValue>>\nexport type FormStateValue = Exclude<FormValue, undefined> | null\n\nexport interface FormValidationResult<\n ValueType extends FormPayload | FormSubmissionState\n> {\n value: ValueType\n errors: FormSubmissionError[] | undefined\n}\n\nexport interface FormContext {\n /**\n * Evaluation form state only (filtered by visited paths),\n * with values formatted for condition evaluation using\n * {@link FormComponent.getContextValueFromState}\n */\n evaluationState: FormState\n\n /**\n * Relevant form state only (filtered by visited paths)\n */\n relevantState: FormState\n\n /**\n * Relevant pages only (filtered by visited paths)\n */\n relevantPages: PageControllerClass[]\n\n /**\n * Form submission payload (single page)\n */\n payload: FormPayload\n\n /**\n * Form submission state (entire form)\n */\n state: FormSubmissionState\n\n /**\n * Validation errors (entire form)\n */\n errors?: FormSubmissionError[]\n\n /**\n * Visited paths evaluated from form state\n */\n paths: string[]\n\n /**\n * Preview URL direct access is allowed\n */\n isForceAccess: boolean\n\n /**\n * Miscellaneous extra data from event responses\n */\n data: object\n\n pageDefMap: Map<string, Page>\n listDefMap: Map<string, List>\n componentDefMap: Map<string, ComponentDef>\n pageMap: Map<string, PageControllerClass>\n componentMap: Map<string, Component>\n referenceNumber: string\n submittedVersionNumber?: number\n}\n\nexport type FormContextRequest = (\n | {\n method: 'get'\n payload?: undefined\n }\n | {\n method: 'post'\n payload: FormPayload\n }\n | {\n method: FormRequest['method']\n payload?: object | undefined\n }\n) &\n Pick<\n FormRequest,\n 'app' | 'method' | 'params' | 'path' | 'query' | 'url' | 'server'\n >\n\nexport interface UploadInitiateResponse {\n uploadId: string\n uploadUrl: string\n statusUrl: string\n}\n\nexport {\n FileStatus,\n UploadStatus\n} from '~/src/server/plugins/engine/types/enums.js'\n\nexport type UploadState = FileState[]\n\nexport type FileUpload = {\n fileId: string\n filename: string\n contentLength: number\n} & (\n | {\n fileStatus: FileStatus.complete | FileStatus.rejected | FileStatus.pending\n errorMessage?: string\n }\n | {\n fileStatus: FileStatus.complete\n errorMessage?: undefined\n }\n)\n\nexport interface FileUploadMetadata {\n retrievalKey: string\n}\n\nexport type UploadStatusResponse =\n | {\n uploadStatus: UploadStatus.initiated\n metadata: FileUploadMetadata\n form: { file?: undefined }\n }\n | {\n uploadStatus: UploadStatus.pending | UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles?: number\n }\n | {\n uploadStatus: UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles: 0\n }\n\nexport type UploadStatusFileResponse = Exclude<\n UploadStatusResponse,\n { uploadStatus: UploadStatus.initiated }\n>\n\nexport interface FileState {\n uploadId: string\n status: UploadStatusFileResponse\n}\n\nexport interface TempFileState {\n upload?: UploadInitiateResponse\n files: UploadState\n}\n\nexport interface RepeatItemState extends FormPayload {\n itemId: string\n}\n\nexport type RepeatListState = RepeatItemState[]\n\nexport interface CheckAnswers {\n title?: ComponentText\n summaryList: SummaryList\n}\n\nexport interface SummaryList {\n classes?: string\n rows: SummaryListRow[]\n}\n\nexport interface SummaryListRow {\n key: ComponentText\n value: ComponentText\n actions?: { items: SummaryListAction[] }\n}\n\nexport type SummaryListAction = ComponentText & {\n href: string\n visuallyHiddenText: string\n}\n\nexport interface PageViewModelBase extends Partial<ViewContext> {\n page: PageController\n name?: string\n pageTitle: string\n sectionTitle?: string\n showTitle: boolean\n isStartPage: boolean\n backLink?: BackLink\n feedbackLink?: string\n serviceUrl: string\n phaseTag?: string\n}\n\nexport interface ItemDeletePageViewModel extends PageViewModelBase {\n context: FormContext\n itemTitle: string\n confirmation?: ComponentText\n buttonConfirm: ComponentText\n buttonCancel: ComponentText\n}\n\nexport interface FormPageViewModel extends PageViewModelBase {\n components: ComponentViewModel[]\n context: FormContext\n errors?: FormSubmissionError[]\n hasMissingNotificationEmail?: boolean\n allowSaveAndExit: boolean\n showSubmitButton?: boolean\n showPaymentExpiredNotification?: boolean\n}\n\nexport interface RepeaterSummaryPageViewModel extends PageViewModelBase {\n context: FormContext\n errors?: FormSubmissionError[]\n checkAnswers: CheckAnswers[]\n repeatTitle: string\n allowSaveAndExit: boolean\n}\n\nexport interface FeaturedFormPageViewModel extends FormPageViewModel {\n formAction?: string\n formComponent: ComponentViewModel\n componentsBefore: ComponentViewModel[]\n uploadId: string | undefined\n proxyUrl: string | null\n}\n\nexport type PageViewModel =\n | PageViewModelBase\n | ItemDeletePageViewModel\n | FormPageViewModel\n | RepeaterSummaryPageViewModel\n | FeaturedFormPageViewModel\n\nexport type GlobalFunction = (value: unknown) => unknown\nexport type FilterFunction = (value: unknown) => unknown\nexport interface ErrorMessageTemplate {\n type: string\n template: JoiExpression\n}\n\nexport interface ErrorMessageTemplateList {\n baseErrors: ErrorMessageTemplate[]\n advancedSettingsErrors: ErrorMessageTemplate[]\n}\n\nexport type PreparePageEventRequestOptions = (\n options: RequestOptions,\n event: Event,\n page: PageControllerClass,\n context: FormContext\n) => void\n\nexport type OnRequestCallback = (\n request: AnyFormRequest,\n h: FormResponseToolkit,\n context: FormContext\n) =>\n | ResponseObject\n | FormResponseToolkit['continue']\n | Promise<ResponseObject | FormResponseToolkit['continue']>\n\nexport type SaveAndExitHandler = (\n request: FormRequestPayload,\n h: FormResponseToolkit,\n context: FormContext\n) => ResponseObject\n\nexport interface ExternalArgs {\n component: ComponentDef\n controller: QuestionPageController\n sourceUrl: string\n actionArgs: Record<string, string>\n isLive: boolean\n isPreview: boolean\n}\n\nexport interface PostcodeLookupExternalArgs extends ExternalArgs {\n component: UkAddressFieldComponent\n actionArgs: { step: string }\n}\n\nexport interface ExternalStateAppendage {\n component: string\n data: FormStateValue | FormState\n}\n\nexport interface PluginOptions {\n model?: FormModel\n services?: Services\n controllers?: Record<string, typeof PageController>\n cache?: CacheService | string\n globals?: Record<string, GlobalFunction>\n filters?: Record<string, FilterFunction>\n saveAndExit?: SaveAndExitHandler\n pluginPath?: string\n nunjucks: {\n baseLayoutPath: string\n paths: string[]\n }\n viewContext: PluginProperties['forms-engine-plugin']['viewContext']\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n onRequest?: OnRequestCallback\n baseUrl: string // base URL of the application, protocol and hostname e.g. \"https://myapp.com\"\n ordnanceSurveyApiKey?: string\n ordnanceSurveyApiSecret?: string\n}\n\nexport interface FormAdapterSubmissionMessageMeta {\n schemaVersion: FormAdapterSubmissionSchemaVersion\n timestamp: Date\n referenceNumber: string\n formName: string\n formId: string\n formSlug: string\n status: FormStatus\n isPreview: boolean\n notificationEmail: string\n versionMetadata?: FormVersionMetadata\n custom?: Record<string, unknown>\n}\n\nexport type FormAdapterSubmissionMessageMetaSerialised = Omit<\n FormAdapterSubmissionMessageMeta,\n 'schemaVersion' | 'timestamp' | 'status'\n> & {\n schemaVersion: number\n status: string\n timestamp: string\n}\nexport interface FormAdapterFile {\n fileName: string\n fileId: string\n userDownloadLink: string\n}\n\nexport interface FormAdapterPayment {\n paymentId: string\n reference: string\n amount: number\n description: string\n createdAt: string\n}\n\nexport interface FormAdapterSubmissionMessageResult {\n files: {\n main: string\n repeaters: Record<string, string>\n }\n}\n\n/**\n * A detail item specifically for files\n */\nexport type FileUploadFieldDetailitem = Omit<DetailItemField, 'field'> & {\n field: FileUploadField\n}\n\n/**\n * A detail item specifically for payments\n */\nexport type PaymentFieldDetailItem = Omit<DetailItemField, 'field'> & {\n field: PaymentField\n}\nexport type RichFormValue =\n | FormValue\n | FormPayload\n | DatePartsState\n | MonthYearState\n | UkAddressState\n | EastingNorthingState\n | LatLongState\n\nexport interface FormAdapterSubmissionMessageData {\n main: Record<string, RichFormValue | null>\n repeaters: Record<string, Record<string, RichFormValue>[]>\n files: Record<string, FormAdapterFile[]>\n payment?: FormAdapterPayment\n}\n\nexport interface FormAdapterSubmissionMessagePayload {\n meta: FormAdapterSubmissionMessageMeta\n data: FormAdapterSubmissionMessageData\n result: FormAdapterSubmissionMessageResult\n}\n\nexport interface FormAdapterSubmissionMessage\n extends FormAdapterSubmissionMessagePayload {\n messageId: string\n recordCreatedAt: Date\n}\n\nexport interface FormAdapterSubmissionService {\n handleFormSubmission: (\n submissionMessage: FormAdapterSubmissionMessage\n ) => unknown\n}\n"],"mappings":"AAyDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAyBA;AACA;AACA;AACA;;AAsGA,SACEA,UAAU,EACVC,YAAY;;AAgQd;AACA;AACA;;AAKA;AACA;AACA","ignoreList":[]}
@@ -3,7 +3,7 @@ import { type FormRequestPayload, type FormResponseToolkit } from '~/src/server/
3
3
  import { type ExternalArgs } from '~/src/server/plugins/engine/types.js';
4
4
  export declare function isExternalComponent(component: unknown): component is ExternalComponent;
5
5
  export interface ExternalComponent {
6
- dispatcher(request: FormRequestPayload, h: FormResponseToolkit, args: ExternalArgs): ResponseObject;
6
+ dispatcher(request: FormRequestPayload, h: FormResponseToolkit, args: ExternalArgs): Promise<ResponseObject>;
7
7
  }
8
8
  /**
9
9
  * Returns internal and external components from a componentMap, regardless of error state.
@@ -1 +1 @@
1
- {"version":3,"file":"validationHelpers.js","names":["Components","isExternalComponent","component","dispatcher","getComponentsByType","internalComponents","Map","externalComponents","componentMap","Object","entries","name","set"],"sources":["../../../../src/server/plugins/engine/validationHelpers.ts"],"sourcesContent":["import { type ResponseObject } from '@hapi/hapi'\n\nimport * as Components from '~/src/server/plugins/engine/components/index.js'\nimport {\n type FormRequestPayload,\n type FormResponseToolkit\n} from '~/src/server/plugins/engine/types/index.js'\nimport { type ExternalArgs } from '~/src/server/plugins/engine/types.js'\n\n// Type guard for ExternalComponent\nexport function isExternalComponent(\n component: unknown\n): component is ExternalComponent {\n return typeof (component as ExternalComponent).dispatcher === 'function'\n}\n\n// External components are guaranteed to have a dispatcher method\nexport interface ExternalComponent {\n dispatcher(\n request: FormRequestPayload,\n h: FormResponseToolkit,\n args: ExternalArgs\n ): ResponseObject\n}\n\n/**\n * Returns internal and external components from a componentMap, regardless of error state.\n * @returns An object containing internalComponents and externalComponents arrays\n */\nexport function getComponentsByType(): {\n internalComponents: Map<string, unknown>\n externalComponents: Map<string, ExternalComponent>\n} {\n const internalComponents = new Map<string, unknown>()\n const externalComponents = new Map<string, ExternalComponent>()\n\n const componentMap = new Map<string, unknown>(Object.entries(Components))\n\n for (const [name, component] of componentMap.entries()) {\n if (isExternalComponent(component)) {\n externalComponents.set(name, component)\n } else {\n internalComponents.set(name, component)\n }\n }\n\n return { internalComponents, externalComponents }\n}\n"],"mappings":"AAEA,OAAO,KAAKA,UAAU;AAOtB;AACA,OAAO,SAASC,mBAAmBA,CACjCC,SAAkB,EACc;EAChC,OAAO,OAAQA,SAAS,CAAuBC,UAAU,KAAK,UAAU;AAC1E;;AAEA;;AASA;AACA;AACA;AACA;AACA,OAAO,SAASC,mBAAmBA,CAAA,EAGjC;EACA,MAAMC,kBAAkB,GAAG,IAAIC,GAAG,CAAkB,CAAC;EACrD,MAAMC,kBAAkB,GAAG,IAAID,GAAG,CAA4B,CAAC;EAE/D,MAAME,YAAY,GAAG,IAAIF,GAAG,CAAkBG,MAAM,CAACC,OAAO,CAACV,UAAU,CAAC,CAAC;EAEzE,KAAK,MAAM,CAACW,IAAI,EAAET,SAAS,CAAC,IAAIM,YAAY,CAACE,OAAO,CAAC,CAAC,EAAE;IACtD,IAAIT,mBAAmB,CAACC,SAAS,CAAC,EAAE;MAClCK,kBAAkB,CAACK,GAAG,CAACD,IAAI,EAAET,SAAS,CAAC;IACzC,CAAC,MAAM;MACLG,kBAAkB,CAACO,GAAG,CAACD,IAAI,EAAET,SAAS,CAAC;IACzC;EACF;EAEA,OAAO;IAAEG,kBAAkB;IAAEE;EAAmB,CAAC;AACnD","ignoreList":[]}
1
+ {"version":3,"file":"validationHelpers.js","names":["Components","isExternalComponent","component","dispatcher","getComponentsByType","internalComponents","Map","externalComponents","componentMap","Object","entries","name","set"],"sources":["../../../../src/server/plugins/engine/validationHelpers.ts"],"sourcesContent":["import { type ResponseObject } from '@hapi/hapi'\n\nimport * as Components from '~/src/server/plugins/engine/components/index.js'\nimport {\n type FormRequestPayload,\n type FormResponseToolkit\n} from '~/src/server/plugins/engine/types/index.js'\nimport { type ExternalArgs } from '~/src/server/plugins/engine/types.js'\n\n// Type guard for ExternalComponent\nexport function isExternalComponent(\n component: unknown\n): component is ExternalComponent {\n return typeof (component as ExternalComponent).dispatcher === 'function'\n}\n\n// External components are guaranteed to have a dispatcher method\nexport interface ExternalComponent {\n dispatcher(\n request: FormRequestPayload,\n h: FormResponseToolkit,\n args: ExternalArgs\n ): Promise<ResponseObject>\n}\n\n/**\n * Returns internal and external components from a componentMap, regardless of error state.\n * @returns An object containing internalComponents and externalComponents arrays\n */\nexport function getComponentsByType(): {\n internalComponents: Map<string, unknown>\n externalComponents: Map<string, ExternalComponent>\n} {\n const internalComponents = new Map<string, unknown>()\n const externalComponents = new Map<string, ExternalComponent>()\n\n const componentMap = new Map<string, unknown>(Object.entries(Components))\n\n for (const [name, component] of componentMap.entries()) {\n if (isExternalComponent(component)) {\n externalComponents.set(name, component)\n } else {\n internalComponents.set(name, component)\n }\n }\n\n return { internalComponents, externalComponents }\n}\n"],"mappings":"AAEA,OAAO,KAAKA,UAAU;AAOtB;AACA,OAAO,SAASC,mBAAmBA,CACjCC,SAAkB,EACc;EAChC,OAAO,OAAQA,SAAS,CAAuBC,UAAU,KAAK,UAAU;AAC1E;;AAEA;;AASA;AACA;AACA;AACA;AACA,OAAO,SAASC,mBAAmBA,CAAA,EAGjC;EACA,MAAMC,kBAAkB,GAAG,IAAIC,GAAG,CAAkB,CAAC;EACrD,MAAMC,kBAAkB,GAAG,IAAID,GAAG,CAA4B,CAAC;EAE/D,MAAME,YAAY,GAAG,IAAIF,GAAG,CAAkBG,MAAM,CAACC,OAAO,CAACV,UAAU,CAAC,CAAC;EAEzE,KAAK,MAAM,CAACW,IAAI,EAAET,SAAS,CAAC,IAAIM,YAAY,CAACE,OAAO,CAAC,CAAC,EAAE;IACtD,IAAIT,mBAAmB,CAACC,SAAS,CAAC,EAAE;MAClCK,kBAAkB,CAACK,GAAG,CAACD,IAAI,EAAET,SAAS,CAAC;IACzC,CAAC,MAAM;MACLG,kBAAkB,CAACO,GAAG,CAACD,IAAI,EAAET,SAAS,CAAC;IACzC;EACF;EAEA,OAAO;IAAEG,kBAAkB;IAAEE;EAAmB,CAAC;AACnD","ignoreList":[]}
@@ -0,0 +1,42 @@
1
+ {% from "govuk/components/warning-text/macro.njk" import govukWarningText %}
2
+ {% from "govuk/components/button/macro.njk" import govukButton %}
3
+
4
+ {% macro PaymentField(component) %}
5
+ {% set model = component.model %}
6
+ {% set amount = model.amount %}
7
+ {% set description = model.description %}
8
+ {% set paymentState = model.paymentState %}
9
+ {% set isPreAuthorised = paymentState and paymentState.preAuth and paymentState.preAuth.status == 'success' %}
10
+
11
+ <div class="app-payment-field">
12
+ {% if isPreAuthorised %}
13
+ {# Payment already pre-authorised - show confirmation message #}
14
+ <h2 class="govuk-heading-m">You have already authorised a payment for this form</h2>
15
+
16
+ <p class="govuk-body">Continue to submit the form. You will not be charged twice.</p>
17
+ {% else %}
18
+ {# No pre-authorisation - show payment form #}
19
+ <h2 class="govuk-heading-m">{{ model.label.text if model.label and model.label.text else "Payment details required" }}</h2>
20
+
21
+ <p class="govuk-body">{{ description }}</p>
22
+
23
+ {{ govukWarningText({
24
+ text: "You may see a pending transaction in your bank account but you will only be charged when you submit the form.",
25
+ iconFallbackText: "Warning"
26
+ }) }}
27
+
28
+ <p class="govuk-body">You can submit the form after you have added your payment details.</p>
29
+
30
+ <p class="govuk-body govuk-!-margin-bottom-1">Total amount:</p>
31
+ <p class="govuk-heading-l govuk-!-margin-bottom-4 govuk-!-padding-top-0">£{{ amount }}</p>
32
+
33
+ {{ govukButton({
34
+ text: "Add payment details",
35
+ attributes: {
36
+ name: "action",
37
+ value: "external-" + model.name
38
+ }
39
+ }) }}
40
+ {% endif %}
41
+ </div>
42
+ {% endmacro %}
@@ -1,6 +1,7 @@
1
1
  {% extends baseLayoutPath %}
2
2
 
3
3
  {% from "govuk/components/error-summary/macro.njk" import govukErrorSummary %}
4
+ {% from "govuk/components/notification-banner/macro.njk" import govukNotificationBanner %}
4
5
  {% from "partials/components.html" import componentList with context %}
5
6
 
6
7
  {% block content %}
@@ -10,7 +11,14 @@
10
11
  {% include "partials/preview-banner.html" %}
11
12
  {% endif %}
12
13
 
13
- {% if errors | length %}
14
+ {% if showPaymentExpiredNotification %}
15
+ {{ govukNotificationBanner({
16
+ titleText: "Important",
17
+ html: '<h3 class="govuk-notification-banner__heading">Your payment has been cancelled</h3><p class="govuk-body">Your payment details were deleted because the form was inactive for 5 days.</p><p class="govuk-body">Add your payment details again.</p>'
18
+ }) }}
19
+ {% endif %}
20
+
21
+ {% if errors | length and not showPaymentExpiredNotification %}
14
22
  {{ govukErrorSummary({
15
23
  titleText: "There is a problem",
16
24
  errorList: checkErrorTemplates(errors)
@@ -1,6 +1,19 @@
1
1
  {% from "govuk/components/button/macro.njk" import govukButton %}
2
2
  {% from "govuk/components/summary-list/macro.njk" import govukSummaryList -%}
3
3
 
4
+ {% set noPaymentFields = true %}
5
+ {% set hasIncompletePayment = false %}
6
+
7
+ {% for comp in components %}
8
+ {% if comp.type == 'PaymentField' %}
9
+ {% set noPaymentFields = false %}
10
+ {# Check if payment is incomplete (no preAuth status) #}
11
+ {% if not comp.model.paymentState or not comp.model.paymentState.preAuth or comp.model.paymentState.preAuth.status != 'success' %}
12
+ {% set hasIncompletePayment = true %}
13
+ {% endif %}
14
+ {% endif %}
15
+ {% endfor %}
16
+
4
17
  <form method="post" novalidate>
5
18
  <input type="hidden" name="crumb" value="{{ crumb }}">
6
19
 
@@ -15,11 +28,13 @@
15
28
  {% endif %}
16
29
 
17
30
  <div class="govuk-button-group">
18
- {{ govukButton({
19
- text: buttonText,
20
- isStartButton: isStartPage,
21
- preventDoubleClick: true
22
- }) }}
31
+ {% if showSubmitButton !== false and not hasIncompletePayment %}
32
+ {{ govukButton({
33
+ text: buttonText,
34
+ isStartButton: isStartPage,
35
+ preventDoubleClick: true
36
+ }) }}
37
+ {% endif %}
23
38
 
24
39
  {% if allowSaveAndExit %}
25
40
  {{ govukButton({
@@ -3,6 +3,7 @@
3
3
  {% from "govuk/components/error-summary/macro.njk" import govukErrorSummary %}
4
4
  {% from "govuk/components/summary-list/macro.njk" import govukSummaryList %}
5
5
  {% from "govuk/components/button/macro.njk" import govukButton %}
6
+ {% from "govuk/components/notification-banner/macro.njk" import govukNotificationBanner %}
6
7
  {% from "partials/components.html" import componentList with context %}
7
8
  {% from "govuk/components/input/macro.njk" import govukInput %}
8
9
 
@@ -13,6 +14,14 @@
13
14
  {% include "partials/preview-banner.html" %}
14
15
  {% endif %}
15
16
 
17
+ {% if paymentState and paymentState.preAuth and paymentState.preAuth.status == 'success' %}
18
+ {{ govukNotificationBanner({
19
+ type: "success",
20
+ titleText: "Success",
21
+ html: "<h3 class=\"govuk-notification-banner__heading\">We have your payment details</h3><p class=\"govuk-body\">Your payment is on hold. We will charge you when you submit the form.</p>"
22
+ }) }}
23
+ {% endif %}
24
+
16
25
  {% if errors %}
17
26
  {{ govukErrorSummary({
18
27
  titleText: "There is a problem",
@@ -41,6 +50,13 @@
41
50
  {% endif %}
42
51
  {% endfor %}
43
52
 
53
+ {% if paymentDetails %}
54
+ <h2 class="govuk-heading-m">
55
+ {{ paymentDetails.title.text }}
56
+ </h2>
57
+ {{ govukSummaryList(paymentDetails.summaryList) }}
58
+ {% endif %}
59
+
44
60
  <form method="post" novalidate>
45
61
  <input type="hidden" name="crumb" value="{{ crumb }}">
46
62
 
@@ -59,7 +75,7 @@
59
75
  {% set isDeclaration = declaration or components | length %}
60
76
 
61
77
  {{ govukButton({
62
- text: "Accept and send" if isDeclaration else "Send",
78
+ text: "Accept and submit" if isDeclaration else "Submit",
63
79
  name: "action",
64
80
  value: "send",
65
81
  preventDoubleClick: true
@@ -3,5 +3,5 @@
3
3
  * @this {NunjucksContext}
4
4
  * @param {string} name - The name of the component
5
5
  */
6
- export function field(this: NunjucksContext, name: string): import("../../engine/components/TextField.js").TextField | import("../../engine/components/SelectField.js").SelectField | import("../../engine/components/RadiosField.js").RadiosField | import("../../engine/components/YesNoField.js").YesNoField | import("../../engine/components/CheckboxesField.js").CheckboxesField | import("../../engine/components/DatePartsField.js").DatePartsField | import("../../engine/components/DeclarationField.js").DeclarationField | import("../../engine/components/EastingNorthingField.js").EastingNorthingField | import("../../engine/components/EmailAddressField.js").EmailAddressField | import("../../engine/components/LatLongField.js").LatLongField | import("../../engine/components/MonthYearField.js").MonthYearField | import("../../engine/components/MultilineTextField.js").MultilineTextField | import("../../engine/components/NationalGridFieldNumberField.js").NationalGridFieldNumberField | import("../../engine/components/NumberField.js").NumberField | import("../../engine/components/OsGridRefField.js").OsGridRefField | import("../../engine/components/TelephoneNumberField.js").TelephoneNumberField | import("../../engine/components/UkAddressField.js").UkAddressField | import("../../engine/components/FileUploadField.js").FileUploadField | import("../../engine/components/HiddenField.js").HiddenField | import("../../engine/components/Details.js").Details | import("../../engine/components/Html.js").Html | import("../../engine/components/InsetText.js").InsetText | import("../../engine/components/List.js").List | import("../../engine/components/Markdown.js").Markdown | undefined;
6
+ export function field(this: NunjucksContext, name: string): import("../../engine/components/TextField.js").TextField | import("../../engine/components/SelectField.js").SelectField | import("../../engine/components/RadiosField.js").RadiosField | import("../../engine/components/YesNoField.js").YesNoField | import("../../engine/components/CheckboxesField.js").CheckboxesField | import("../../engine/components/DatePartsField.js").DatePartsField | import("../../engine/components/DeclarationField.js").DeclarationField | import("../../engine/components/EastingNorthingField.js").EastingNorthingField | import("../../engine/components/EmailAddressField.js").EmailAddressField | import("../../engine/components/LatLongField.js").LatLongField | import("../../engine/components/MonthYearField.js").MonthYearField | import("../../engine/components/MultilineTextField.js").MultilineTextField | import("../../engine/components/NationalGridFieldNumberField.js").NationalGridFieldNumberField | import("../../engine/components/NumberField.js").NumberField | import("../../engine/components/OsGridRefField.js").OsGridRefField | import("../../engine/components/TelephoneNumberField.js").TelephoneNumberField | import("../../engine/components/UkAddressField.js").UkAddressField | import("../../engine/components/FileUploadField.js").FileUploadField | import("../../engine/components/HiddenField.js").HiddenField | import("../../engine/components/PaymentField.js").PaymentField | import("../../engine/components/Details.js").Details | import("../../engine/components/Html.js").Html | import("../../engine/components/InsetText.js").InsetText | import("../../engine/components/List.js").List | import("../../engine/components/Markdown.js").Markdown | undefined;
7
7
  import type { NunjucksContext } from '~/src/server/plugins/nunjucks/types.js';