import { z } from 'zod' /** Coerce empty strings to undefined — solves HTML form inputs sending '' for blank optional fields */ function opt(schema: T) { return z.preprocess((v) => (v === '' ? undefined : v), schema.optional()) } export const BillingMode = z.enum(['consolidated', 'split']) export type BillingMode = z.infer export const PaymentProcessor = z.enum(['stripe', 'global_payments']) export type PaymentProcessor = z.infer export const TaxExemptStatus = z.enum(['none', 'pending', 'approved']) export type TaxExemptStatus = z.infer export const AccountCreateSchema = z.object({ name: z.string().min(1).max(255), email: opt(z.string().email()), phone: opt(z.string().max(50)), address: z .object({ street: z.string().optional(), city: z.string().optional(), state: z.string().optional(), zip: z.string().optional(), }) .optional(), billingMode: BillingMode.default('consolidated'), notes: opt(z.string()), }) export type AccountCreateInput = z.infer export const AccountUpdateSchema = AccountCreateSchema.partial() export type AccountUpdateInput = z.infer export const MemberCreateSchema = z.object({ accountId: z.string().uuid(), firstName: z.string().min(1).max(100), lastName: z.string().min(1).max(100), dateOfBirth: opt(z.string().date()), isMinor: z.boolean().optional(), email: opt(z.string().email()), phone: opt(z.string().max(50)), notes: opt(z.string()), }) export type MemberCreateInput = z.infer export const MemberUpdateSchema = MemberCreateSchema.omit({ accountId: true }).partial() export type MemberUpdateInput = z.infer export const AccountSearchSchema = z.object({ q: z.string().min(1).max(255), }) // --- Member Identifier --- export const IdentifierType = z.enum(['drivers_license', 'passport', 'school_id']) export type IdentifierType = z.infer export const MemberIdentifierCreateSchema = z.object({ memberId: z.string().uuid(), type: IdentifierType, label: opt(z.string().max(100)), value: z.string().min(1).max(255), issuingAuthority: opt(z.string().max(255)), issuedDate: opt(z.string().date()), expiresAt: opt(z.string().date()), imageFrontFileId: opt(z.string().uuid()), imageBackFileId: opt(z.string().uuid()), notes: opt(z.string()), isPrimary: z.boolean().default(false), }) export type MemberIdentifierCreateInput = z.infer export const MemberIdentifierUpdateSchema = MemberIdentifierCreateSchema.omit({ memberId: true }).partial() export type MemberIdentifierUpdateInput = z.infer // --- Account Processor Link --- export const ProcessorLinkCreateSchema = z.object({ accountId: z.string().uuid(), processor: PaymentProcessor, processorCustomerId: z.string().min(1).max(255), }) export type ProcessorLinkCreateInput = z.infer export const ProcessorLinkUpdateSchema = z.object({ isActive: z.boolean().optional(), }) export type ProcessorLinkUpdateInput = z.infer // --- Account Payment Method --- export const PaymentMethodCreateSchema = z.object({ accountId: z.string().uuid(), processor: PaymentProcessor, processorPaymentMethodId: z.string().min(1).max(255), cardBrand: opt(z.string().max(50)), lastFour: opt(z.string().length(4)), expMonth: z.number().int().min(1).max(12).optional(), expYear: z.number().int().min(2000).max(2100).optional(), isDefault: z.boolean().default(false), }) export type PaymentMethodCreateInput = z.infer export const PaymentMethodUpdateSchema = z.object({ isDefault: z.boolean().optional(), requiresUpdate: z.boolean().optional(), }) export type PaymentMethodUpdateInput = z.infer // --- Tax Exemption --- export const TaxExemptionCreateSchema = z.object({ accountId: z.string().uuid(), certificateNumber: z.string().min(1).max(255), certificateType: opt(z.string().max(100)), issuingState: opt(z.string().length(2)), expiresAt: opt(z.string().date()), notes: opt(z.string()), }) export type TaxExemptionCreateInput = z.infer export const TaxExemptionUpdateSchema = z.object({ certificateNumber: opt(z.string().min(1).max(255)), certificateType: opt(z.string().max(100)), issuingState: opt(z.string().length(2)), expiresAt: opt(z.string().date()), notes: opt(z.string()), }) export type TaxExemptionUpdateInput = z.infer // --- Lookup Tables --- export const LookupCreateSchema = z.object({ name: z.string().min(1).max(100), slug: z.string().min(1).max(100).regex(/^[a-z0-9_]+$/, 'Slug must be lowercase alphanumeric with underscores'), description: opt(z.string()), sortOrder: z.number().int().default(0), }) export type LookupCreateInput = z.infer export const LookupUpdateSchema = z.object({ name: opt(z.string().min(1).max(100)), description: opt(z.string()), sortOrder: z.number().int().optional(), isActive: z.boolean().optional(), }) export type LookupUpdateInput = z.infer