77 lines
2.5 KiB
TypeScript
77 lines
2.5 KiB
TypeScript
import { z } from "zod"
|
|
|
|
/**
|
|
* Reusable schema for relation/lookup fields stored as `{ value, label }` objects.
|
|
* Use `.nullable()` when the field is optional but explicitly clearable.
|
|
*/
|
|
const relationFieldSchema = z
|
|
.object({ value: z.string(), label: z.string() })
|
|
.nullable()
|
|
|
|
type RelationField = z.infer<typeof relationFieldSchema>
|
|
|
|
const optionalTrimmedString = (max: number) =>
|
|
z.preprocess(
|
|
(value) => {
|
|
if (typeof value !== "string") {
|
|
return value
|
|
}
|
|
|
|
const trimmed = value.trim()
|
|
return trimmed === "" ? undefined : trimmed
|
|
},
|
|
z.string().max(max).optional(),
|
|
)
|
|
|
|
const optionalInteger = z.preprocess(
|
|
(value) => {
|
|
if (value === "" || value == null) {
|
|
return undefined
|
|
}
|
|
|
|
return Number(value)
|
|
},
|
|
z.number().int().optional(),
|
|
)
|
|
|
|
const customerFormSchema = z.object({
|
|
// ── Relations (stored as objects, mapped to IDs on submit) ──
|
|
customer_type: relationFieldSchema.refine((val) => !!val?.value, "Customer type is required"),
|
|
referral_source: relationFieldSchema,
|
|
payment_terms: relationFieldSchema,
|
|
country: relationFieldSchema,
|
|
state: relationFieldSchema,
|
|
|
|
// ── Basic info ──
|
|
salutation: z.string().trim().min(1, "Salutation is required").max(50, "Salutation cannot exceed 50 characters"),
|
|
first_name: z.string().trim().min(1, "First name is required").max(50, "First name cannot exceed 50 characters"),
|
|
last_name: z.string().trim().min(1, "Last name is required").max(50, "Last name cannot exceed 50 characters"),
|
|
company_name: optionalTrimmedString(50),
|
|
|
|
// ── Contact ──
|
|
email: z
|
|
.string()
|
|
.trim()
|
|
.min(1, "Email is required")
|
|
.email("Enter a valid email address")
|
|
.max(100, "Email cannot exceed 100 characters"),
|
|
phone: optionalTrimmedString(20),
|
|
alternate_phone: optionalTrimmedString(20),
|
|
opening_balance: optionalInteger,
|
|
credit_limit: optionalInteger,
|
|
website: optionalTrimmedString(255).refine(
|
|
(value) => !value || z.string().url().safeParse(value).success,
|
|
"Enter a valid website URL",
|
|
),
|
|
|
|
// ── Address ──
|
|
address_line_1: optionalTrimmedString(255),
|
|
address_line_2: optionalTrimmedString(255),
|
|
city: optionalTrimmedString(100),
|
|
zip_code: optionalTrimmedString(20),
|
|
})
|
|
|
|
type CustomerFormValues = z.infer<typeof customerFormSchema>
|
|
|
|
export { customerFormSchema, relationFieldSchema }
|
|
export type { CustomerFormValues, RelationField } |