import { z } from 'zod' import { sanitizeAndFormat, getMaxLength } from './inputValidation' import type { FieldType } from './inputValidation' // ─── Regex constants ─────────────────────────────────────────── const GLOBAL_ALLOWED = /^[a-zA-Z0-9@\-/\s]*$/ const EMAIL_REGEX = /^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$/ /** Morocco phone: (+212|00212|0) + 5/6/7 + 8 digits (optional spaces) */ const MA_PHONE_REGEX = /^(?:(?:\+|00)212|0)\s?[5-7](?:\s?\d){8}$/ // ─── Phone sanitization ─────────────────────────────────────── function sanitizePhone(raw: string): string { let out = '' for (const ch of raw) { if (/[\d\s+]/.test(ch)) out += ch } return out.trim() } // ─── Required fields ─────────────────────────────────────────── function applyFieldRules(fieldType: FieldType) { const maxLength = getMaxLength(fieldType) return z .string() .min(1, { message: 'This field is required' }) .trim() .transform((val: string) => sanitizeAndFormat(val, fieldType)) .pipe( z.string().refine( (val: string) => val.length <= maxLength, { message: 'Maximum ' + maxLength + ' characters allowed' } ) ) } // ─── Optional fields ─────────────────────────────────────────── function applyOptionalFieldRules(fieldType: FieldType) { const maxLength = getMaxLength(fieldType) return z .string() .optional() .transform((val) => { if (val === undefined || val === null || val.trim() === '') return undefined return sanitizeAndFormat(val, fieldType) }) .pipe( z.string().optional().refine( (val) => val === undefined || val.length <= maxLength, { message: 'Maximum ' + maxLength + ' characters allowed' } ) ) } // ─── Exported helpers ────────────────────────────────────────── export function textField(fieldType: FieldType) { return applyFieldRules(fieldType) } export function optionalTextField(fieldType: FieldType) { return applyOptionalFieldRules(fieldType) } export function titleCaseAllField(fieldType: FieldType) { return applyFieldRules(fieldType) } export function optionalTitleCaseAllField(fieldType: FieldType) { return applyOptionalFieldRules(fieldType) } export function upperField(fieldType: FieldType) { return applyFieldRules(fieldType) } export function optionalUpperField(fieldType: FieldType) { return applyOptionalFieldRules(fieldType) } export function carTextField(fieldType: FieldType) { return applyFieldRules(fieldType) } export function optionalCarTextField(fieldType: FieldType) { return applyOptionalFieldRules(fieldType) } export function zipField() { return applyFieldRules('zipCode') } export function optionalZipField() { return applyOptionalFieldRules('zipCode') } // ─── Email fields ────────────────────────────────────────────── export function emailField() { return z .string() .min(1, { message: 'Email is required' }) .trim() .transform((val: string) => sanitizeAndFormat(val, 'email')) .pipe( z.string().refine( (val: string) => EMAIL_REGEX.test(val), { message: 'Please enter a valid email address' } ) ) } export function optionalEmailField() { return z .string() .optional() .transform((val) => { if (val === undefined || val === null || val.trim() === '') return undefined return sanitizeAndFormat(val, 'email') }) .pipe( z.string().optional().refine( (val) => val === undefined || EMAIL_REGEX.test(val), { message: 'Please enter a valid email address' } ) ) } // ─── Phone fields ────────────────────────────────────────────── export function phoneField() { return z .string() .min(1, { message: 'Phone number is required' }) .trim() .transform((val: string) => sanitizePhone(val)) .pipe( z.string().refine( (val: string) => MA_PHONE_REGEX.test(val), { message: 'Please enter a valid Morocco phone number' } ) ) } export function optionalPhoneField() { return z .string() .optional() .transform((val) => { if (val === undefined || val === null || val.trim() === '') return undefined return sanitizePhone(val) }) .pipe( z.string().optional().refine( (val) => val === undefined || MA_PHONE_REGEX.test(val), { message: 'Please enter a valid Morocco phone number' } ) ) }