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/schemas/post-office-redirection-deceased.json b/schemas/post-office-redirection-deceased.json
index c8e9c8d..bf28cc4 100644
--- a/schemas/post-office-redirection-deceased.json
+++ b/schemas/post-office-redirection-deceased.json
@@ -4,7 +4,7 @@
"description": "Change where your mail is sent (deceased)",
"fields": [
{
- "name": "applicant",
+ "name": "deceased",
"type": "object",
"required": true,
"fields": [
@@ -29,6 +29,12 @@
"message": "First name is required"
}
},
+ {
+ "name": "middleName",
+ "type": "string",
+ "label": "Middle name",
+ "required": false
+ },
{
"name": "lastName",
"type": "string",
@@ -41,45 +47,14 @@
}
},
{
- "name": "dateOfBirth",
+ "name": "dateOfDeath",
"type": "date",
- "label": "Date of birth",
+ "label": "Date of death",
"required": false,
"validations": {
"regex": "^\\d{4}-\\d{2}-\\d{2}$",
- "message": "Date of birth is required and must be in YYYY-MM-DD format"
- }
- },
- {
- "name": "idNumber",
- "type": "string",
- "label": "National Identification (ID) Number",
- "required": false,
- "validations": {
- "min": 2,
- "message": "ID Number must be at least 2 characters"
- }
- },
- {
- "name": "passportNumber",
- "type": "string",
- "label": "Passport Number",
- "required": false,
- "validations": {
- "message": "Passport number must be at least 6 characters"
+ "message": "Date of death is required and must be in YYYY-MM-DD format"
}
- },
- {
- "name": "email",
- "type": "email",
- "label": "Email Address",
- "required": true
- },
- {
- "name": "telephoneNumber",
- "type": "string",
- "label": "Telephone Number",
- "required": true
}
]
},
@@ -91,7 +66,7 @@
{
"name": "addressLine1",
"type": "string",
- "label": "Address Line 1",
+ "label": "Address line 1",
"required": true,
"validations": {
"min": 5,
@@ -102,7 +77,7 @@
{
"name": "addressLine2",
"type": "string",
- "label": "Address Line 2",
+ "label": "Address line 2",
"required": false,
"validations": {
"max": 200
@@ -119,9 +94,9 @@
}
},
{
- "name": "postalCode",
+ "name": "postcode",
"type": "string",
- "label": "Postal Code",
+ "label": "Postcode",
"required": false,
"validations": {
"regex": "^BB\\d{5}$",
@@ -130,6 +105,77 @@
}
]
},
+ {
+ "name": "applicant",
+ "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",
+ "type": "string",
+ "label": "First name",
+ "required": true,
+ "validations": {
+ "min": 1,
+ "max": 100,
+ "message": "First name is required"
+ }
+ },
+ {
+ "name": "relationshipToDeceased",
+ "type": "string",
+ "label": "What is your relationship to the person?",
+ "required": true,
+ "validations": {
+ "min": 1,
+ "max": 100,
+ "message": "Relationship to deceased is required"
+ }
+ },
+ {
+ "name": "lastName",
+ "type": "string",
+ "label": "Last name",
+ "required": true,
+ "validations": {
+ "min": 1,
+ "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": "permissionDetails",
+ "type": "string",
+ "label": "Tell us what permission you have to act on behalf of the estate",
+ "required": true,
+ "validations": {
+ "message": "Permission details required"
+ }
+ },
{
"name": "newAddress",
"type": "object",
@@ -138,7 +184,7 @@
{
"name": "addressLine1",
"type": "string",
- "label": "Address Line 1",
+ "label": "Address line 1",
"required": true,
"validations": {
"min": 5,
@@ -149,7 +195,7 @@
{
"name": "addressLine2",
"type": "string",
- "label": "Address Line 2",
+ "label": "Address line 2",
"required": false,
"validations": {
"max": 200
@@ -166,9 +212,9 @@
}
},
{
- "name": "postalCode",
+ "name": "postcode",
"type": "string",
- "label": "Postal Code",
+ "label": "Postcode",
"required": false,
"validations": {
"regex": "^BB\\d{5}$",
@@ -176,9 +222,9 @@
}
},
{
- "name": "isMovingPermanent",
+ "name": "isRedirectPermanent",
"type": "string",
- "label": "Are you moving permanently?",
+ "label": "Are you redirecting their mail permanently?",
"required": true,
"validations": {
"regex": "^(yes|no)$",
@@ -187,23 +233,15 @@
},
{
"name": "redirectionStartDate",
- "type": "date",
+ "type": "string",
"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",
+ "type": "string",
"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
}
]
},
diff --git a/schemas/sell-goods-services-beach-park.json b/schemas/sell-goods-services-beach-park.json
index 4081786..5caa9c6 100644
--- a/schemas/sell-goods-services-beach-park.json
+++ b/schemas/sell-goods-services-beach-park.json
@@ -60,13 +60,18 @@
"message": "Date of birth is required and must be in YYYY-MM-DD format"
}
},
+ {
+ "name": "nationality",
+ "type": "string",
+ "label": "Nationality",
+ "required": true
+ },
{
"name": "idNumber",
"type": "string",
"label": "National Identification (ID) Number",
"required": false,
"validations": {
- "min": 2,
"message": "ID Number must be at least 2 characters"
}
},
@@ -89,11 +94,7 @@
"name": "telephoneNumber",
"type": "string",
"label": "Telephone Number",
- "required": true,
- "validations": {
- "regex": "^\\+?[0-9]{10,15}$",
- "message": "Telephone number must be 10-15 digits"
- }
+ "required": true
},
{
"name": "addressLine1",
diff --git a/src/email/email.service.ts b/src/email/email.service.ts
index 4571c9d..1bdcbbc 100644
--- a/src/email/email.service.ts
+++ b/src/email/email.service.ts
@@ -198,8 +198,10 @@ export class EmailService {
`Email sent successfully to ${validToAddresses.join(', ')}`,
);
} catch (error) {
- this.logger.error(`Failed to send email: ${error.message}`, error.stack);
- throw error;
+ this.logger.warn(
+ `Failed to send email (continuing silently): ${error.message}`,
+ );
+ // Silently fail - do not throw the error
}
}
diff --git a/src/email/templates/post-office-redirection-notice.hbs b/src/email/templates/post-office-redirection-notice.hbs
index 5346102..5ba9bd9 100644
--- a/src/email/templates/post-office-redirection-notice.hbs
+++ b/src/email/templates/post-office-redirection-notice.hbs
@@ -18,136 +18,176 @@
border-radius: 5px; } .old-address { background-color: #fed7d7; }
.new-address { background-color: #c6f6d5; } .footer { margin-top: 30px;
padding: 20px; background-color: #edf2f7; border-radius: 5px; text-align:
- center; font-size: 14px; color: #718096; }
+ center; font-size: 14px; color: #718096; } .document-list { list-style:
+ none; padding-left: 0; } .document-list li { padding: 8px;
+ background-color: #edf2f7; margin-bottom: 8px; border-radius: 3px;
+ word-break: break-all; }
-
A new post office redirection notice request has been submitted.
+
A new post office redirection request for a deceased person has been
+ submitted.
+
-
Personal Information
+
Deceased Person Information
- Name:
- {{applicant.title}}
- {{applicant.firstName}}
- {{applicant.lastName}}
+ Title:
+ {{deceased.title}}
- Date of Birth:
- {{applicant.dateOfBirth}}
+ First Name:
+ {{deceased.firstName}}
-
- ID Number:
- {{applicant.idNumber}}
-
- {{#if applicant.passportNumber}}
+ {{#if deceased.middleName}}
- Passport Number:
- {{applicant.passportNumber}}
+ Middle Name:
+ {{deceased.middleName}}
{{/if}}
- {{#if applicant.email}}
+
+ Last Name:
+ {{deceased.lastName}}
+
+ {{#if deceased.dateOfDeath}}
- Email:
- {{applicant.email}}
+ Date of Death:
+ {{deceased.dateOfDeath}}
{{/if}}
- {{#if applicant.email}}
+
+
+
+
+
Old/Previous Address
+
+ Address Line 1:
+ {{oldAddress.addressLine1}}
+
+ {{#if oldAddress.addressLine2}}
- Email:
- {{applicant.email}}
+ Address Line 2:
+ {{oldAddress.addressLine2}}
{{/if}}
- {{#if applicant.telephoneNumber}}
+
+ Parish:
+ {{oldAddress.parish}}
+
+ {{#if oldAddress.postcode}}
- Phone Number:
- {{applicant.telephoneNumber}}
+ Postcode:
+ {{oldAddress.postcode}}
{{/if}}
+
-
Address Redirection Details
-
-
-
Old Address
-
- Address Line 1:
- {{oldaddress.addressLine1}}
-
- {{#if oldaddress.addressLine2}}
-
- Address Line 2:
- {{oldaddress.addressLine2}}
-
- {{/if}}
-
- Parish:
- {{oldaddress.parish}}
-
- {{#if oldaddress.postalCode}}
-
- Postal Code:
- {{oldaddress.postalCode}}
-
- {{/if}}
-
+
Applicant Information
+
+ Title:
+ {{applicant.title}}
+
+
+ First Name:
+ {{applicant.firstName}}
+
+
+ Last Name:
+ {{applicant.lastName}}
+
+
+ Relationship to Deceased:
+ {{applicant.relationshipToDeceased}}
+
+
+ Email Address:
+ {{applicant.email}}
+
+
+ Telephone Number:
+ {{applicant.telephoneNumber}}
+
+
-
-
New Address
-
- Address Line 1:
- {{newAddress.addressLine1}}
-
- {{#if newAddress.addressLine2}}
-
- Address Line 2:
- {{newAddress.addressLine2}}
-
- {{/if}}
-
- Parish:
- {{newAddress.parish}}
-
- {{#if newAddress.postalCode}}
-
- Postal Code:
- {{newAddress.postalCode}}
-
- {{/if}}
-
+
+
+
Permission Details
+
+ Permission to Act on Estate:
+ {{permissionDetails}}
+
-
House Member Information
+
New Address for Mail Redirection
+
+ Address Line 1:
+ {{newAddress.addressLine1}}
+
+ {{#if newAddress.addressLine2}}
+
+ Address Line 2:
+ {{newAddress.addressLine2}}
+
+ {{/if}}
- Member Name:
- {{houseMembers.firstName}}
- {{houseMembers.lastName}}
+ Parish:
+ {{newAddress.parish}}
+ {{#if newAddress.postcode}}
+
+ Postcode:
+ {{newAddress.postcode}}
+
+ {{/if}}
- Member ID Number:
- {{houseMembers.idNumber}}
+ Permanent Redirection:
+ {{newAddress.isRedirectPermanent}}
- {{#if houseMembers.addAnother}}
+ {{#if newAddress.redirectionStartDate}}
- Add Another Member:
- {{houseMembers.addAnother}}
+ Redirection Start Date:
+ {{newAddress.redirectionStartDate}}
+
+ {{/if}}
+ {{#if newAddress.redirectionEndDate}}
+
+ Redirection End Date:
+ {{newAddress.redirectionEndDate}}
{{/if}}
+
+ {{#if uploadDocumentUrls}}
+
+
Uploaded Documents
+ {{#if uploadDocumentUrls.length}}
+
+ {{#each uploadDocumentUrls}}
+ - {{this}}
+ {{/each}}
+
+ {{else}}
+
No documents uploaded.
+ {{/if}}
+
+ {{/if}}
+
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