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