proposal-system/web/src/domain/customers/schemas.ts
Adam Moussa 6acfdabc8c
style(web): prettier format pass (mechanical)
npx prettier --write . with the new .prettierrc (singleQuote,
printWidth 100). No functional changes — enforced by format:check in
CI from this PR on.
2026-07-13 20:14:47 -04:00

46 lines
1.8 KiB
TypeScript

// 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<typeof customerFormSchema>;
/**
* 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(),
};
}