import { z } from "zod" import { ExpenseStatus, InvoiceDiscount } from "@garage/api" const relationFieldSchema = z .object({ value: z.string(), label: z.string() }) .nullable() const requiredRelationFieldSchema = relationFieldSchema.superRefine((value, ctx) => { if (!value?.value) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "This field is required", }) } }) const optionalDateSchema = z.preprocess( (value) => (value === "" || value == null ? undefined : value), z.string().date().optional(), ) const optionalStringMax255Schema = z.preprocess( (value) => (value === "" || value == null ? undefined : value), z.string().max(255, "Must be at most 255 characters").optional(), ) const labelItemSchema = z.object({ id: z.number(), title: z.string(), color_code: z.string(), }) const expenseLineItemSchema = z.object({ expense_id: z.number(), title: z.string(), quantity: z.number().min(1), rate: z.number().min(0), discount_amount: z.number().min(0).optional(), chart_of_account: z.string().optional(), description: z.string().optional(), }) const expenseFormSchema = z.object({ // ── Relations ── job_card: requiredRelationFieldSchema, category: relationFieldSchema, vendor: relationFieldSchema, department: relationFieldSchema, tax: relationFieldSchema, // ── Basic info ── title: z.string().trim().min(1, "Title is required").max(255, "Title must be at most 255 characters"), invoice_number: optionalStringMax255Schema, expense_date: optionalDateSchema, notes: z.string().optional(), status: z.enum(ExpenseStatus).optional(), paid_through: z.coerce.number().int().optional(), // ── Discount / Tax ── discount: z.enum(InvoiceDiscount).optional(), discount_amount: z.coerce.number().min(0).optional(), labels: z.array(labelItemSchema).optional(), items: z.array(expenseLineItemSchema).optional(), }) type ExpenseFormValues = z.infer export { expenseFormSchema, relationFieldSchema } export type { ExpenseFormValues }