diff --git a/schemas/post-office-redirection-business.json b/schemas/post-office-redirection-business.json index 7f0b3e4..bc81c82 100644 --- a/schemas/post-office-redirection-business.json +++ b/schemas/post-office-redirection-business.json @@ -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, @@ -149,7 +149,7 @@ { "name": "addressLine2", "type": "string", - "label": "Address Line 2", + "label": "Address line 2", "required": false, "validations": { "max": 200 @@ -166,9 +166,9 @@ } }, { - "name": "postalCode", + "name": "postcode", "type": "string", - "label": "Postal Code", + "label": "Postcode", "required": false, "validations": { "regex": "^BB\\d{5}$", @@ -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 } ] }, @@ -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" } } diff --git a/src/validation/schema-builder.service.ts b/src/validation/schema-builder.service.ts index f420541..6659dce 100644 --- a/src/validation/schema-builder.service.ts +++ b/src/validation/schema-builder.service.ts @@ -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'); @@ -138,21 +144,15 @@ 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( @@ -160,6 +160,9 @@ export class SchemaBuilderService { 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