70 lines
2.7 KiB
JavaScript
70 lines
2.7 KiB
JavaScript
import { z } from 'zod';
|
|
import { stripHtml } from '../services/sanitize.js';
|
|
|
|
const panPattern = /^[A-Z]{5}[0-9]{4}[A-Z]$/;
|
|
const ifscPattern = /^[A-Z]{4}0[A-Z0-9]{6}$/;
|
|
const gstinPattern = /^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z][1-9A-Z]Z[0-9A-Z]$/;
|
|
const accountPattern = /^[0-9]{9,18}$/;
|
|
|
|
const emptyToUndefined = (value) => (value === '' || value === null ? undefined : value);
|
|
|
|
/**
|
|
* @param {z.ZodTypeAny} schema
|
|
*/
|
|
function text(schema) {
|
|
return z.preprocess((value) => (typeof value === 'string' ? stripHtml(value) : value), schema);
|
|
}
|
|
|
|
export const applicationSchema = z.object({
|
|
agreement_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
|
|
full_name: text(z.string().trim().min(1)),
|
|
age: z.number().int().min(21).max(100),
|
|
address: text(z.string().trim().min(1)),
|
|
registration_no: text(z.string().trim().min(1)),
|
|
medical_council: text(z.string().trim().min(1)),
|
|
pan: text(z.string().regex(panPattern)),
|
|
gstin: z.preprocess(emptyToUndefined, text(z.string().regex(gstinPattern)).optional()),
|
|
bank_name: text(z.string().trim().min(1)),
|
|
account_holder: text(z.string().trim().min(1)),
|
|
account_number: text(z.string().regex(accountPattern)),
|
|
ifsc: text(z.string().regex(ifscPattern)),
|
|
});
|
|
|
|
const emptyToNull = (value) => (value === '' ? null : value);
|
|
|
|
const optionalText = (schema) => z.preprocess(emptyToNull, schema.nullable().optional());
|
|
|
|
export const draftApplicationSchema = z
|
|
.object({
|
|
agreement_date: optionalText(z.string().regex(/^\d{4}-\d{2}-\d{2}$/)),
|
|
full_name: optionalText(text(z.string().trim().min(1))),
|
|
age: z.preprocess((value) => {
|
|
if (value === '' || value === null) {
|
|
return null;
|
|
}
|
|
|
|
if (typeof value === 'string' && /^\d+$/.test(value)) {
|
|
return Number(value);
|
|
}
|
|
|
|
return value;
|
|
}, z.number().int().min(21).max(100).nullable().optional()),
|
|
address: optionalText(text(z.string().trim().min(1))),
|
|
registration_no: optionalText(text(z.string().trim().min(1))),
|
|
medical_council: optionalText(text(z.string().trim().min(1))),
|
|
pan: optionalText(text(z.string().regex(panPattern))),
|
|
gstin: optionalText(text(z.string().regex(gstinPattern))),
|
|
bank_name: optionalText(text(z.string().trim().min(1))),
|
|
account_holder: optionalText(text(z.string().trim().min(1))),
|
|
account_number: optionalText(text(z.string().regex(accountPattern))),
|
|
ifsc: optionalText(text(z.string().regex(ifscPattern))),
|
|
accepted: z.boolean().optional(),
|
|
/** Optional cancelled-cheque image as a data URL, or null to remove. */
|
|
cancelled_cheque: z.union([z.string().min(1), z.null()]).optional(),
|
|
})
|
|
.strict();
|
|
|
|
export const finalizeApplicationSchema = applicationSchema.extend({
|
|
accepted: z.literal(true),
|
|
});
|