@defra/forms-engine-plugin 4.0.0 → 4.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.public/stylesheets/application.min.css +2 -2
- package/.public/stylesheets/application.min.css.map +1 -1
- package/.server/client/stylesheets/shared.scss +15 -0
- package/.server/config/index.d.ts +1 -0
- package/.server/config/index.js +7 -0
- package/.server/config/index.js.map +1 -1
- package/.server/index.js +6 -2
- package/.server/index.js.map +1 -1
- package/.server/server/constants.d.ts +2 -0
- package/.server/server/constants.js +2 -0
- package/.server/server/constants.js.map +1 -1
- package/.server/server/forms/components.json +7 -0
- package/.server/server/forms/register-as-a-unicorn-breeder.yaml +18 -2
- package/.server/server/plugins/engine/components/UkAddressField.d.ts +15 -9
- package/.server/server/plugins/engine/components/UkAddressField.js +67 -6
- package/.server/server/plugins/engine/components/UkAddressField.js.map +1 -1
- package/.server/server/plugins/engine/configureEnginePlugin.d.ts +1 -1
- package/.server/server/plugins/engine/configureEnginePlugin.js +6 -3
- package/.server/server/plugins/engine/configureEnginePlugin.js.map +1 -1
- package/.server/server/plugins/engine/models/FormModel.d.ts +2 -0
- package/.server/server/plugins/engine/models/FormModel.js +3 -1
- package/.server/server/plugins/engine/models/FormModel.js.map +1 -1
- package/.server/server/plugins/engine/options.js +2 -1
- package/.server/server/plugins/engine/options.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.d.ts +1 -0
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +46 -3
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
- package/.server/server/plugins/engine/plugin.js +13 -1
- package/.server/server/plugins/engine/plugin.js.map +1 -1
- package/.server/server/plugins/engine/routes/index.js +41 -3
- package/.server/server/plugins/engine/routes/index.js.map +1 -1
- package/.server/server/plugins/engine/types.d.ts +19 -1
- package/.server/server/plugins/engine/types.js.map +1 -1
- package/.server/server/plugins/engine/validationHelpers.d.ts +15 -0
- package/.server/server/plugins/engine/validationHelpers.js +29 -0
- package/.server/server/plugins/engine/validationHelpers.js.map +1 -0
- package/.server/server/plugins/engine/views/components/ukaddressfield.html +50 -6
- package/.server/server/plugins/engine/vision.js +3 -1
- package/.server/server/plugins/engine/vision.js.map +1 -1
- package/.server/server/plugins/postcode-lookup/index.d.ts +8 -0
- package/.server/server/plugins/postcode-lookup/index.js +21 -0
- package/.server/server/plugins/postcode-lookup/index.js.map +1 -0
- package/.server/server/plugins/postcode-lookup/models/index.d.ts +255 -0
- package/.server/server/plugins/postcode-lookup/models/index.js +517 -0
- package/.server/server/plugins/postcode-lookup/models/index.js.map +1 -0
- package/.server/server/plugins/postcode-lookup/routes/index.d.ts +19 -0
- package/.server/server/plugins/postcode-lookup/routes/index.js +267 -0
- package/.server/server/plugins/postcode-lookup/routes/index.js.map +1 -0
- package/.server/server/plugins/postcode-lookup/service.d.ts +26 -0
- package/.server/server/plugins/postcode-lookup/service.js +148 -0
- package/.server/server/plugins/postcode-lookup/service.js.map +1 -0
- package/.server/server/plugins/postcode-lookup/service.test.js +144 -0
- package/.server/server/plugins/postcode-lookup/service.test.js.map +1 -0
- package/.server/server/plugins/postcode-lookup/test/__stubs__/postcode.d.ts +282 -0
- package/.server/server/plugins/postcode-lookup/test/__stubs__/postcode.js +370 -0
- package/.server/server/plugins/postcode-lookup/test/__stubs__/postcode.js.map +1 -0
- package/.server/server/plugins/postcode-lookup/test/__stubs__/query.d.ts +131 -0
- package/.server/server/plugins/postcode-lookup/test/__stubs__/query.js +195 -0
- package/.server/server/plugins/postcode-lookup/test/__stubs__/query.js.map +1 -0
- package/.server/server/plugins/postcode-lookup/test/__stubs__/uprn.d.ts +51 -0
- package/.server/server/plugins/postcode-lookup/test/__stubs__/uprn.js +52 -0
- package/.server/server/plugins/postcode-lookup/test/__stubs__/uprn.js.map +1 -0
- package/.server/server/plugins/postcode-lookup/types.d.ts +204 -0
- package/.server/server/plugins/postcode-lookup/types.js +144 -0
- package/.server/server/plugins/postcode-lookup/types.js.map +1 -0
- package/.server/server/plugins/postcode-lookup/views/postcode-lookup-details.html +83 -0
- package/.server/server/routes/types.d.ts +6 -1
- package/.server/server/routes/types.js +6 -0
- package/.server/server/routes/types.js.map +1 -1
- package/.server/server/schemas/index.js +1 -1
- package/.server/server/schemas/index.js.map +1 -1
- package/.server/server/types.d.ts +1 -0
- package/.server/server/types.js.map +1 -1
- package/package.json +2 -2
- package/src/client/stylesheets/shared.scss +15 -0
- package/src/config/index.ts +9 -1
- package/src/index.ts +5 -4
- package/src/server/constants.js +2 -0
- package/src/server/forms/components.json +7 -0
- package/src/server/forms/register-as-a-unicorn-breeder.yaml +18 -2
- package/src/server/plugins/engine/components/UkAddressField.test.ts +50 -27
- package/src/server/plugins/engine/components/UkAddressField.ts +91 -8
- package/src/server/plugins/engine/configureEnginePlugin.ts +5 -3
- package/src/server/plugins/engine/models/FormModel.ts +10 -2
- package/src/server/plugins/engine/options.js +2 -1
- package/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts +1 -0
- package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +69 -1
- package/src/server/plugins/engine/plugin.ts +13 -1
- package/src/server/plugins/engine/routes/index.test.ts +1 -0
- package/src/server/plugins/engine/routes/index.ts +71 -3
- package/src/server/plugins/engine/types.ts +21 -1
- package/src/server/plugins/engine/validationHelpers.ts +48 -0
- package/src/server/plugins/engine/views/components/ukaddressfield.html +50 -6
- package/src/server/plugins/engine/vision.ts +6 -0
- package/src/server/plugins/postcode-lookup/index.js +21 -0
- package/src/server/plugins/postcode-lookup/models/index.js +549 -0
- package/src/server/plugins/postcode-lookup/routes/index.js +258 -0
- package/src/server/plugins/postcode-lookup/service.js +188 -0
- package/src/server/plugins/postcode-lookup/service.test.js +177 -0
- package/src/server/plugins/postcode-lookup/test/__stubs__/postcode.js +382 -0
- package/src/server/plugins/postcode-lookup/test/__stubs__/query.js +200 -0
- package/src/server/plugins/postcode-lookup/test/__stubs__/uprn.js +53 -0
- package/src/server/plugins/postcode-lookup/types.js +143 -0
- package/src/server/plugins/postcode-lookup/views/postcode-lookup-details.html +83 -0
- package/src/server/postcode-lookup.test.ts +64 -0
- package/src/server/routes/types.ts +7 -1
- package/src/server/schemas/index.ts +5 -7
- package/src/server/types.ts +1 -0
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
import Joi from 'joi'
|
|
2
|
+
|
|
3
|
+
import * as service from '~/src/server/plugins/postcode-lookup/service.js'
|
|
4
|
+
import { crumbSchema } from '~/src/server/schemas/index.js'
|
|
5
|
+
|
|
6
|
+
// Field names/ids
|
|
7
|
+
const postcodeQueryFieldName = 'postcodeQuery'
|
|
8
|
+
const buildingNameQueryFieldName = 'buildingNameQuery'
|
|
9
|
+
const uprnFieldName = 'uprn'
|
|
10
|
+
|
|
11
|
+
const line1FieldName = 'addressLine1'
|
|
12
|
+
const line2FieldName = 'addressLine2'
|
|
13
|
+
const townFieldName = 'town'
|
|
14
|
+
const countyFieldName = 'county'
|
|
15
|
+
const postcodeFieldName = 'postcode'
|
|
16
|
+
|
|
17
|
+
const selectLabelText = 'Select an address'
|
|
18
|
+
|
|
19
|
+
const GOVUK_MARGIN_RIGHT_1 = 'govuk-!-margin-right-1'
|
|
20
|
+
|
|
21
|
+
export const steps = {
|
|
22
|
+
// Step 1: Postcode/building name input
|
|
23
|
+
details: 'details',
|
|
24
|
+
// Step 2: Select address
|
|
25
|
+
select: 'select',
|
|
26
|
+
// Step 3: Manual address
|
|
27
|
+
manual: 'manual'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const JOURNEY_BASE_URL = '/postcode-lookup'
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Build form errors
|
|
34
|
+
* @param {Error} [err]
|
|
35
|
+
*/
|
|
36
|
+
function buildErrors(err) {
|
|
37
|
+
const hasErrors = Joi.isError(err) && err.details.length > 0
|
|
38
|
+
|
|
39
|
+
if (!hasErrors) {
|
|
40
|
+
return {}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get error by path
|
|
45
|
+
* @param {string} fieldName
|
|
46
|
+
*/
|
|
47
|
+
const getError = (fieldName) => {
|
|
48
|
+
return err.details.find((item) => item.path[0] === fieldName)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const postcodeQueryError = getError(postcodeQueryFieldName)
|
|
52
|
+
const buildingNameQueryError = getError(buildingNameQueryFieldName)
|
|
53
|
+
const uprnError = getError(uprnFieldName)
|
|
54
|
+
const line1Error = getError(line1FieldName)
|
|
55
|
+
const line2Error = getError(line2FieldName)
|
|
56
|
+
const townError = getError(townFieldName)
|
|
57
|
+
const countyError = getError(countyFieldName)
|
|
58
|
+
const postcodeError = getError(postcodeFieldName)
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @type {{ text: string, href: string }[]}
|
|
62
|
+
*/
|
|
63
|
+
const errors = []
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Push error
|
|
67
|
+
* @param {string} fieldName - the field name
|
|
68
|
+
* @param {ValidationErrorItem} [item] - the joi validation error
|
|
69
|
+
*/
|
|
70
|
+
const pushError = (fieldName, item) => {
|
|
71
|
+
if (item) {
|
|
72
|
+
errors.push({
|
|
73
|
+
text: item.message,
|
|
74
|
+
href: `#${fieldName}`
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
pushError(postcodeQueryFieldName, postcodeQueryError)
|
|
80
|
+
pushError(buildingNameQueryFieldName, buildingNameQueryError)
|
|
81
|
+
pushError(uprnFieldName, uprnError)
|
|
82
|
+
pushError(line1FieldName, line1Error)
|
|
83
|
+
pushError(line2FieldName, line2Error)
|
|
84
|
+
pushError(townFieldName, townError)
|
|
85
|
+
pushError(countyFieldName, countyError)
|
|
86
|
+
pushError(postcodeFieldName, postcodeError)
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
errors,
|
|
90
|
+
postcodeQueryError,
|
|
91
|
+
buildingNameQueryError,
|
|
92
|
+
uprnError,
|
|
93
|
+
line1Error,
|
|
94
|
+
line2Error,
|
|
95
|
+
townError,
|
|
96
|
+
countyError,
|
|
97
|
+
postcodeError
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Search ordnance survey for addresses
|
|
103
|
+
* @param {string} postcodeQuery
|
|
104
|
+
* @param {string} buildingNameQuery
|
|
105
|
+
* @param {string} apiKey
|
|
106
|
+
*/
|
|
107
|
+
async function getAddresses(postcodeQuery, buildingNameQuery, apiKey) {
|
|
108
|
+
const addresses = await service.search(
|
|
109
|
+
postcodeQuery,
|
|
110
|
+
buildingNameQuery,
|
|
111
|
+
apiKey
|
|
112
|
+
)
|
|
113
|
+
const addressCount = addresses.length
|
|
114
|
+
const singleAddress = addressCount === 1 ? addresses.at(0) : undefined
|
|
115
|
+
const hasAddresses = addressCount > 0
|
|
116
|
+
const hasMultipleAddresses = addressCount > 1
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
hasAddresses,
|
|
120
|
+
hasMultipleAddresses,
|
|
121
|
+
singleAddress,
|
|
122
|
+
addresses,
|
|
123
|
+
addressCount
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get the details view fields
|
|
129
|
+
* @param {PostcodeLookupDetailsData | undefined} details
|
|
130
|
+
* @param {OptionalValidationErrorItem} postcodeQueryError
|
|
131
|
+
* @param {OptionalValidationErrorItem} buildingNameQueryError
|
|
132
|
+
*/
|
|
133
|
+
function getDetailsFields(details, postcodeQueryError, buildingNameQueryError) {
|
|
134
|
+
return {
|
|
135
|
+
[postcodeQueryFieldName]: {
|
|
136
|
+
id: postcodeQueryFieldName,
|
|
137
|
+
name: postcodeQueryFieldName,
|
|
138
|
+
label: {
|
|
139
|
+
text: 'Postcode'
|
|
140
|
+
},
|
|
141
|
+
hint: {
|
|
142
|
+
text: 'For example, AA3 1AB'
|
|
143
|
+
},
|
|
144
|
+
value: details?.postcodeQuery,
|
|
145
|
+
errorMessage: postcodeQueryError && { text: postcodeQueryError.message }
|
|
146
|
+
},
|
|
147
|
+
[buildingNameQueryFieldName]: {
|
|
148
|
+
id: buildingNameQueryFieldName,
|
|
149
|
+
name: buildingNameQueryFieldName,
|
|
150
|
+
label: {
|
|
151
|
+
text: 'Building name or number (optional)'
|
|
152
|
+
},
|
|
153
|
+
hint: {
|
|
154
|
+
text: 'For example, 15 or Prospect Cottage'
|
|
155
|
+
},
|
|
156
|
+
value: details?.buildingNameQuery,
|
|
157
|
+
errorMessage: buildingNameQueryError && {
|
|
158
|
+
text: buildingNameQueryError.message
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Get the select view fields
|
|
166
|
+
* @param {PostcodeLookupDetailsData} details
|
|
167
|
+
* @param {boolean} hasMultipleAddresses
|
|
168
|
+
* @param {Address | undefined} singleAddress
|
|
169
|
+
* @param {PostcodeLookupSelectPayload | undefined} payload
|
|
170
|
+
* @param {OptionalValidationErrorItem} uprnError
|
|
171
|
+
* @param {Address[]} addresses
|
|
172
|
+
*/
|
|
173
|
+
function getSelectFields(
|
|
174
|
+
details,
|
|
175
|
+
hasMultipleAddresses,
|
|
176
|
+
singleAddress,
|
|
177
|
+
payload,
|
|
178
|
+
uprnError,
|
|
179
|
+
addresses
|
|
180
|
+
) {
|
|
181
|
+
return {
|
|
182
|
+
[postcodeQueryFieldName]: {
|
|
183
|
+
id: postcodeQueryFieldName,
|
|
184
|
+
name: postcodeQueryFieldName,
|
|
185
|
+
type: 'hidden',
|
|
186
|
+
value: details.postcodeQuery
|
|
187
|
+
},
|
|
188
|
+
[buildingNameQueryFieldName]: {
|
|
189
|
+
id: buildingNameQueryFieldName,
|
|
190
|
+
name: buildingNameQueryFieldName,
|
|
191
|
+
type: 'hidden',
|
|
192
|
+
value: details.buildingNameQuery
|
|
193
|
+
},
|
|
194
|
+
[uprnFieldName]: {
|
|
195
|
+
id: uprnFieldName,
|
|
196
|
+
name: uprnFieldName,
|
|
197
|
+
label: hasMultipleAddresses
|
|
198
|
+
? {
|
|
199
|
+
text: selectLabelText
|
|
200
|
+
}
|
|
201
|
+
: undefined,
|
|
202
|
+
value: singleAddress ? singleAddress.uprn : payload?.uprn,
|
|
203
|
+
errorMessage: uprnError && { text: uprnError.message },
|
|
204
|
+
items: hasMultipleAddresses
|
|
205
|
+
? [{ text: selectLabelText, value: '' }].concat(
|
|
206
|
+
addresses.map((item) => ({
|
|
207
|
+
text: item.formatted,
|
|
208
|
+
value: item.uprn
|
|
209
|
+
}))
|
|
210
|
+
)
|
|
211
|
+
: undefined,
|
|
212
|
+
type: singleAddress ? 'hidden' : undefined
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Get the manual view fields
|
|
219
|
+
* @param {PostcodeLookupManualPayload | undefined} payload
|
|
220
|
+
* @param {OptionalValidationErrorItem} line1Error
|
|
221
|
+
* @param {OptionalValidationErrorItem} line2Error
|
|
222
|
+
* @param {OptionalValidationErrorItem} townError
|
|
223
|
+
* @param {OptionalValidationErrorItem} countyError
|
|
224
|
+
* @param {OptionalValidationErrorItem} postcodeError
|
|
225
|
+
*/
|
|
226
|
+
function getManualFields(
|
|
227
|
+
payload,
|
|
228
|
+
line1Error,
|
|
229
|
+
line2Error,
|
|
230
|
+
townError,
|
|
231
|
+
countyError,
|
|
232
|
+
postcodeError
|
|
233
|
+
) {
|
|
234
|
+
return {
|
|
235
|
+
[line1FieldName]: {
|
|
236
|
+
id: line1FieldName,
|
|
237
|
+
name: line1FieldName,
|
|
238
|
+
label: {
|
|
239
|
+
text: 'Address line 1'
|
|
240
|
+
},
|
|
241
|
+
value: payload?.addressLine1,
|
|
242
|
+
errorMessage: line1Error && { text: line1Error.message }
|
|
243
|
+
},
|
|
244
|
+
[line2FieldName]: {
|
|
245
|
+
id: line2FieldName,
|
|
246
|
+
name: line2FieldName,
|
|
247
|
+
label: {
|
|
248
|
+
text: 'Address line 2 (optional)'
|
|
249
|
+
},
|
|
250
|
+
value: payload?.addressLine2,
|
|
251
|
+
errorMessage: line2Error && { text: line2Error.message }
|
|
252
|
+
},
|
|
253
|
+
[townFieldName]: {
|
|
254
|
+
id: townFieldName,
|
|
255
|
+
name: townFieldName,
|
|
256
|
+
label: {
|
|
257
|
+
text: 'Town or city'
|
|
258
|
+
},
|
|
259
|
+
classes: 'govuk-!-width-two-thirds',
|
|
260
|
+
value: payload?.town,
|
|
261
|
+
errorMessage: townError && { text: townError.message }
|
|
262
|
+
},
|
|
263
|
+
[countyFieldName]: {
|
|
264
|
+
id: countyFieldName,
|
|
265
|
+
name: countyFieldName,
|
|
266
|
+
label: {
|
|
267
|
+
text: 'County (optional)'
|
|
268
|
+
},
|
|
269
|
+
value: payload?.county,
|
|
270
|
+
errorMessage: countyError && { text: countyError.message }
|
|
271
|
+
},
|
|
272
|
+
[postcodeFieldName]: {
|
|
273
|
+
id: postcodeFieldName,
|
|
274
|
+
name: postcodeFieldName,
|
|
275
|
+
label: {
|
|
276
|
+
text: 'Postcode'
|
|
277
|
+
},
|
|
278
|
+
classes: 'govuk-input--width-10',
|
|
279
|
+
value: payload?.postcode,
|
|
280
|
+
errorMessage: postcodeError && { text: postcodeError.message }
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export const stepSchema = Joi.string()
|
|
286
|
+
.valid(...Object.keys(steps))
|
|
287
|
+
.required()
|
|
288
|
+
|
|
289
|
+
const sharedPayloadSchemaKeys = {
|
|
290
|
+
crumb: crumbSchema,
|
|
291
|
+
step: stepSchema
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Postcode lookup details form payload schema
|
|
296
|
+
* @type {ObjectSchema<PostcodeLookupDetailsPayload>}
|
|
297
|
+
*/
|
|
298
|
+
export const detailsPayloadSchema = Joi.object()
|
|
299
|
+
.keys({
|
|
300
|
+
...sharedPayloadSchemaKeys,
|
|
301
|
+
[postcodeQueryFieldName]: Joi.string()
|
|
302
|
+
.pattern(/^[a-zA-Z]{1,2}\d[a-zA-Z\d]?\s?\d[a-zA-Z]{2}$/)
|
|
303
|
+
.trim()
|
|
304
|
+
.required()
|
|
305
|
+
.messages({
|
|
306
|
+
'string.pattern.base':
|
|
307
|
+
'Enter a valid postcode or enter an address manually',
|
|
308
|
+
'*': 'Enter a postcode'
|
|
309
|
+
}),
|
|
310
|
+
[buildingNameQueryFieldName]: Joi.string()
|
|
311
|
+
.trim()
|
|
312
|
+
.required()
|
|
313
|
+
.allow('')
|
|
314
|
+
.trim()
|
|
315
|
+
})
|
|
316
|
+
.required()
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Postcode lookup select form payload schema
|
|
320
|
+
* @type {ObjectSchema<PostcodeLookupSelectPayload>}
|
|
321
|
+
*/
|
|
322
|
+
export const selectPayloadSchema = Joi.object()
|
|
323
|
+
.keys({
|
|
324
|
+
...sharedPayloadSchemaKeys,
|
|
325
|
+
[uprnFieldName]: Joi.string().required().messages({
|
|
326
|
+
'*': selectLabelText
|
|
327
|
+
})
|
|
328
|
+
})
|
|
329
|
+
.required()
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Postcode lookup manual form payload schema
|
|
333
|
+
* @type {ObjectSchema<PostcodeLookupManualPayload>}
|
|
334
|
+
*/
|
|
335
|
+
export const manualPayloadSchema = Joi.object()
|
|
336
|
+
.keys({
|
|
337
|
+
...sharedPayloadSchemaKeys,
|
|
338
|
+
[line1FieldName]: Joi.string().trim().required().messages({
|
|
339
|
+
'*': 'Enter address line 1'
|
|
340
|
+
}),
|
|
341
|
+
[line2FieldName]: Joi.string().trim().allow('').required(),
|
|
342
|
+
[townFieldName]: Joi.string().trim().required().messages({
|
|
343
|
+
'*': 'Enter town or city'
|
|
344
|
+
}),
|
|
345
|
+
[countyFieldName]: Joi.string().trim().allow('').required(),
|
|
346
|
+
[postcodeFieldName]: Joi.string().trim().required().messages({
|
|
347
|
+
'*': 'Enter postcode'
|
|
348
|
+
})
|
|
349
|
+
})
|
|
350
|
+
.required()
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Get the postcode lookup href
|
|
354
|
+
* @param {string} [step] - the postcode lookup step
|
|
355
|
+
*/
|
|
356
|
+
function getHref(step) {
|
|
357
|
+
const query = step ? `?step=${step}` : ''
|
|
358
|
+
|
|
359
|
+
return `${JOURNEY_BASE_URL}${query}`
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* The postcode lookup details form view model
|
|
364
|
+
* @param {PostcodeLookupSessionData} data
|
|
365
|
+
* @param {PostcodeLookupDetailsData} [payload]
|
|
366
|
+
* @param {Error} [err]
|
|
367
|
+
*/
|
|
368
|
+
export function detailsViewModel(data, payload, err) {
|
|
369
|
+
const { componentTitle: pageTitle, formName, sourceUrl } = data.initial
|
|
370
|
+
|
|
371
|
+
const backLink = {
|
|
372
|
+
href: sourceUrl
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const { errors, postcodeQueryError, buildingNameQueryError } =
|
|
376
|
+
buildErrors(err)
|
|
377
|
+
|
|
378
|
+
// Model fields
|
|
379
|
+
const fields = getDetailsFields(
|
|
380
|
+
payload ?? data.details,
|
|
381
|
+
postcodeQueryError,
|
|
382
|
+
buildingNameQueryError
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
// Model buttons
|
|
386
|
+
const continueButton = {
|
|
387
|
+
text: 'Find address',
|
|
388
|
+
classes: GOVUK_MARGIN_RIGHT_1
|
|
389
|
+
}
|
|
390
|
+
const manualLink = {
|
|
391
|
+
text: 'enter address manually',
|
|
392
|
+
href: getHref(steps.manual)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return {
|
|
396
|
+
step: steps.details,
|
|
397
|
+
showTitle: true,
|
|
398
|
+
name: formName,
|
|
399
|
+
serviceUrl: sourceUrl,
|
|
400
|
+
pageTitle,
|
|
401
|
+
backLink,
|
|
402
|
+
errors,
|
|
403
|
+
fields,
|
|
404
|
+
buttons: { continueButton, manualLink }
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* The postcode lookup select form view model
|
|
410
|
+
* @param {{ session: PostcodeLookupSessionData, apiKey: string }} data
|
|
411
|
+
* @param {PostcodeLookupSelectPayload} [payload]
|
|
412
|
+
* @param {Error} [err]
|
|
413
|
+
*/
|
|
414
|
+
export async function selectViewModel(data, payload, err) {
|
|
415
|
+
const { session, apiKey } = data
|
|
416
|
+
const { details, initial } = session
|
|
417
|
+
const { postcodeQuery, buildingNameQuery } = details
|
|
418
|
+
|
|
419
|
+
// Search for addresses
|
|
420
|
+
const {
|
|
421
|
+
hasAddresses,
|
|
422
|
+
hasMultipleAddresses,
|
|
423
|
+
singleAddress,
|
|
424
|
+
addresses,
|
|
425
|
+
addressCount
|
|
426
|
+
} = await getAddresses(postcodeQuery, buildingNameQuery, apiKey)
|
|
427
|
+
|
|
428
|
+
const title = hasAddresses ? initial.componentTitle : 'No address found'
|
|
429
|
+
const formPath = initial.sourceUrl
|
|
430
|
+
const href = getHref()
|
|
431
|
+
|
|
432
|
+
const backLink = { href }
|
|
433
|
+
|
|
434
|
+
const { errors, uprnError } = buildErrors(err)
|
|
435
|
+
|
|
436
|
+
// Model fields
|
|
437
|
+
const fields = getSelectFields(
|
|
438
|
+
details,
|
|
439
|
+
hasMultipleAddresses,
|
|
440
|
+
singleAddress,
|
|
441
|
+
payload,
|
|
442
|
+
uprnError,
|
|
443
|
+
addresses
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
const searchAgainLink = {
|
|
447
|
+
text: 'Search again',
|
|
448
|
+
href
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Model buttons
|
|
452
|
+
const continueButton = {
|
|
453
|
+
href: hasAddresses ? undefined : href,
|
|
454
|
+
text: hasAddresses ? 'Use this address' : 'Search again',
|
|
455
|
+
classes: GOVUK_MARGIN_RIGHT_1
|
|
456
|
+
}
|
|
457
|
+
const manualLink = {
|
|
458
|
+
text: 'enter address manually',
|
|
459
|
+
href: `${href}?step=${steps.manual}`
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return {
|
|
463
|
+
step: steps.select,
|
|
464
|
+
showTitle: true,
|
|
465
|
+
name: title,
|
|
466
|
+
serviceUrl: formPath,
|
|
467
|
+
pageTitle: title,
|
|
468
|
+
backLink,
|
|
469
|
+
errors,
|
|
470
|
+
searchAgainLink,
|
|
471
|
+
fields,
|
|
472
|
+
details,
|
|
473
|
+
addressCount,
|
|
474
|
+
singleAddress,
|
|
475
|
+
hasAddresses,
|
|
476
|
+
hasMultipleAddresses,
|
|
477
|
+
buttons: { continueButton, manualLink }
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* The postcode lookup manual form view model
|
|
483
|
+
* @param {PostcodeLookupSessionData} data
|
|
484
|
+
* @param {PostcodeLookupManualPayload} [payload]
|
|
485
|
+
* @param {Error} [err]
|
|
486
|
+
*/
|
|
487
|
+
export function manualViewModel(data, payload, err) {
|
|
488
|
+
const { componentTitle, sourceUrl, componentHint } = data.initial
|
|
489
|
+
const formPath = sourceUrl
|
|
490
|
+
const href = getHref()
|
|
491
|
+
|
|
492
|
+
const backLink = {
|
|
493
|
+
href
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const {
|
|
497
|
+
errors,
|
|
498
|
+
line1Error,
|
|
499
|
+
line2Error,
|
|
500
|
+
townError,
|
|
501
|
+
countyError,
|
|
502
|
+
postcodeError
|
|
503
|
+
} = buildErrors(err)
|
|
504
|
+
|
|
505
|
+
// Model hint
|
|
506
|
+
const hint = componentHint && {
|
|
507
|
+
text: componentHint
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Model fields
|
|
511
|
+
const fields = getManualFields(
|
|
512
|
+
payload,
|
|
513
|
+
line1Error,
|
|
514
|
+
line2Error,
|
|
515
|
+
townError,
|
|
516
|
+
countyError,
|
|
517
|
+
postcodeError
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
// Model buttons
|
|
521
|
+
const continueButton = {
|
|
522
|
+
text: 'Use this address',
|
|
523
|
+
classes: GOVUK_MARGIN_RIGHT_1
|
|
524
|
+
}
|
|
525
|
+
const detailsLink = {
|
|
526
|
+
text: 'find an address instead',
|
|
527
|
+
href
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
return {
|
|
531
|
+
step: steps.manual,
|
|
532
|
+
showTitle: true,
|
|
533
|
+
name: componentTitle,
|
|
534
|
+
serviceUrl: formPath,
|
|
535
|
+
pageTitle: componentTitle,
|
|
536
|
+
backLink,
|
|
537
|
+
errors,
|
|
538
|
+
hint,
|
|
539
|
+
fields,
|
|
540
|
+
buttons: { continueButton, detailsLink }
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/** @typedef { ValidationErrorItem | undefined } OptionalValidationErrorItem */
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* @import { ObjectSchema, ValidationErrorItem } from 'joi'
|
|
548
|
+
* @import { Address, PostcodeLookupDetailsData, PostcodeLookupDetailsPayload, PostcodeLookupManualPayload, PostcodeLookupSelectPayload, PostcodeLookupSessionData } from '~/src/server/plugins/postcode-lookup/types.js'
|
|
549
|
+
*/
|