@defra/fcp-sfd-frontend-engine 0.3.0 → 0.5.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 +384 -7
- package/dist/index.js +384 -7
- package/package.json +1 -1
- package/src/constants/index.js +16 -0
- package/src/constants/month-map.js +26 -0
- package/src/constants/patterns.js +1 -0
- package/src/constants/validation-fields-export.js +17 -0
- package/src/constants/validation-fields.js +15 -0
- package/src/mappers/business-details-mapper.js +48 -0
- package/src/mappers/mappers.js +3 -1
- package/src/presenters/business-details-presenter.js +67 -0
- package/src/presenters/presenters.js +12 -1
- package/src/schemas/address-schema.js +61 -0
- package/src/schemas/business/business-email-schema.js +19 -0
- package/src/schemas/business/business-name-schema.js +13 -0
- package/src/schemas/business/business-phone-schema.js +33 -0
- package/src/schemas/business/business-schemas.js +14 -1
- package/src/schemas/business/business-vat-schema.js +11 -0
- package/src/schemas/personal/personal-dob-schema.js +178 -0
- package/src/schemas/personal/personal-email-schema.js +20 -0
- package/src/schemas/personal/personal-name-schema.js +31 -0
- package/src/schemas/personal/personal-phone-schema.js +33 -0
- package/src/schemas/personal/personal-schemas.js +13 -0
- package/src/schemas/schemas.js +3 -1
package/dist/index.js
CHANGED
|
@@ -49,11 +49,46 @@ var mapCustomerName = (name = {}) => {
|
|
|
49
49
|
};
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
+
// src/mappers/business-details-mapper.js
|
|
53
|
+
var asNullable = (value) => value ?? null;
|
|
54
|
+
var mapBusinessInfo = (business) => {
|
|
55
|
+
var _a, _b;
|
|
56
|
+
const info = business.info ?? {};
|
|
57
|
+
return {
|
|
58
|
+
sbi: business.sbi,
|
|
59
|
+
businessName: asNullable(info.name),
|
|
60
|
+
vat: asNullable(info.vat),
|
|
61
|
+
traderNumber: asNullable(info.traderNumber),
|
|
62
|
+
vendorNumber: asNullable(info.vendorNumber),
|
|
63
|
+
legalStatus: asNullable((_a = info.legalStatus) == null ? void 0 : _a.type),
|
|
64
|
+
type: asNullable((_b = info.type) == null ? void 0 : _b.type),
|
|
65
|
+
countyParishHoldingNumbers: business.countyParishHoldings ?? []
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
var mapBusinessContact = (businessInfo) => {
|
|
69
|
+
var _a, _b, _c;
|
|
70
|
+
return {
|
|
71
|
+
email: asNullable((_a = businessInfo.email) == null ? void 0 : _a.address),
|
|
72
|
+
landline: asNullable((_b = businessInfo.phone) == null ? void 0 : _b.landline),
|
|
73
|
+
mobile: asNullable((_c = businessInfo.phone) == null ? void 0 : _c.mobile)
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
var mapBusinessDetails = (value) => {
|
|
77
|
+
const business = (value == null ? void 0 : value.business) ?? {};
|
|
78
|
+
const businessInfo = business.info ?? {};
|
|
79
|
+
return {
|
|
80
|
+
info: mapBusinessInfo(business),
|
|
81
|
+
address: mapAddress(businessInfo.address),
|
|
82
|
+
contact: mapBusinessContact(businessInfo)
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
|
|
52
86
|
// src/mappers/mappers.js
|
|
53
87
|
var mappers = {
|
|
54
88
|
personalBusinessDetails: mapPersonalBusinessDetails,
|
|
55
89
|
address: mapAddress,
|
|
56
|
-
customerName: mapCustomerName
|
|
90
|
+
customerName: mapCustomerName,
|
|
91
|
+
businessDetails: mapBusinessDetails
|
|
57
92
|
};
|
|
58
93
|
|
|
59
94
|
// src/schemas/business/business-sbi-schema.js
|
|
@@ -64,15 +99,129 @@ var businessSbiSchema = Joi.object({
|
|
|
64
99
|
})
|
|
65
100
|
});
|
|
66
101
|
|
|
102
|
+
// src/schemas/address-schema.js
|
|
103
|
+
import Joi2 from "joi";
|
|
104
|
+
|
|
105
|
+
// src/constants/validation-fields.js
|
|
106
|
+
var FIRST_NAME_MAX = 100;
|
|
107
|
+
var LAST_NAME_MAX = 100;
|
|
108
|
+
var MIDDLE_NAMES_MAX = 100;
|
|
109
|
+
var BUSINESS_NAME_MAX = 160;
|
|
110
|
+
var EMAIL_MAX = 254;
|
|
111
|
+
var PHONE_NUMBER_MIN = 10;
|
|
112
|
+
var PHONE_NUMBER_MAX = 50;
|
|
113
|
+
var MAX_AGE_YEARS = 120;
|
|
114
|
+
var ADDRESS_LINE_MAX = 100;
|
|
115
|
+
var TOWN_CITY_MAX = 60;
|
|
116
|
+
var COUNTY_MAX = 60;
|
|
117
|
+
var COUNTRY_MAX = 60;
|
|
118
|
+
var POSTCODE_MAX = 8;
|
|
119
|
+
|
|
120
|
+
// src/schemas/address-schema.js
|
|
121
|
+
var addressSchema = Joi2.object({
|
|
122
|
+
address1: Joi2.string().required().max(ADDRESS_LINE_MAX).messages({
|
|
123
|
+
"string.empty": "Enter address line 1, typically the building and street",
|
|
124
|
+
"string.max": `Address line 1 must be ${ADDRESS_LINE_MAX} characters or less`,
|
|
125
|
+
"any.required": "Enter address line 1, typically the building and street"
|
|
126
|
+
}),
|
|
127
|
+
address2: Joi2.string().allow("").max(ADDRESS_LINE_MAX).messages({
|
|
128
|
+
"string.max": `Address line 2 must be ${ADDRESS_LINE_MAX} characters or less`
|
|
129
|
+
}),
|
|
130
|
+
address3: Joi2.string().allow("").max(ADDRESS_LINE_MAX).messages({
|
|
131
|
+
"string.max": `Address line 3 must be ${ADDRESS_LINE_MAX} characters or less`
|
|
132
|
+
}),
|
|
133
|
+
city: Joi2.string().required().max(TOWN_CITY_MAX).messages({
|
|
134
|
+
"string.empty": "Enter town or city",
|
|
135
|
+
"string.max": `Town or city must be ${TOWN_CITY_MAX} characters or less`,
|
|
136
|
+
"any.required": "Enter town or city"
|
|
137
|
+
}),
|
|
138
|
+
county: Joi2.string().allow("").max(COUNTY_MAX).messages({
|
|
139
|
+
"string.max": `County must be ${COUNTY_MAX} characters or less`
|
|
140
|
+
}),
|
|
141
|
+
postcode: Joi2.string().required().max(POSTCODE_MAX).messages({
|
|
142
|
+
"any.required": "Enter a postal code or zip code",
|
|
143
|
+
"string.empty": "Enter a postal code or zip code",
|
|
144
|
+
"string.max": `Postal code or zip code must be ${POSTCODE_MAX} characters or less`
|
|
145
|
+
}),
|
|
146
|
+
country: Joi2.string().required().max(COUNTRY_MAX).messages({
|
|
147
|
+
"string.empty": "Enter a country",
|
|
148
|
+
"string.max": `Country must be ${COUNTRY_MAX} characters or less`,
|
|
149
|
+
"any.required": "Enter a country"
|
|
150
|
+
})
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// src/schemas/business/business-name-schema.js
|
|
154
|
+
import Joi3 from "joi";
|
|
155
|
+
var businessNameSchema = Joi3.object({
|
|
156
|
+
businessName: Joi3.string().required().max(BUSINESS_NAME_MAX).messages({
|
|
157
|
+
"string.empty": "Enter business name",
|
|
158
|
+
"string.max": `Business name must be ${BUSINESS_NAME_MAX} characters or less`,
|
|
159
|
+
"any.required": "Enter business name"
|
|
160
|
+
})
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// src/schemas/business/business-email-schema.js
|
|
164
|
+
import Joi4 from "joi";
|
|
165
|
+
var businessEmailSchema = Joi4.object({
|
|
166
|
+
businessEmail: Joi4.string().required().max(EMAIL_MAX).email({
|
|
167
|
+
minDomainSegments: 2,
|
|
168
|
+
tlds: {
|
|
169
|
+
allow: true
|
|
170
|
+
}
|
|
171
|
+
}).messages({
|
|
172
|
+
"string.max": `Business email address must be ${EMAIL_MAX} characters or less`,
|
|
173
|
+
"string.empty": "Enter business email address",
|
|
174
|
+
"string.email": "Enter an email address, like name@example.com"
|
|
175
|
+
})
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// src/schemas/business/business-phone-schema.js
|
|
179
|
+
import Joi5 from "joi";
|
|
180
|
+
|
|
181
|
+
// src/constants/patterns.js
|
|
182
|
+
var PHONE_NUMBER_PATTERN = /^\+?[0-9 ()]+$/;
|
|
183
|
+
|
|
184
|
+
// src/schemas/business/business-phone-schema.js
|
|
185
|
+
var businessPhoneSchema = Joi5.object({
|
|
186
|
+
businessTelephone: Joi5.string().empty("").min(PHONE_NUMBER_MIN).max(PHONE_NUMBER_MAX).pattern(PHONE_NUMBER_PATTERN).messages({
|
|
187
|
+
"string.min": `Business telephone number must be ${PHONE_NUMBER_MIN} characters or more`,
|
|
188
|
+
"string.max": `Business telephone number must be ${PHONE_NUMBER_MAX} characters or less`,
|
|
189
|
+
"string.pattern.base": "Business telephone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +"
|
|
190
|
+
}),
|
|
191
|
+
businessMobile: Joi5.string().empty("").min(PHONE_NUMBER_MIN).max(PHONE_NUMBER_MAX).pattern(PHONE_NUMBER_PATTERN).messages({
|
|
192
|
+
"string.min": `Business mobile phone number must be ${PHONE_NUMBER_MIN} characters or more`,
|
|
193
|
+
"string.max": `Business mobile phone number must be ${PHONE_NUMBER_MAX} characters or less`,
|
|
194
|
+
"string.pattern.base": "Business mobile phone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +"
|
|
195
|
+
})
|
|
196
|
+
}).or("businessTelephone", "businessMobile").messages({
|
|
197
|
+
"object.missing": "Enter at least one phone number"
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// src/schemas/business/business-vat-schema.js
|
|
201
|
+
import Joi6 from "joi";
|
|
202
|
+
var businessVatSchema = Joi6.object({
|
|
203
|
+
vatNumber: Joi6.string().pattern(/^\d{9}$/).allow("").optional().messages({
|
|
204
|
+
"string.pattern.base": "Enter a VAT registration number, like 123456789"
|
|
205
|
+
})
|
|
206
|
+
});
|
|
207
|
+
|
|
67
208
|
// src/schemas/business/business-schemas.js
|
|
68
209
|
var businessSchemas = {
|
|
69
|
-
sbi: businessSbiSchema
|
|
210
|
+
sbi: businessSbiSchema,
|
|
211
|
+
address: addressSchema,
|
|
212
|
+
details: {
|
|
213
|
+
name: businessNameSchema,
|
|
214
|
+
address: addressSchema,
|
|
215
|
+
phone: businessPhoneSchema,
|
|
216
|
+
email: businessEmailSchema,
|
|
217
|
+
vat: businessVatSchema
|
|
218
|
+
}
|
|
70
219
|
};
|
|
71
220
|
|
|
72
221
|
// src/schemas/customer/customer-crn-schema.js
|
|
73
|
-
import
|
|
74
|
-
var customerCrnSchema =
|
|
75
|
-
crn:
|
|
222
|
+
import Joi7 from "joi";
|
|
223
|
+
var customerCrnSchema = Joi7.object({
|
|
224
|
+
crn: Joi7.string().pattern(/^\d{10}$/).allow("").optional().messages({
|
|
76
225
|
"string.pattern.base": "Enter the full CRN"
|
|
77
226
|
})
|
|
78
227
|
});
|
|
@@ -82,10 +231,216 @@ var customerSchemas = {
|
|
|
82
231
|
crn: customerCrnSchema
|
|
83
232
|
};
|
|
84
233
|
|
|
234
|
+
// src/schemas/personal/personal-name-schema.js
|
|
235
|
+
import Joi8 from "joi";
|
|
236
|
+
var personalNameSchema = Joi8.object({
|
|
237
|
+
first: Joi8.string().required().max(FIRST_NAME_MAX).messages({
|
|
238
|
+
"string.empty": "Enter first name",
|
|
239
|
+
"string.max": `First name must be ${FIRST_NAME_MAX} characters or less`,
|
|
240
|
+
"any.required": "Enter first name"
|
|
241
|
+
}),
|
|
242
|
+
last: Joi8.string().required().max(LAST_NAME_MAX).messages({
|
|
243
|
+
"string.empty": "Enter last name",
|
|
244
|
+
"string.max": `Last name must be ${LAST_NAME_MAX} characters or less`,
|
|
245
|
+
"any.required": "Enter last name"
|
|
246
|
+
}),
|
|
247
|
+
middle: Joi8.string().allow("").max(MIDDLE_NAMES_MAX).messages({
|
|
248
|
+
"string.max": `Middle names must be ${MIDDLE_NAMES_MAX} characters or less`
|
|
249
|
+
})
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// src/schemas/personal/personal-dob-schema.js
|
|
253
|
+
import Joi9 from "joi";
|
|
254
|
+
|
|
255
|
+
// src/constants/month-map.js
|
|
256
|
+
var MONTH_MAP = {
|
|
257
|
+
january: 1,
|
|
258
|
+
jan: 1,
|
|
259
|
+
february: 2,
|
|
260
|
+
feb: 2,
|
|
261
|
+
march: 3,
|
|
262
|
+
mar: 3,
|
|
263
|
+
april: 4,
|
|
264
|
+
apr: 4,
|
|
265
|
+
may: 5,
|
|
266
|
+
june: 6,
|
|
267
|
+
jun: 6,
|
|
268
|
+
july: 7,
|
|
269
|
+
jul: 7,
|
|
270
|
+
august: 8,
|
|
271
|
+
aug: 8,
|
|
272
|
+
september: 9,
|
|
273
|
+
sep: 9,
|
|
274
|
+
sept: 9,
|
|
275
|
+
october: 10,
|
|
276
|
+
oct: 10,
|
|
277
|
+
november: 11,
|
|
278
|
+
nov: 11,
|
|
279
|
+
december: 12,
|
|
280
|
+
dec: 12
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
// src/schemas/personal/personal-dob-schema.js
|
|
284
|
+
var personalDobSchema = Joi9.object({
|
|
285
|
+
day: Joi9.string().allow(""),
|
|
286
|
+
month: Joi9.string().allow(""),
|
|
287
|
+
year: Joi9.string().allow("")
|
|
288
|
+
}).custom((value, helpers) => {
|
|
289
|
+
const { day, month, year } = value;
|
|
290
|
+
const missingFieldsError = checkMissingFields(day, month, year, helpers);
|
|
291
|
+
if (missingFieldsError) {
|
|
292
|
+
return missingFieldsError;
|
|
293
|
+
}
|
|
294
|
+
if (year && year.length !== 4) {
|
|
295
|
+
return makeError(helpers, "dob.yearLength", ["year"]);
|
|
296
|
+
}
|
|
297
|
+
const monthValue = getMonthNumber(month, helpers);
|
|
298
|
+
if (monthValue.isJoiError) {
|
|
299
|
+
return monthValue;
|
|
300
|
+
}
|
|
301
|
+
const fullDate = getFullDate(day, monthValue, year, helpers);
|
|
302
|
+
if (fullDate.isJoiError) {
|
|
303
|
+
return fullDate;
|
|
304
|
+
}
|
|
305
|
+
if (fullDate > /* @__PURE__ */ new Date()) {
|
|
306
|
+
return makeError(helpers, "dob.future", ["day", "month", "year"]);
|
|
307
|
+
}
|
|
308
|
+
const tooOldError = checkNotTooOld(fullDate, helpers);
|
|
309
|
+
if (tooOldError) {
|
|
310
|
+
return tooOldError;
|
|
311
|
+
}
|
|
312
|
+
return value;
|
|
313
|
+
}).messages({
|
|
314
|
+
"dob.missingAll": "Enter your date of birth",
|
|
315
|
+
"dob.missingDay": "Date of birth must include a day",
|
|
316
|
+
"dob.missingMonth": "Date of birth must include a month",
|
|
317
|
+
"dob.missingYear": "Date of birth must include a year",
|
|
318
|
+
"dob.missingDayMonth": "Date of birth must include a day and month",
|
|
319
|
+
"dob.missingDayYear": "Date of birth must include a day and year",
|
|
320
|
+
"dob.missingMonthYear": "Date of birth must include a month and year",
|
|
321
|
+
"dob.yearLength": "Enter a year with 4 numbers, like 1975",
|
|
322
|
+
"dob.invalid": "Date of birth must be a real date",
|
|
323
|
+
"dob.future": "Date of birth must be in the past",
|
|
324
|
+
"dob.tooOld": "Date of birth must be on or after {{#oldest}}"
|
|
325
|
+
});
|
|
326
|
+
var checkNotTooOld = (fullDate, helpers) => {
|
|
327
|
+
const oldestDateAllowed = getOldestAllowedDate();
|
|
328
|
+
if (fullDate < oldestDateAllowed) {
|
|
329
|
+
const oldestDateString = oldestDateAllowed.toLocaleDateString("en-GB", {
|
|
330
|
+
day: "numeric",
|
|
331
|
+
month: "long",
|
|
332
|
+
year: "numeric"
|
|
333
|
+
});
|
|
334
|
+
const error = helpers.error("dob.tooOld", { oldest: oldestDateString });
|
|
335
|
+
error.path = ["day", "month", "year"];
|
|
336
|
+
return error;
|
|
337
|
+
}
|
|
338
|
+
return null;
|
|
339
|
+
};
|
|
340
|
+
var getOldestAllowedDate = () => {
|
|
341
|
+
const date = /* @__PURE__ */ new Date();
|
|
342
|
+
date.setUTCHours(0, 0, 0, 0);
|
|
343
|
+
date.setUTCFullYear(date.getUTCFullYear() - MAX_AGE_YEARS);
|
|
344
|
+
return date;
|
|
345
|
+
};
|
|
346
|
+
var getFullDate = (day, monthValue, year, helpers) => {
|
|
347
|
+
const dayValue = Number.parseInt(day, 10);
|
|
348
|
+
const yearValue = Number.parseInt(year, 10);
|
|
349
|
+
const date = new Date(Date.UTC(yearValue, monthValue - 1, dayValue));
|
|
350
|
+
if (date.getUTCFullYear() !== yearValue || date.getUTCMonth() + 1 !== monthValue || date.getUTCDate() !== dayValue) {
|
|
351
|
+
return makeError(helpers, "dob.invalid", ["day", "month", "year"]);
|
|
352
|
+
}
|
|
353
|
+
return date;
|
|
354
|
+
};
|
|
355
|
+
var getMonthNumber = (month, helpers) => {
|
|
356
|
+
if (Number.isNaN(Number(month))) {
|
|
357
|
+
const lower = month.toLowerCase();
|
|
358
|
+
const mapped = MONTH_MAP[lower];
|
|
359
|
+
if (!mapped) {
|
|
360
|
+
return makeError(helpers, "dob.invalid", ["month"]);
|
|
361
|
+
}
|
|
362
|
+
return mapped;
|
|
363
|
+
}
|
|
364
|
+
return Number.parseInt(month, 10);
|
|
365
|
+
};
|
|
366
|
+
var checkMissingFields = (day, month, year, helpers) => {
|
|
367
|
+
if (!day && !month && !year) {
|
|
368
|
+
return makeError(helpers, "dob.missingAll", ["day", "month", "year"]);
|
|
369
|
+
}
|
|
370
|
+
if (!day && month && year) {
|
|
371
|
+
return makeError(helpers, "dob.missingDay", ["day"]);
|
|
372
|
+
}
|
|
373
|
+
if (day && !month && year) {
|
|
374
|
+
return makeError(helpers, "dob.missingMonth", ["month"]);
|
|
375
|
+
}
|
|
376
|
+
if (day && month && !year) {
|
|
377
|
+
return makeError(helpers, "dob.missingYear", ["year"]);
|
|
378
|
+
}
|
|
379
|
+
if (!day && !month && year) {
|
|
380
|
+
return makeError(helpers, "dob.missingDayMonth", ["day", "month"]);
|
|
381
|
+
}
|
|
382
|
+
if (!day && month && !year) {
|
|
383
|
+
return makeError(helpers, "dob.missingDayYear", ["day", "year"]);
|
|
384
|
+
}
|
|
385
|
+
if (day && !month && !year) {
|
|
386
|
+
return makeError(helpers, "dob.missingMonthYear", ["month", "year"]);
|
|
387
|
+
}
|
|
388
|
+
return null;
|
|
389
|
+
};
|
|
390
|
+
var makeError = (helpers, code, fields) => {
|
|
391
|
+
const error = helpers.error(code);
|
|
392
|
+
error.path = fields;
|
|
393
|
+
error.isJoiError = true;
|
|
394
|
+
return error;
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
// src/schemas/personal/personal-email-schema.js
|
|
398
|
+
import Joi10 from "joi";
|
|
399
|
+
var personalEmailSchema = Joi10.object({
|
|
400
|
+
personalEmail: Joi10.string().required().max(EMAIL_MAX).email({
|
|
401
|
+
minDomainSegments: 2,
|
|
402
|
+
tlds: {
|
|
403
|
+
allow: true,
|
|
404
|
+
min: 2
|
|
405
|
+
}
|
|
406
|
+
}).messages({
|
|
407
|
+
"string.max": `Email address must be ${EMAIL_MAX} characters or less`,
|
|
408
|
+
"string.empty": "Enter a personal email address",
|
|
409
|
+
"string.email": "Enter an email address, like name@example.com"
|
|
410
|
+
})
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
// src/schemas/personal/personal-phone-schema.js
|
|
414
|
+
import Joi11 from "joi";
|
|
415
|
+
var personalPhoneSchema = Joi11.object({
|
|
416
|
+
personalTelephone: Joi11.string().empty("").min(PHONE_NUMBER_MIN).max(PHONE_NUMBER_MAX).pattern(PHONE_NUMBER_PATTERN).messages({
|
|
417
|
+
"string.min": `Personal telephone number must be ${PHONE_NUMBER_MIN} characters or more`,
|
|
418
|
+
"string.max": `Personal telephone number must be ${PHONE_NUMBER_MAX} characters or less`,
|
|
419
|
+
"string.pattern.base": "Personal telephone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +"
|
|
420
|
+
}),
|
|
421
|
+
personalMobile: Joi11.string().empty("").min(PHONE_NUMBER_MIN).max(PHONE_NUMBER_MAX).pattern(PHONE_NUMBER_PATTERN).messages({
|
|
422
|
+
"string.min": `Personal mobile phone number must be ${PHONE_NUMBER_MIN} characters or more`,
|
|
423
|
+
"string.max": `Personal mobile phone number must be ${PHONE_NUMBER_MAX} characters or less`,
|
|
424
|
+
"string.pattern.base": "Personal mobile phone number must only include numbers 0 to 9 and special characters such as spaces, brackets and +"
|
|
425
|
+
})
|
|
426
|
+
}).or("personalTelephone", "personalMobile").messages({
|
|
427
|
+
"object.missing": "Enter at least one phone number"
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
// src/schemas/personal/personal-schemas.js
|
|
431
|
+
var personalSchemas = {
|
|
432
|
+
name: personalNameSchema,
|
|
433
|
+
dob: personalDobSchema,
|
|
434
|
+
address: addressSchema,
|
|
435
|
+
phone: personalPhoneSchema,
|
|
436
|
+
email: personalEmailSchema
|
|
437
|
+
};
|
|
438
|
+
|
|
85
439
|
// src/schemas/schemas.js
|
|
86
440
|
var schemas = {
|
|
87
441
|
business: businessSchemas,
|
|
88
|
-
customer: customerSchemas
|
|
442
|
+
customer: customerSchemas,
|
|
443
|
+
personal: personalSchemas
|
|
89
444
|
};
|
|
90
445
|
|
|
91
446
|
// src/utils/format-validation-errors.js
|
|
@@ -257,6 +612,24 @@ var sortErrorsBySectionOrder = (errors, orderedSectionsToFix, SECTION_FIELD_ORDE
|
|
|
257
612
|
return sortedErrors;
|
|
258
613
|
};
|
|
259
614
|
|
|
615
|
+
// src/presenters/business-details-presenter.js
|
|
616
|
+
var getActionText = (value) => {
|
|
617
|
+
return value ? "Change" : "Add";
|
|
618
|
+
};
|
|
619
|
+
var formatCph = (countyParishHoldings) => {
|
|
620
|
+
return (countyParishHoldings || []).filter((cph) => cph == null ? void 0 : cph.cphNumber).map((cph) => cph.cphNumber);
|
|
621
|
+
};
|
|
622
|
+
var formatCphText = (count) => {
|
|
623
|
+
return `County Parish Holding (CPH) number${count !== 1 ? "s" : ""}`;
|
|
624
|
+
};
|
|
625
|
+
var formatBusinessAddress = (businessAddress) => {
|
|
626
|
+
var _a, _b;
|
|
627
|
+
if (((_a = businessAddress == null ? void 0 : businessAddress.lookup) == null ? void 0 : _a.uprn) || ((_b = businessAddress == null ? void 0 : businessAddress.manual) == null ? void 0 : _b.line1)) {
|
|
628
|
+
return formatDisplayAddress(businessAddress);
|
|
629
|
+
}
|
|
630
|
+
return [];
|
|
631
|
+
};
|
|
632
|
+
|
|
260
633
|
// src/presenters/presenters.js
|
|
261
634
|
var presenters = {
|
|
262
635
|
formatBackLink,
|
|
@@ -265,7 +638,11 @@ var presenters = {
|
|
|
265
638
|
formatOriginalAddress,
|
|
266
639
|
formatChangedAddress,
|
|
267
640
|
formatDisplayAddresses,
|
|
268
|
-
sortErrorsBySectionOrder
|
|
641
|
+
sortErrorsBySectionOrder,
|
|
642
|
+
getActionText,
|
|
643
|
+
formatCph,
|
|
644
|
+
formatCphText,
|
|
645
|
+
formatBusinessAddress
|
|
269
646
|
};
|
|
270
647
|
export {
|
|
271
648
|
mappers,
|
package/package.json
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export {
|
|
2
|
+
FIRST_NAME_MAX,
|
|
3
|
+
LAST_NAME_MAX,
|
|
4
|
+
MIDDLE_NAMES_MAX,
|
|
5
|
+
EMAIL_MAX,
|
|
6
|
+
PHONE_NUMBER_MIN,
|
|
7
|
+
PHONE_NUMBER_MAX,
|
|
8
|
+
MAX_AGE_YEARS,
|
|
9
|
+
ADDRESS_LINE_MAX,
|
|
10
|
+
TOWN_CITY_MAX,
|
|
11
|
+
COUNTY_MAX,
|
|
12
|
+
COUNTRY_MAX,
|
|
13
|
+
POSTCODE_MAX,
|
|
14
|
+
PHONE_NUMBER_PATTERN,
|
|
15
|
+
MONTH_MAP
|
|
16
|
+
} from './validation-fields-export.js'
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const MONTH_MAP = {
|
|
2
|
+
january: 1,
|
|
3
|
+
jan: 1,
|
|
4
|
+
february: 2,
|
|
5
|
+
feb: 2,
|
|
6
|
+
march: 3,
|
|
7
|
+
mar: 3,
|
|
8
|
+
april: 4,
|
|
9
|
+
apr: 4,
|
|
10
|
+
may: 5,
|
|
11
|
+
june: 6,
|
|
12
|
+
jun: 6,
|
|
13
|
+
july: 7,
|
|
14
|
+
jul: 7,
|
|
15
|
+
august: 8,
|
|
16
|
+
aug: 8,
|
|
17
|
+
september: 9,
|
|
18
|
+
sep: 9,
|
|
19
|
+
sept: 9,
|
|
20
|
+
october: 10,
|
|
21
|
+
oct: 10,
|
|
22
|
+
november: 11,
|
|
23
|
+
nov: 11,
|
|
24
|
+
december: 12,
|
|
25
|
+
dec: 12
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const PHONE_NUMBER_PATTERN = /^\+?[0-9 ()]+$/
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export {
|
|
2
|
+
FIRST_NAME_MAX,
|
|
3
|
+
LAST_NAME_MAX,
|
|
4
|
+
MIDDLE_NAMES_MAX,
|
|
5
|
+
EMAIL_MAX,
|
|
6
|
+
PHONE_NUMBER_MIN,
|
|
7
|
+
PHONE_NUMBER_MAX,
|
|
8
|
+
MAX_AGE_YEARS,
|
|
9
|
+
ADDRESS_LINE_MAX,
|
|
10
|
+
TOWN_CITY_MAX,
|
|
11
|
+
COUNTY_MAX,
|
|
12
|
+
COUNTRY_MAX,
|
|
13
|
+
POSTCODE_MAX
|
|
14
|
+
} from './validation-fields.js'
|
|
15
|
+
|
|
16
|
+
export { PHONE_NUMBER_PATTERN } from './patterns.js'
|
|
17
|
+
export { MONTH_MAP } from './month-map.js'
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const FIRST_NAME_MAX = 100
|
|
2
|
+
export const LAST_NAME_MAX = 100
|
|
3
|
+
export const MIDDLE_NAMES_MAX = 100
|
|
4
|
+
export const BUSINESS_NAME_MAX = 160
|
|
5
|
+
export const EMAIL_MAX = 254
|
|
6
|
+
export const PHONE_NUMBER_MIN = 10
|
|
7
|
+
export const PHONE_NUMBER_MAX = 50
|
|
8
|
+
export const MAX_AGE_YEARS = 120
|
|
9
|
+
|
|
10
|
+
// Address
|
|
11
|
+
export const ADDRESS_LINE_MAX = 100
|
|
12
|
+
export const TOWN_CITY_MAX = 60
|
|
13
|
+
export const COUNTY_MAX = 60
|
|
14
|
+
export const COUNTRY_MAX = 60
|
|
15
|
+
export const POSTCODE_MAX = 8
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Takes the raw business details data from the DAL and maps it to a more usable format.
|
|
3
|
+
*
|
|
4
|
+
* Maps the core `info`, `address`, and `contact` fields.
|
|
5
|
+
* Callers that need additional fields (e.g. `customer`) should extend the result.
|
|
6
|
+
*
|
|
7
|
+
* @param {Object} value - The raw data from the DAL
|
|
8
|
+
*
|
|
9
|
+
* @returns {Object} Mapped business details with `info`, `address`, and `contact`
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { mapAddress } from './address-mapper.js'
|
|
13
|
+
|
|
14
|
+
const asNullable = (value) => value ?? null
|
|
15
|
+
|
|
16
|
+
const mapBusinessInfo = (business) => {
|
|
17
|
+
const info = business.info ?? {}
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
sbi: business.sbi,
|
|
21
|
+
businessName: asNullable(info.name),
|
|
22
|
+
vat: asNullable(info.vat),
|
|
23
|
+
traderNumber: asNullable(info.traderNumber),
|
|
24
|
+
vendorNumber: asNullable(info.vendorNumber),
|
|
25
|
+
legalStatus: asNullable(info.legalStatus?.type),
|
|
26
|
+
type: asNullable(info.type?.type),
|
|
27
|
+
countyParishHoldingNumbers: business.countyParishHoldings ?? []
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const mapBusinessContact = (businessInfo) => {
|
|
32
|
+
return {
|
|
33
|
+
email: asNullable(businessInfo.email?.address),
|
|
34
|
+
landline: asNullable(businessInfo.phone?.landline),
|
|
35
|
+
mobile: asNullable(businessInfo.phone?.mobile)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const mapBusinessDetails = (value) => {
|
|
40
|
+
const business = value?.business ?? {}
|
|
41
|
+
const businessInfo = business.info ?? {}
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
info: mapBusinessInfo(business),
|
|
45
|
+
address: mapAddress(businessInfo.address),
|
|
46
|
+
contact: mapBusinessContact(businessInfo)
|
|
47
|
+
}
|
|
48
|
+
}
|
package/src/mappers/mappers.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { mapPersonalBusinessDetails } from './personal-business-details-mapper.js'
|
|
2
2
|
import { mapAddress } from './address-mapper.js'
|
|
3
3
|
import { mapCustomerName } from './customer-name-mapper.js'
|
|
4
|
+
import { mapBusinessDetails } from './business-details-mapper.js'
|
|
4
5
|
|
|
5
6
|
export const mappers = {
|
|
6
7
|
personalBusinessDetails: mapPersonalBusinessDetails,
|
|
7
8
|
address: mapAddress,
|
|
8
|
-
customerName: mapCustomerName
|
|
9
|
+
customerName: mapCustomerName,
|
|
10
|
+
businessDetails: mapBusinessDetails
|
|
9
11
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared presenter helpers for the "View and update your business details" page.
|
|
3
|
+
*
|
|
4
|
+
* These functions are identical across the internal and external frontend
|
|
5
|
+
* applications and are provided here to avoid duplication.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { formatDisplayAddress } from './base-presenter.js'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Returns the action text for a summary-list row.
|
|
12
|
+
*
|
|
13
|
+
* Returns 'Change' when a value is already present, or 'Add' when it is absent.
|
|
14
|
+
*
|
|
15
|
+
* @param {*} value - The current field value
|
|
16
|
+
*
|
|
17
|
+
* @returns {'Change'|'Add'}
|
|
18
|
+
*/
|
|
19
|
+
export const getActionText = (value) => {
|
|
20
|
+
return value ? 'Change' : 'Add'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Extracts an array of CPH number strings from the raw DAL response.
|
|
25
|
+
*
|
|
26
|
+
* Filters out any objects that have a null or undefined `cphNumber` property.
|
|
27
|
+
*
|
|
28
|
+
* @param {Array<Object>} countyParishHoldings - Raw CPH objects from the DAL
|
|
29
|
+
*
|
|
30
|
+
* @returns {string[]} Array of CPH number strings
|
|
31
|
+
*/
|
|
32
|
+
export const formatCph = (countyParishHoldings) => {
|
|
33
|
+
return (countyParishHoldings || [])
|
|
34
|
+
.filter((cph) => cph?.cphNumber)
|
|
35
|
+
.map((cph) => cph.cphNumber)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Returns the heading text for the CPH summary-list row.
|
|
40
|
+
*
|
|
41
|
+
* Uses the singular form when `count` is 1, plural otherwise.
|
|
42
|
+
*
|
|
43
|
+
* @param {number} count - Number of CPH numbers
|
|
44
|
+
*
|
|
45
|
+
* @returns {string}
|
|
46
|
+
*/
|
|
47
|
+
export const formatCphText = (count) => {
|
|
48
|
+
return `County Parish Holding (CPH) number${count !== 1 ? 's' : ''}`
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Formats a business address object into an array of display lines.
|
|
53
|
+
*
|
|
54
|
+
* Returns an empty array when the address contains no recognised content
|
|
55
|
+
* (i.e. neither a UPRN from a lookup nor a manually entered first line).
|
|
56
|
+
*
|
|
57
|
+
* @param {Object} businessAddress - The mapped address object
|
|
58
|
+
*
|
|
59
|
+
* @returns {string[]} Array of address lines, or an empty array if absent
|
|
60
|
+
*/
|
|
61
|
+
export const formatBusinessAddress = (businessAddress) => {
|
|
62
|
+
if (businessAddress?.lookup?.uprn || businessAddress?.manual?.line1) {
|
|
63
|
+
return formatDisplayAddress(businessAddress)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return []
|
|
67
|
+
}
|
|
@@ -8,6 +8,13 @@ import {
|
|
|
8
8
|
sortErrorsBySectionOrder
|
|
9
9
|
} from './base-presenter.js'
|
|
10
10
|
|
|
11
|
+
import {
|
|
12
|
+
getActionText,
|
|
13
|
+
formatCph,
|
|
14
|
+
formatCphText,
|
|
15
|
+
formatBusinessAddress
|
|
16
|
+
} from './business-details-presenter.js'
|
|
17
|
+
|
|
11
18
|
export const presenters = {
|
|
12
19
|
formatBackLink,
|
|
13
20
|
formatNumber,
|
|
@@ -15,5 +22,9 @@ export const presenters = {
|
|
|
15
22
|
formatOriginalAddress,
|
|
16
23
|
formatChangedAddress,
|
|
17
24
|
formatDisplayAddresses,
|
|
18
|
-
sortErrorsBySectionOrder
|
|
25
|
+
sortErrorsBySectionOrder,
|
|
26
|
+
getActionText,
|
|
27
|
+
formatCph,
|
|
28
|
+
formatCphText,
|
|
29
|
+
formatBusinessAddress
|
|
19
30
|
}
|