Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 79 additions & 87 deletions schemas/post-office-redirection-business.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,141 +4,141 @@
"description": "Change where your mail is sent (business)",
"fields": [
{
"name": "applicant",
"name": "businessName",
"type": "string",
"label": "Business name",
"required": true,
"validations": {
"min": 2,
"message": "Business name is required"
}
},
{
"name": "currentAddress",
"type": "object",
"required": true,
"fields": [
{
"name": "title",
"type": "string",
"label": "Title",
"required": true,
"validations": {
"regex": "^(mr|ms|mrs)$",
"message": "Must select a valid title"
}
},
{
"name": "firstName",
"name": "addressLine1",
"type": "string",
"label": "First name",
"label": "Address line 1",
"required": true,
"validations": {
"min": 2,
"max": 100,
"message": "First name is required"
"min": 5,
"max": 200,
"message": "Address must be at least 5 characters"
}
},
{
"name": "lastName",
"name": "addressLine2",
"type": "string",
"label": "Last name",
"required": true,
"validations": {
"min": 2,
"max": 100,
"message": "Last name is required"
}
},
{
"name": "dateOfBirth",
"type": "date",
"label": "Date of birth",
"label": "Address line 2",
"required": false,
"validations": {
"regex": "^\\d{4}-\\d{2}-\\d{2}$",
"message": "Date of birth is required and must be in YYYY-MM-DD format"
"max": 200
}
},
{
"name": "idNumber",
"name": "parish",
"type": "string",
"label": "National Identification (ID) Number",
"required": false,
"label": "Parish",
"required": true,
"validations": {
"min": 2,
"message": "ID Number must be at least 2 characters"
"regex": "^(christ-church|st-andrew|st-george|st-james|st-john|st-joseph|st-lucy|st-michael|st-peter|st-philip|st-thomas)$",
"message": "Must select a valid parish"
}
},
{
"name": "passportNumber",
"name": "postcode",
"type": "string",
"label": "Passport Number",
"label": "Postcode",
"required": false,
"validations": {
"message": "Passport number must be at least 6 characters"
"regex": "^BB\\d{5}$",
"message": "Enter a valid postal code (e.g., BB17004)"
}
},
{
"name": "email",
"type": "email",
"label": "Email Address",
"required": true
},
{
"name": "telephoneNumber",
"type": "string",
"label": "Telephone Number",
"required": true
}
]
},
{
"name": "oldBusinessAddress",
"name": "applicant",
"type": "object",
"required": true,
"fields": [
{
"name": "addressLine1",
"name": "title",
"type": "string",
"label": "Address Line 1",
"label": "Title",
"required": true,
"validations": {
"min": 5,
"max": 200,
"message": "Address must be at least 5 characters"
"regex": "^(mr|ms|mrs)$",
"message": "Must select a valid title"
}
},
{
"name": "addressLine2",
"name": "firstName",
"type": "string",
"label": "Address Line 2",
"required": false,
"label": "First name",
"required": true,
"validations": {
"max": 200
"min": 2,
"max": 100,
"message": "First name is required"
}
},
{
"name": "parish",
"name": "middleName",
"type": "string",
"label": "Parish",
"required": true,
"label": "Middle name",
"required": false,
"validations": {
"regex": "^(christ-church|st-andrew|st-george|st-james|st-john|st-joseph|st-lucy|st-michael|st-peter|st-philip|st-thomas)$",
"message": "Must select a valid parish"
"max": 100,
"message": "Middle name is required"
}
},
{
"name": "postalCode",
"name": "lastName",
"type": "string",
"label": "Postal Code",
"required": false,
"label": "Last name",
"required": true,
"validations": {
"regex": "^BB\\d{5}$",
"message": "Enter a valid postal code (e.g., BB17004)"
"min": 2,
"max": 100,
"message": "Last name is required"
}
},
{
"name": "email",
"type": "email",
"label": "Email Address",
"required": true
},
{
"name": "telephoneNumber",
"type": "string",
"label": "Telephone Number",
"required": true
}
]
},
{
"name": "newBusinessAddress",
"name": "permissionDetails",
"type": "string",
"label": "Tell us what permission you have to act for this business",
"required": true,
"validations": {
"message": "Permission details required"
}
},
{
"name": "newAddress",
"type": "object",
"required": true,
"fields": [
{
"name": "addressLine1",
"type": "string",
"label": "Address Line 1",
"label": "Address line 1",
"required": true,
"validations": {
"min": 5,
Expand All @@ -149,7 +149,7 @@
{
"name": "addressLine2",
"type": "string",
"label": "Address Line 2",
"label": "Address line 2",
"required": false,
"validations": {
"max": 200
Expand All @@ -166,9 +166,9 @@
}
},
{
"name": "postalCode",
"name": "postcode",
"type": "string",
"label": "Postal Code",
"label": "Postcode",
"required": false,
"validations": {
"regex": "^BB\\d{5}$",
Expand All @@ -189,21 +189,13 @@
"name": "redirectionStartDate",
"type": "date",
"label": "Redirection Start Date",
"required": false,
"validations": {
"regex": "^\\d{4}-\\d{2}-\\d{2}$",
"message": "Redirection Start Date is required and must be in YYYY-MM-DD format"
}
"required": false
},
{
"name": "redirectionEndDate",
"type": "date",
"label": "Redirection End Date",
"required": false,
"validations": {
"regex": "^\\d{4}-\\d{2}-\\d{2}$",
"message": "Redirection End Date is required and must be in YYYY-MM-DD format"
}
"required": false
}
]
},
Expand All @@ -222,7 +214,7 @@
"type": "email",
"config": {
"to": "{{db:post-office-redirection-business:admin_email}}",
"subject": "New Request to Redirect Mail for a Business - {{formData.applicant.firstName}} {{formData.applicant.lastName}}",
"subject": "New Request to Redirect my business mail - {{formData.applicant.firstName}} {{formData.applicant.lastName}}",
"template": "post-office-redirection-notice"
}
}
Expand Down
35 changes: 19 additions & 16 deletions src/validation/schema-builder.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,13 @@ export class SchemaBuilderService {
// Build base schema based on field type
switch (field.type) {
case 'string':
schema = z.coerce.string();
// For required strings, use z.string().min(1) to reject empty strings
// For optional strings, use z.coerce.string() but allow empty
if (field.required) {
schema = z.string().min(1, 'This field is required');
} else {
schema = z.coerce.string();
}
break;
case 'email':
schema = z.string().email('Invalid email format');
Expand Down Expand Up @@ -138,28 +144,25 @@ export class SchemaBuilderService {
) {
if (validations.min !== undefined) {
const minLength = validations.min;
if (!required) {
// For optional fields, allow empty strings or strings that meet min length
schema = schema.refine(
(val: string) => !val || val.length >= minLength,
{
message:
validations.message || `Minimum length is ${minLength}`,
},
);
} else {
schema = schema.min(
minLength,
validations.message || `Minimum length is ${minLength}`,
);
}
// Always enforce minimum length (use max(1, minLength) for required fields)
const effectiveMin = required ? Math.max(1, minLength) : minLength;
schema = schema.min(
effectiveMin,
validations.message || `Minimum length is ${effectiveMin}`,
);
} else if (required) {
// If no min specified but field is required, enforce min length of 1
schema = schema.min(1, validations.message || 'This field is required');
}
if (validations.max !== undefined) {
schema = schema.max(
validations.max,
validations.message || `Maximum length is ${validations.max}`,
);
}
} else if (fieldType === 'string' && required) {
// If no validations but field is required, enforce non-empty string
schema = schema.min(1, 'This field is required');
}

// Min/Max for numbers
Expand Down