// Customers domain — contract schema re-exports + customer form schema. import { z } from 'zod'; import { createCustomerRequestSchema } from '@proposal-system/api-contracts/schemas'; import type { CreateCustomerRequest, UpdateCustomerRequest } from './types'; export { customerSchema, createCustomerRequestSchema, updateCustomerRequestSchema, } from '@proposal-system/api-contracts/schemas'; /** Simple email format check (client-side only) — mirrors CustomerManagementPage. */ const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; /** * Customer create/edit form. Derived from createCustomerRequestSchema — the * form edits a single primary address string (the wire type takes * addresses: string[]); toCustomerRequest() maps to the request shape. */ export const customerFormSchema = createCustomerRequestSchema .pick({ name: true }) .extend({ name: z.string().trim().min(1, 'Name is required'), address: z.string().trim().min(1, 'Address is required'), contactEmail: z .string() .refine((v) => v.trim() === '' || EMAIL_RE.test(v.trim()), { message: 'Enter a valid email address', }), }); export type CustomerFormValues = z.infer; /** * Map validated form values to the create/update request. The form edits the * primary address; any additional existing addresses are preserved. * contactEmail is always sent: the API clears the stored email on empty * string (CustomerService normalizes '' → null), so omitting the key when the * admin blanks the field would make the email impossible to clear. */ export function toCustomerRequest( values: CustomerFormValues, existingAddresses: string[] = [] ): CreateCustomerRequest & UpdateCustomerRequest { return { name: values.name.trim(), addresses: [values.address.trim(), ...existingAddresses.slice(1)], contactEmail: values.contactEmail.trim(), }; }