@defra/fcp-sfd-frontend-engine 0.15.0 → 0.17.0
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/dist/index.cjs
CHANGED
|
@@ -336,6 +336,77 @@ var formatNumber = (payloadNumber, changedNumber, originalNumber) => {
|
|
|
336
336
|
}
|
|
337
337
|
return originalNumber;
|
|
338
338
|
};
|
|
339
|
+
var sortErrorsBySectionOrder = (errors, orderedSectionsToFix, SECTION_FIELD_ORDER) => {
|
|
340
|
+
const sortedErrors = [];
|
|
341
|
+
for (const section of orderedSectionsToFix) {
|
|
342
|
+
const fieldsInSection = SECTION_FIELD_ORDER[section] || [];
|
|
343
|
+
for (const field of fieldsInSection) {
|
|
344
|
+
if (errors[field]) {
|
|
345
|
+
sortedErrors.push({
|
|
346
|
+
field,
|
|
347
|
+
...errors[field]
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return sortedErrors;
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
// src/presenters/address-presenter.js
|
|
356
|
+
var addressChangeLink = (postcodeLookup, context) => {
|
|
357
|
+
if (context === "business") {
|
|
358
|
+
if (postcodeLookup) {
|
|
359
|
+
return "/business-address-change";
|
|
360
|
+
}
|
|
361
|
+
return "/business-address-enter";
|
|
362
|
+
}
|
|
363
|
+
if (context === "personal") {
|
|
364
|
+
if (postcodeLookup) {
|
|
365
|
+
return "/account-address-change";
|
|
366
|
+
}
|
|
367
|
+
return "/account-address-enter";
|
|
368
|
+
}
|
|
369
|
+
return null;
|
|
370
|
+
};
|
|
371
|
+
var addressBackLink = (postcodeLookup, context) => {
|
|
372
|
+
if (context === "business") {
|
|
373
|
+
if (postcodeLookup) {
|
|
374
|
+
return { href: "/business-address-select" };
|
|
375
|
+
}
|
|
376
|
+
return { href: "/business-address-enter" };
|
|
377
|
+
}
|
|
378
|
+
if (context === "personal") {
|
|
379
|
+
if (postcodeLookup) {
|
|
380
|
+
return { href: "/account-address-select" };
|
|
381
|
+
}
|
|
382
|
+
return { href: "/account-address-enter" };
|
|
383
|
+
}
|
|
384
|
+
return null;
|
|
385
|
+
};
|
|
386
|
+
var buildAddressLine = (parts) => {
|
|
387
|
+
return parts.filter(Boolean).join(", ") || null;
|
|
388
|
+
};
|
|
389
|
+
var buildStreetLine = (buildingRange, street) => {
|
|
390
|
+
return [buildingRange, street].filter(Boolean).join(" ") || null;
|
|
391
|
+
};
|
|
392
|
+
var formatLookupAddress = (lookup, city, country, postcode) => ({
|
|
393
|
+
address1: buildAddressLine([lookup.pafOrganisationName, lookup.flatName, lookup.buildingName]),
|
|
394
|
+
address2: buildStreetLine(lookup.buildingNumberRange, lookup.street),
|
|
395
|
+
address3: buildAddressLine([lookup.doubleDependentLocality, lookup.dependentLocality]),
|
|
396
|
+
county: lookup.county ?? null,
|
|
397
|
+
city: city ?? null,
|
|
398
|
+
country: country ?? null,
|
|
399
|
+
postcode: postcode ?? null
|
|
400
|
+
});
|
|
401
|
+
var formatManualAddress = (manual, city, country, postcode) => ({
|
|
402
|
+
address1: manual.line1 ?? null,
|
|
403
|
+
address2: manual.line2 ?? null,
|
|
404
|
+
address3: manual.line3 ?? null,
|
|
405
|
+
city: city ?? null,
|
|
406
|
+
county: manual.line4 ?? null,
|
|
407
|
+
country: country ?? null,
|
|
408
|
+
postcode: postcode ?? null
|
|
409
|
+
});
|
|
339
410
|
var formatDisplayAddress = (address) => {
|
|
340
411
|
const { lookup, manual, postcode, country, city } = address;
|
|
341
412
|
let addressLines = [];
|
|
@@ -371,30 +442,6 @@ var formatDisplayAddress = (address) => {
|
|
|
371
442
|
country
|
|
372
443
|
];
|
|
373
444
|
};
|
|
374
|
-
var buildAddressLine = (parts) => {
|
|
375
|
-
return parts.filter(Boolean).join(", ") || null;
|
|
376
|
-
};
|
|
377
|
-
var buildStreetLine = (buildingRange, street) => {
|
|
378
|
-
return [buildingRange, street].filter(Boolean).join(" ") || null;
|
|
379
|
-
};
|
|
380
|
-
var formatLookupAddress = (lookup, city, country, postcode) => ({
|
|
381
|
-
address1: buildAddressLine([lookup.pafOrganisationName, lookup.flatName, lookup.buildingName]),
|
|
382
|
-
address2: buildStreetLine(lookup.buildingNumberRange, lookup.street),
|
|
383
|
-
address3: buildAddressLine([lookup.doubleDependentLocality, lookup.dependentLocality]),
|
|
384
|
-
county: lookup.county ?? null,
|
|
385
|
-
city: city ?? null,
|
|
386
|
-
country: country ?? null,
|
|
387
|
-
postcode: postcode ?? null
|
|
388
|
-
});
|
|
389
|
-
var formatManualAddress = (manual, city, country, postcode) => ({
|
|
390
|
-
address1: manual.line1 ?? null,
|
|
391
|
-
address2: manual.line2 ?? null,
|
|
392
|
-
address3: manual.line3 ?? null,
|
|
393
|
-
city: city ?? null,
|
|
394
|
-
county: manual.line4 ?? null,
|
|
395
|
-
country: country ?? null,
|
|
396
|
-
postcode: postcode ?? null
|
|
397
|
-
});
|
|
398
445
|
var formatOriginalAddress = (originalAddress) => {
|
|
399
446
|
const { lookup, manual, city, country, postcode } = originalAddress;
|
|
400
447
|
return lookup.uprn ? formatLookupAddress(lookup, city, country, postcode) : formatManualAddress(manual, city, country, postcode);
|
|
@@ -441,21 +488,6 @@ var formatDisplayAddresses = (addresses, previouslyPickedAddress) => {
|
|
|
441
488
|
});
|
|
442
489
|
return displayAddresses;
|
|
443
490
|
};
|
|
444
|
-
var sortErrorsBySectionOrder = (errors, orderedSectionsToFix, SECTION_FIELD_ORDER) => {
|
|
445
|
-
const sortedErrors = [];
|
|
446
|
-
for (const section of orderedSectionsToFix) {
|
|
447
|
-
const fieldsInSection = SECTION_FIELD_ORDER[section] || [];
|
|
448
|
-
for (const field of fieldsInSection) {
|
|
449
|
-
if (errors[field]) {
|
|
450
|
-
sortedErrors.push({
|
|
451
|
-
field,
|
|
452
|
-
...errors[field]
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
return sortedErrors;
|
|
458
|
-
};
|
|
459
491
|
|
|
460
492
|
// src/presenters/business-details-presenter.js
|
|
461
493
|
var getActionText = (value) => {
|
|
@@ -487,7 +519,9 @@ var presenters = {
|
|
|
487
519
|
getActionText,
|
|
488
520
|
formatCph,
|
|
489
521
|
formatCphText,
|
|
490
|
-
formatBusinessAddress
|
|
522
|
+
formatBusinessAddress,
|
|
523
|
+
addressBackLink,
|
|
524
|
+
addressChangeLink
|
|
491
525
|
};
|
|
492
526
|
|
|
493
527
|
// src/utils/joi.js
|
package/dist/index.js
CHANGED
|
@@ -296,6 +296,77 @@ var formatNumber = (payloadNumber, changedNumber, originalNumber) => {
|
|
|
296
296
|
}
|
|
297
297
|
return originalNumber;
|
|
298
298
|
};
|
|
299
|
+
var sortErrorsBySectionOrder = (errors, orderedSectionsToFix, SECTION_FIELD_ORDER) => {
|
|
300
|
+
const sortedErrors = [];
|
|
301
|
+
for (const section of orderedSectionsToFix) {
|
|
302
|
+
const fieldsInSection = SECTION_FIELD_ORDER[section] || [];
|
|
303
|
+
for (const field of fieldsInSection) {
|
|
304
|
+
if (errors[field]) {
|
|
305
|
+
sortedErrors.push({
|
|
306
|
+
field,
|
|
307
|
+
...errors[field]
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return sortedErrors;
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
// src/presenters/address-presenter.js
|
|
316
|
+
var addressChangeLink = (postcodeLookup, context) => {
|
|
317
|
+
if (context === "business") {
|
|
318
|
+
if (postcodeLookup) {
|
|
319
|
+
return "/business-address-change";
|
|
320
|
+
}
|
|
321
|
+
return "/business-address-enter";
|
|
322
|
+
}
|
|
323
|
+
if (context === "personal") {
|
|
324
|
+
if (postcodeLookup) {
|
|
325
|
+
return "/account-address-change";
|
|
326
|
+
}
|
|
327
|
+
return "/account-address-enter";
|
|
328
|
+
}
|
|
329
|
+
return null;
|
|
330
|
+
};
|
|
331
|
+
var addressBackLink = (postcodeLookup, context) => {
|
|
332
|
+
if (context === "business") {
|
|
333
|
+
if (postcodeLookup) {
|
|
334
|
+
return { href: "/business-address-select" };
|
|
335
|
+
}
|
|
336
|
+
return { href: "/business-address-enter" };
|
|
337
|
+
}
|
|
338
|
+
if (context === "personal") {
|
|
339
|
+
if (postcodeLookup) {
|
|
340
|
+
return { href: "/account-address-select" };
|
|
341
|
+
}
|
|
342
|
+
return { href: "/account-address-enter" };
|
|
343
|
+
}
|
|
344
|
+
return null;
|
|
345
|
+
};
|
|
346
|
+
var buildAddressLine = (parts) => {
|
|
347
|
+
return parts.filter(Boolean).join(", ") || null;
|
|
348
|
+
};
|
|
349
|
+
var buildStreetLine = (buildingRange, street) => {
|
|
350
|
+
return [buildingRange, street].filter(Boolean).join(" ") || null;
|
|
351
|
+
};
|
|
352
|
+
var formatLookupAddress = (lookup, city, country, postcode) => ({
|
|
353
|
+
address1: buildAddressLine([lookup.pafOrganisationName, lookup.flatName, lookup.buildingName]),
|
|
354
|
+
address2: buildStreetLine(lookup.buildingNumberRange, lookup.street),
|
|
355
|
+
address3: buildAddressLine([lookup.doubleDependentLocality, lookup.dependentLocality]),
|
|
356
|
+
county: lookup.county ?? null,
|
|
357
|
+
city: city ?? null,
|
|
358
|
+
country: country ?? null,
|
|
359
|
+
postcode: postcode ?? null
|
|
360
|
+
});
|
|
361
|
+
var formatManualAddress = (manual, city, country, postcode) => ({
|
|
362
|
+
address1: manual.line1 ?? null,
|
|
363
|
+
address2: manual.line2 ?? null,
|
|
364
|
+
address3: manual.line3 ?? null,
|
|
365
|
+
city: city ?? null,
|
|
366
|
+
county: manual.line4 ?? null,
|
|
367
|
+
country: country ?? null,
|
|
368
|
+
postcode: postcode ?? null
|
|
369
|
+
});
|
|
299
370
|
var formatDisplayAddress = (address) => {
|
|
300
371
|
const { lookup, manual, postcode, country, city } = address;
|
|
301
372
|
let addressLines = [];
|
|
@@ -331,30 +402,6 @@ var formatDisplayAddress = (address) => {
|
|
|
331
402
|
country
|
|
332
403
|
];
|
|
333
404
|
};
|
|
334
|
-
var buildAddressLine = (parts) => {
|
|
335
|
-
return parts.filter(Boolean).join(", ") || null;
|
|
336
|
-
};
|
|
337
|
-
var buildStreetLine = (buildingRange, street) => {
|
|
338
|
-
return [buildingRange, street].filter(Boolean).join(" ") || null;
|
|
339
|
-
};
|
|
340
|
-
var formatLookupAddress = (lookup, city, country, postcode) => ({
|
|
341
|
-
address1: buildAddressLine([lookup.pafOrganisationName, lookup.flatName, lookup.buildingName]),
|
|
342
|
-
address2: buildStreetLine(lookup.buildingNumberRange, lookup.street),
|
|
343
|
-
address3: buildAddressLine([lookup.doubleDependentLocality, lookup.dependentLocality]),
|
|
344
|
-
county: lookup.county ?? null,
|
|
345
|
-
city: city ?? null,
|
|
346
|
-
country: country ?? null,
|
|
347
|
-
postcode: postcode ?? null
|
|
348
|
-
});
|
|
349
|
-
var formatManualAddress = (manual, city, country, postcode) => ({
|
|
350
|
-
address1: manual.line1 ?? null,
|
|
351
|
-
address2: manual.line2 ?? null,
|
|
352
|
-
address3: manual.line3 ?? null,
|
|
353
|
-
city: city ?? null,
|
|
354
|
-
county: manual.line4 ?? null,
|
|
355
|
-
country: country ?? null,
|
|
356
|
-
postcode: postcode ?? null
|
|
357
|
-
});
|
|
358
405
|
var formatOriginalAddress = (originalAddress) => {
|
|
359
406
|
const { lookup, manual, city, country, postcode } = originalAddress;
|
|
360
407
|
return lookup.uprn ? formatLookupAddress(lookup, city, country, postcode) : formatManualAddress(manual, city, country, postcode);
|
|
@@ -401,21 +448,6 @@ var formatDisplayAddresses = (addresses, previouslyPickedAddress) => {
|
|
|
401
448
|
});
|
|
402
449
|
return displayAddresses;
|
|
403
450
|
};
|
|
404
|
-
var sortErrorsBySectionOrder = (errors, orderedSectionsToFix, SECTION_FIELD_ORDER) => {
|
|
405
|
-
const sortedErrors = [];
|
|
406
|
-
for (const section of orderedSectionsToFix) {
|
|
407
|
-
const fieldsInSection = SECTION_FIELD_ORDER[section] || [];
|
|
408
|
-
for (const field of fieldsInSection) {
|
|
409
|
-
if (errors[field]) {
|
|
410
|
-
sortedErrors.push({
|
|
411
|
-
field,
|
|
412
|
-
...errors[field]
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
return sortedErrors;
|
|
418
|
-
};
|
|
419
451
|
|
|
420
452
|
// src/presenters/business-details-presenter.js
|
|
421
453
|
var getActionText = (value) => {
|
|
@@ -447,7 +479,9 @@ var presenters = {
|
|
|
447
479
|
getActionText,
|
|
448
480
|
formatCph,
|
|
449
481
|
formatCphText,
|
|
450
|
-
formatBusinessAddress
|
|
482
|
+
formatBusinessAddress,
|
|
483
|
+
addressBackLink,
|
|
484
|
+
addressChangeLink
|
|
451
485
|
};
|
|
452
486
|
|
|
453
487
|
// src/utils/joi.js
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@defra/fcp-sfd-frontend-engine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Shared frontend engine used by both the internal and external frontend applications.",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"exports": {
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"vitest": "4.1.10"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"joi": "18.2.3"
|
|
49
|
+
"joi": "18.2.3",
|
|
50
|
+
"osdatahub": "0.2.2"
|
|
50
51
|
}
|
|
51
52
|
}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Determines the correct link for changing an address.
|
|
3
|
+
*
|
|
4
|
+
* If the user previously selected an address from a postcode lookup, they will
|
|
5
|
+
* navigate to the address change page to choose a different address.
|
|
6
|
+
* Otherwise, they navigate to the manual address entry page.
|
|
7
|
+
*
|
|
8
|
+
* @param {boolean} postcodeLookup - Whether the user used a postcode lookup to find their address
|
|
9
|
+
* @param {string} context - The context for the address change ('business' or 'personal')
|
|
10
|
+
* @returns {string} The URL path for the address change page
|
|
11
|
+
*/
|
|
12
|
+
export const addressChangeLink = (postcodeLookup, context) => {
|
|
13
|
+
if (context === 'business') {
|
|
14
|
+
if (postcodeLookup) {
|
|
15
|
+
return '/business-address-change'
|
|
16
|
+
}
|
|
17
|
+
return '/business-address-enter'
|
|
18
|
+
}
|
|
19
|
+
if (context === 'personal') {
|
|
20
|
+
if (postcodeLookup) {
|
|
21
|
+
return '/account-address-change'
|
|
22
|
+
}
|
|
23
|
+
return '/account-address-enter'
|
|
24
|
+
}
|
|
25
|
+
return null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Generates the back link object for address-related pages.
|
|
30
|
+
*
|
|
31
|
+
* If the user used a postcode lookup, the back link returns to the address selection page.
|
|
32
|
+
* If they manually entered the address, the back link returns to the manual entry page.
|
|
33
|
+
*
|
|
34
|
+
* @param {boolean} postcodeLookup - Whether the user used a postcode lookup to find their address
|
|
35
|
+
* @param {string} context - The context for the address change ('business' or 'personal')
|
|
36
|
+
* @returns {Object} An object with an `href` property containing the back link URL
|
|
37
|
+
*/
|
|
38
|
+
export const addressBackLink = (postcodeLookup, context) => {
|
|
39
|
+
if (context === 'business') {
|
|
40
|
+
if (postcodeLookup) {
|
|
41
|
+
return { href: '/business-address-select' }
|
|
42
|
+
}
|
|
43
|
+
return { href: '/business-address-enter' }
|
|
44
|
+
}
|
|
45
|
+
if (context === 'personal') {
|
|
46
|
+
if (postcodeLookup) {
|
|
47
|
+
return { href: '/account-address-select' }
|
|
48
|
+
}
|
|
49
|
+
return { href: '/account-address-enter' }
|
|
50
|
+
}
|
|
51
|
+
return null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Combines address parts into a single line, joining non-empty values with commas.
|
|
56
|
+
*
|
|
57
|
+
* @private
|
|
58
|
+
*/
|
|
59
|
+
const buildAddressLine = (parts) => {
|
|
60
|
+
return parts.filter(Boolean).join(', ') || null
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Combines building range and street into a single line, joining with a space.
|
|
65
|
+
*
|
|
66
|
+
* @private
|
|
67
|
+
*/
|
|
68
|
+
const buildStreetLine = (buildingRange, street) => {
|
|
69
|
+
return [buildingRange, street].filter(Boolean).join(' ') || null
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Formats a lookup address (from address API) into a consistent flat structure.
|
|
74
|
+
*
|
|
75
|
+
* @private
|
|
76
|
+
*/
|
|
77
|
+
const formatLookupAddress = (lookup, city, country, postcode) => ({
|
|
78
|
+
address1: buildAddressLine([lookup.pafOrganisationName, lookup.flatName, lookup.buildingName]),
|
|
79
|
+
address2: buildStreetLine(lookup.buildingNumberRange, lookup.street),
|
|
80
|
+
address3: buildAddressLine([lookup.doubleDependentLocality, lookup.dependentLocality]),
|
|
81
|
+
county: lookup.county ?? null,
|
|
82
|
+
city: city ?? null,
|
|
83
|
+
country: country ?? null,
|
|
84
|
+
postcode: postcode ?? null
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Formats a manually entered address into a consistent flat structure.
|
|
89
|
+
*
|
|
90
|
+
* @private
|
|
91
|
+
*/
|
|
92
|
+
const formatManualAddress = (manual, city, country, postcode) => ({
|
|
93
|
+
address1: manual.line1 ?? null,
|
|
94
|
+
address2: manual.line2 ?? null,
|
|
95
|
+
address3: manual.line3 ?? null,
|
|
96
|
+
city: city ?? null,
|
|
97
|
+
county: manual.line4 ?? null,
|
|
98
|
+
country: country ?? null,
|
|
99
|
+
postcode: postcode ?? null
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Identify the correct address to use from the DAL response.
|
|
104
|
+
*
|
|
105
|
+
* An address lookup is where a user enters a postcode and selects an address
|
|
106
|
+
* returned from an API. Manual input is when a user chooses to type the address in themselves.
|
|
107
|
+
*
|
|
108
|
+
* If the UPRN is populated, the user has selected an address from the lookup.
|
|
109
|
+
* UPRN is only returned by the lookup API; manual addresses will always have `uprn = null`.
|
|
110
|
+
*
|
|
111
|
+
* If both a building number range and a street are present, they are combined into one line so they display together.
|
|
112
|
+
*
|
|
113
|
+
* City, postcode and country are always appended to the final address array.
|
|
114
|
+
*
|
|
115
|
+
* @param {Object} address - The complete address object
|
|
116
|
+
*
|
|
117
|
+
* @returns {string[]} An array of address fields (either from lookup or manual)
|
|
118
|
+
*
|
|
119
|
+
* @private
|
|
120
|
+
*/
|
|
121
|
+
export const formatDisplayAddress = (address) => {
|
|
122
|
+
const { lookup, manual, postcode, country, city } = address
|
|
123
|
+
|
|
124
|
+
let addressLines = []
|
|
125
|
+
|
|
126
|
+
if (lookup.uprn) {
|
|
127
|
+
// If the uprn is populated then the user has selected an address from the lookup
|
|
128
|
+
const buildingAndStreet = [
|
|
129
|
+
lookup.buildingNumberRange,
|
|
130
|
+
lookup.street
|
|
131
|
+
].filter(Boolean).join(' ')
|
|
132
|
+
|
|
133
|
+
addressLines = [
|
|
134
|
+
lookup.pafOrganisationName,
|
|
135
|
+
lookup.flatName,
|
|
136
|
+
lookup.buildingName,
|
|
137
|
+
buildingAndStreet,
|
|
138
|
+
lookup.doubleDependentLocality,
|
|
139
|
+
lookup.dependentLocality,
|
|
140
|
+
city,
|
|
141
|
+
lookup.county
|
|
142
|
+
]
|
|
143
|
+
} else {
|
|
144
|
+
// Otherwise the user manually entered the address
|
|
145
|
+
addressLines = [
|
|
146
|
+
manual.line1,
|
|
147
|
+
manual.line2,
|
|
148
|
+
manual.line3,
|
|
149
|
+
city,
|
|
150
|
+
manual.line4, // County
|
|
151
|
+
manual.line5
|
|
152
|
+
]
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return [
|
|
156
|
+
...addressLines.filter(Boolean),
|
|
157
|
+
postcode,
|
|
158
|
+
country
|
|
159
|
+
]
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Formats an original address fetched from the DAL into a flattened structure
|
|
164
|
+
* suitable for display or form population on the `address-enter` pages.
|
|
165
|
+
*
|
|
166
|
+
* If the address contains a UPRN, it is treated as a lookup address;
|
|
167
|
+
* otherwise, it is treated as a manually entered address.
|
|
168
|
+
*
|
|
169
|
+
* @param {Object} originalAddress - The full address object from the DAL
|
|
170
|
+
*
|
|
171
|
+
* @returns {Object} A flattened address object with consistent keys
|
|
172
|
+
*/
|
|
173
|
+
export const formatOriginalAddress = (originalAddress) => {
|
|
174
|
+
const { lookup, manual, city, country, postcode } = originalAddress
|
|
175
|
+
return lookup.uprn
|
|
176
|
+
? formatLookupAddress(lookup, city, country, postcode)
|
|
177
|
+
: formatManualAddress(manual, city, country, postcode)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Formats a changed address object into a consistent structure for form display.
|
|
182
|
+
*
|
|
183
|
+
* If the address includes a UPRN, it indicates the user selected it from an address lookup.
|
|
184
|
+
* In that case, the lookup fields are combined and mapped into the manual address format used by the form.
|
|
185
|
+
*
|
|
186
|
+
* If the address does not include a UPRN, it is assumed to be manually entered and returned as-is.
|
|
187
|
+
*
|
|
188
|
+
* @param {Object} changeBusinessAddress - The changed address object to format
|
|
189
|
+
*
|
|
190
|
+
* @returns {Object} A formatted address object with fields `address1`, `address2`, `address3`,
|
|
191
|
+
* `city`, `county`, `country`, and `postcode`.
|
|
192
|
+
*/
|
|
193
|
+
export const formatChangedAddress = (changeBusinessAddress) => {
|
|
194
|
+
if (!changeBusinessAddress.uprn) {
|
|
195
|
+
// manual address (no lookup used)
|
|
196
|
+
return changeBusinessAddress
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const {
|
|
200
|
+
pafOrganisationName,
|
|
201
|
+
flatName,
|
|
202
|
+
buildingName,
|
|
203
|
+
buildingNumberRange,
|
|
204
|
+
street,
|
|
205
|
+
doubleDependentLocality,
|
|
206
|
+
dependentLocality,
|
|
207
|
+
city,
|
|
208
|
+
county,
|
|
209
|
+
country,
|
|
210
|
+
postcode
|
|
211
|
+
} = changeBusinessAddress
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
address1: buildAddressLine([pafOrganisationName, flatName, buildingName]),
|
|
215
|
+
address2: buildStreetLine(buildingNumberRange, street),
|
|
216
|
+
address3: buildAddressLine([doubleDependentLocality, dependentLocality]),
|
|
217
|
+
city: city ?? null,
|
|
218
|
+
county: county ?? null,
|
|
219
|
+
country: country ?? null,
|
|
220
|
+
postcode: postcode ?? null
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Formats a list of address objects for display in a dropdown menu.
|
|
226
|
+
*
|
|
227
|
+
* Each address object is transformed into an option with:
|
|
228
|
+
* - `value`: a concatenation of the address UPRN and displayAddress
|
|
229
|
+
* - `text`: the formatted display address string
|
|
230
|
+
* - `selected`: true only if it matches the previously picked address
|
|
231
|
+
*
|
|
232
|
+
* A summary option is prepended to the start of the list, showing how many
|
|
233
|
+
* addresses were found. This summary option is selected by default unless
|
|
234
|
+
* a previously picked address exists.
|
|
235
|
+
*
|
|
236
|
+
* This function is shared across presenters that display address selection
|
|
237
|
+
* lists (e.g. personal or business address flows).
|
|
238
|
+
*
|
|
239
|
+
* Note: The `value` combines `uprn` and `displayAddress` to ensure uniqueness.
|
|
240
|
+
* Some addresses (for example, postcode LL55 2NF) have been observed to share
|
|
241
|
+
* the same UPRN, which caused incorrect selections when UPRN alone was used.
|
|
242
|
+
* Concatenating both fields guarantees each dropdown option has a unique value.
|
|
243
|
+
*
|
|
244
|
+
* @param {Array<Object>} addresses - List of address objects with `uprn` and `displayAddress` properties
|
|
245
|
+
* @param {Object} [previouslyPickedAddress] - Optional object representing the address previously selected by the user
|
|
246
|
+
*
|
|
247
|
+
* @returns {Array<Object>} Array of formatted address options ready for display
|
|
248
|
+
*/
|
|
249
|
+
export const formatDisplayAddresses = (addresses, previouslyPickedAddress) => {
|
|
250
|
+
const displayAddresses = addresses.map(address => ({
|
|
251
|
+
value: `${address.uprn}${address.displayAddress}`,
|
|
252
|
+
text: address.displayAddress,
|
|
253
|
+
selected:
|
|
254
|
+
previouslyPickedAddress?.uprn === address.uprn &&
|
|
255
|
+
previouslyPickedAddress?.displayAddress === address.displayAddress
|
|
256
|
+
}))
|
|
257
|
+
|
|
258
|
+
// Check if any address is already selected
|
|
259
|
+
const hasSelectedAddress = displayAddresses.some(addr => addr.selected)
|
|
260
|
+
|
|
261
|
+
// Add a display summary option to the beginning of the list
|
|
262
|
+
// e.g. "18 addresses found"
|
|
263
|
+
const text = addresses.length === 1 ? '1 address found' : `${addresses.length} addresses found`
|
|
264
|
+
|
|
265
|
+
displayAddresses.unshift({
|
|
266
|
+
value: 'display',
|
|
267
|
+
text,
|
|
268
|
+
selected: !hasSelectedAddress
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
return displayAddresses
|
|
272
|
+
}
|
|
@@ -37,227 +37,6 @@ export const formatNumber = (payloadNumber, changedNumber, originalNumber) => {
|
|
|
37
37
|
return originalNumber
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
/**
|
|
41
|
-
* Identify the correct address to use from the DAL response.
|
|
42
|
-
*
|
|
43
|
-
* An address lookup is where a user enters a postcode and selects an address
|
|
44
|
-
* returned from an API. Manual input is when a user chooses to type the address in themselves.
|
|
45
|
-
*
|
|
46
|
-
* If the UPRN is populated, the user has selected an address from the lookup.
|
|
47
|
-
* UPRN is only returned by the lookup API; manual addresses will always have `uprn = null`.
|
|
48
|
-
*
|
|
49
|
-
* If both a building number range and a street are present, they are combined into one line so they display together.
|
|
50
|
-
*
|
|
51
|
-
* City, postcode and country are always appended to the final address array.
|
|
52
|
-
*
|
|
53
|
-
* @param {Object} address - The complete address object
|
|
54
|
-
*
|
|
55
|
-
* @returns {string[]} An array of address fields (either from lookup or manual)
|
|
56
|
-
*
|
|
57
|
-
* @private
|
|
58
|
-
*/
|
|
59
|
-
|
|
60
|
-
export const formatDisplayAddress = (address) => {
|
|
61
|
-
const { lookup, manual, postcode, country, city } = address
|
|
62
|
-
|
|
63
|
-
let addressLines = []
|
|
64
|
-
|
|
65
|
-
if (lookup.uprn) {
|
|
66
|
-
// If the uprn is populated then the user has selected an address from the lookup
|
|
67
|
-
const buildingAndStreet = [
|
|
68
|
-
lookup.buildingNumberRange,
|
|
69
|
-
lookup.street
|
|
70
|
-
].filter(Boolean).join(' ')
|
|
71
|
-
|
|
72
|
-
addressLines = [
|
|
73
|
-
lookup.pafOrganisationName,
|
|
74
|
-
lookup.flatName,
|
|
75
|
-
lookup.buildingName,
|
|
76
|
-
buildingAndStreet,
|
|
77
|
-
lookup.doubleDependentLocality,
|
|
78
|
-
lookup.dependentLocality,
|
|
79
|
-
city,
|
|
80
|
-
lookup.county
|
|
81
|
-
]
|
|
82
|
-
} else {
|
|
83
|
-
// Otherwise the user manually entered the address
|
|
84
|
-
addressLines = [
|
|
85
|
-
manual.line1,
|
|
86
|
-
manual.line2,
|
|
87
|
-
manual.line3,
|
|
88
|
-
city,
|
|
89
|
-
manual.line4, // County
|
|
90
|
-
manual.line5
|
|
91
|
-
]
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return [
|
|
95
|
-
...addressLines.filter(Boolean),
|
|
96
|
-
postcode,
|
|
97
|
-
country
|
|
98
|
-
]
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Combines address parts into a single line, joining non-empty values with commas.
|
|
103
|
-
*
|
|
104
|
-
* @private
|
|
105
|
-
*/
|
|
106
|
-
const buildAddressLine = (parts) => {
|
|
107
|
-
return parts.filter(Boolean).join(', ') || null
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Combines building range and street into a single line, joining with a space.
|
|
112
|
-
*
|
|
113
|
-
* @private
|
|
114
|
-
*/
|
|
115
|
-
const buildStreetLine = (buildingRange, street) => {
|
|
116
|
-
return [buildingRange, street].filter(Boolean).join(' ') || null
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Formats a lookup address (from address API) into a consistent flat structure.
|
|
121
|
-
*
|
|
122
|
-
* @private
|
|
123
|
-
*/
|
|
124
|
-
const formatLookupAddress = (lookup, city, country, postcode) => ({
|
|
125
|
-
address1: buildAddressLine([lookup.pafOrganisationName, lookup.flatName, lookup.buildingName]),
|
|
126
|
-
address2: buildStreetLine(lookup.buildingNumberRange, lookup.street),
|
|
127
|
-
address3: buildAddressLine([lookup.doubleDependentLocality, lookup.dependentLocality]),
|
|
128
|
-
county: lookup.county ?? null,
|
|
129
|
-
city: city ?? null,
|
|
130
|
-
country: country ?? null,
|
|
131
|
-
postcode: postcode ?? null
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Formats a manually entered address into a consistent flat structure.
|
|
136
|
-
*
|
|
137
|
-
* @private
|
|
138
|
-
*/
|
|
139
|
-
const formatManualAddress = (manual, city, country, postcode) => ({
|
|
140
|
-
address1: manual.line1 ?? null,
|
|
141
|
-
address2: manual.line2 ?? null,
|
|
142
|
-
address3: manual.line3 ?? null,
|
|
143
|
-
city: city ?? null,
|
|
144
|
-
county: manual.line4 ?? null,
|
|
145
|
-
country: country ?? null,
|
|
146
|
-
postcode: postcode ?? null
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Formats an original address fetched from the DAL into a flattened structure
|
|
151
|
-
* suitable for display or form population on the `address-enter` pages.
|
|
152
|
-
*
|
|
153
|
-
* If the address contains a UPRN, it is treated as a lookup address;
|
|
154
|
-
* otherwise, it is treated as a manually entered address.
|
|
155
|
-
*
|
|
156
|
-
* @param {Object} originalAddress - The full address object from the DAL
|
|
157
|
-
*
|
|
158
|
-
* @returns {Object} A flattened address object with consistent keys
|
|
159
|
-
*/
|
|
160
|
-
export const formatOriginalAddress = (originalAddress) => {
|
|
161
|
-
const { lookup, manual, city, country, postcode } = originalAddress
|
|
162
|
-
return lookup.uprn
|
|
163
|
-
? formatLookupAddress(lookup, city, country, postcode)
|
|
164
|
-
: formatManualAddress(manual, city, country, postcode)
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Formats a changed address object into a consistent structure for form display.
|
|
169
|
-
*
|
|
170
|
-
* If the address includes a UPRN, it indicates the user selected it from an address lookup.
|
|
171
|
-
* In that case, the lookup fields are combined and mapped into the manual address format used by the form.
|
|
172
|
-
*
|
|
173
|
-
* If the address does not include a UPRN, it is assumed to be manually entered and returned as-is.
|
|
174
|
-
*
|
|
175
|
-
* @param {Object} changeBusinessAddress - The changed address object to format
|
|
176
|
-
*
|
|
177
|
-
* @returns {Object} A formatted address object with fields `address1`, `address2`, `address3`,
|
|
178
|
-
* `city`, `county`, `country`, and `postcode`.
|
|
179
|
-
*/
|
|
180
|
-
export const formatChangedAddress = (changeBusinessAddress) => {
|
|
181
|
-
if (!changeBusinessAddress.uprn) {
|
|
182
|
-
// manual address (no lookup used)
|
|
183
|
-
return changeBusinessAddress
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const {
|
|
187
|
-
pafOrganisationName,
|
|
188
|
-
flatName,
|
|
189
|
-
buildingName,
|
|
190
|
-
buildingNumberRange,
|
|
191
|
-
street,
|
|
192
|
-
doubleDependentLocality,
|
|
193
|
-
dependentLocality,
|
|
194
|
-
city,
|
|
195
|
-
county,
|
|
196
|
-
country,
|
|
197
|
-
postcode
|
|
198
|
-
} = changeBusinessAddress
|
|
199
|
-
|
|
200
|
-
return {
|
|
201
|
-
address1: buildAddressLine([pafOrganisationName, flatName, buildingName]),
|
|
202
|
-
address2: buildStreetLine(buildingNumberRange, street),
|
|
203
|
-
address3: buildAddressLine([doubleDependentLocality, dependentLocality]),
|
|
204
|
-
city: city ?? null,
|
|
205
|
-
county: county ?? null,
|
|
206
|
-
country: country ?? null,
|
|
207
|
-
postcode: postcode ?? null
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Formats a list of address objects for display in a dropdown menu.
|
|
213
|
-
*
|
|
214
|
-
* Each address object is transformed into an option with:
|
|
215
|
-
* - `value`: a concatenation of the address UPRN and displayAddress
|
|
216
|
-
* - `text`: the formatted display address string
|
|
217
|
-
* - `selected`: true only if it matches the previously picked address
|
|
218
|
-
*
|
|
219
|
-
* A summary option is prepended to the start of the list, showing how many
|
|
220
|
-
* addresses were found. This summary option is selected by default unless
|
|
221
|
-
* a previously picked address exists.
|
|
222
|
-
*
|
|
223
|
-
* This function is shared across presenters that display address selection
|
|
224
|
-
* lists (e.g. personal or business address flows).
|
|
225
|
-
*
|
|
226
|
-
* Note: The `value` combines `uprn` and `displayAddress` to ensure uniqueness.
|
|
227
|
-
* Some addresses (for example, postcode LL55 2NF) have been observed to share
|
|
228
|
-
* the same UPRN, which caused incorrect selections when UPRN alone was used.
|
|
229
|
-
* Concatenating both fields guarantees each dropdown option has a unique value.
|
|
230
|
-
*
|
|
231
|
-
* @param {Array<Object>} addresses - List of address objects with `uprn` and `displayAddress` properties
|
|
232
|
-
* @param {Object} [previouslyPickedAddress] - Optional object representing the address previously selected by the user
|
|
233
|
-
*
|
|
234
|
-
* @returns {Array<Object>} Array of formatted address options ready for display
|
|
235
|
-
*/
|
|
236
|
-
export const formatDisplayAddresses = (addresses, previouslyPickedAddress) => {
|
|
237
|
-
const displayAddresses = addresses.map(address => ({
|
|
238
|
-
value: `${address.uprn}${address.displayAddress}`,
|
|
239
|
-
text: address.displayAddress,
|
|
240
|
-
selected:
|
|
241
|
-
previouslyPickedAddress?.uprn === address.uprn &&
|
|
242
|
-
previouslyPickedAddress?.displayAddress === address.displayAddress
|
|
243
|
-
}))
|
|
244
|
-
|
|
245
|
-
// Check if any address is already selected
|
|
246
|
-
const hasSelectedAddress = displayAddresses.some(addr => addr.selected)
|
|
247
|
-
|
|
248
|
-
// Add a display summary option to the beginning of the list
|
|
249
|
-
// e.g. "18 addresses found"
|
|
250
|
-
const text = addresses.length === 1 ? '1 address found' : `${addresses.length} addresses found`
|
|
251
|
-
|
|
252
|
-
displayAddresses.unshift({
|
|
253
|
-
value: 'display',
|
|
254
|
-
text,
|
|
255
|
-
selected: !hasSelectedAddress
|
|
256
|
-
})
|
|
257
|
-
|
|
258
|
-
return displayAddresses
|
|
259
|
-
}
|
|
260
|
-
|
|
261
40
|
/**
|
|
262
41
|
* Shared helper used by base presenters to sort validation errors so they
|
|
263
42
|
* appear in the same order as the sections and fields shown on a Fix List page.
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
formatBackLink,
|
|
3
3
|
formatNumber,
|
|
4
|
-
formatDisplayAddress,
|
|
5
|
-
formatOriginalAddress,
|
|
6
|
-
formatChangedAddress,
|
|
7
|
-
formatDisplayAddresses,
|
|
8
4
|
sortErrorsBySectionOrder
|
|
9
5
|
} from './base-presenter.js'
|
|
10
6
|
|
|
@@ -15,6 +11,15 @@ import {
|
|
|
15
11
|
formatBusinessAddress
|
|
16
12
|
} from './business-details-presenter.js'
|
|
17
13
|
|
|
14
|
+
import {
|
|
15
|
+
addressBackLink,
|
|
16
|
+
addressChangeLink,
|
|
17
|
+
formatDisplayAddress,
|
|
18
|
+
formatOriginalAddress,
|
|
19
|
+
formatChangedAddress,
|
|
20
|
+
formatDisplayAddresses
|
|
21
|
+
} from './address-presenter.js'
|
|
22
|
+
|
|
18
23
|
export const presenters = {
|
|
19
24
|
formatBackLink,
|
|
20
25
|
formatNumber,
|
|
@@ -26,5 +31,7 @@ export const presenters = {
|
|
|
26
31
|
getActionText,
|
|
27
32
|
formatCph,
|
|
28
33
|
formatCphText,
|
|
29
|
-
formatBusinessAddress
|
|
34
|
+
formatBusinessAddress,
|
|
35
|
+
addressBackLink,
|
|
36
|
+
addressChangeLink
|
|
30
37
|
}
|