import { z } from 'zod'; import { baseListQuerySchema } from '@/lib/api/route-helpers'; import { PIPELINE_STAGES, LEAD_CATEGORIES } from '@/lib/constants'; // ─── Create ────────────────────────────────────────────────────────────────── export const createInterestSchema = z.object({ clientId: z.string().min(1), yachtId: z.string().optional(), berthId: z.string().optional(), pipelineStage: z.enum(PIPELINE_STAGES).default('open'), leadCategory: z.enum(LEAD_CATEGORIES).optional(), source: z.string().optional(), notes: z.string().optional(), tagIds: z.array(z.string()).optional().default([]), reminderEnabled: z.boolean().optional().default(false), reminderDays: z.number().int().min(1).optional(), }); // ─── Update ────────────────────────────────────────────────────────────────── export const updateInterestSchema = createInterestSchema .omit({ clientId: true, tagIds: true }) .partial(); // ─── Change Stage ───────────────────────────────────────────────────────────── export const changeStageSchema = z.object({ pipelineStage: z.enum(PIPELINE_STAGES), reason: z.string().optional(), }); // ─── List ───────────────────────────────────────────────────────────────────── export const listInterestsSchema = baseListQuerySchema.extend({ clientId: z.string().optional(), yachtId: z.string().optional(), berthId: z.string().optional(), pipelineStage: z .string() .transform((v) => v.split(',').filter(Boolean)) .optional(), leadCategory: z.enum(LEAD_CATEGORIES).optional(), eoiStatus: z.string().optional(), tagIds: z .string() .transform((v) => v.split(',').filter(Boolean)) .optional(), }); // ─── Waiting List ───────────────────────────────────────────────────────────── export const waitingListAddSchema = z.object({ clientId: z.string().min(1), priority: z.enum(['normal', 'high']).default('normal'), notifyPref: z.enum(['email', 'in_app', 'both']).default('email'), notes: z.string().optional(), }); // ─── Generate Recommendations ───────────────────────────────────────────────── export const generateRecommendationsSchema = z.object({ interestId: z.string().min(1), }); // ─── Public Interest ────────────────────────────────────────────────────────── const addressSchema = z.object({ street: z.string().max(500).optional(), city: z.string().max(200).optional(), stateProvince: z.string().max(200).optional(), postalCode: z.string().max(50).optional(), country: z.string().max(100).optional(), }); // Nested yacht block. Public submissions must now include yacht data because the // route inserts a yacht row as part of the trio (client + yacht + interest). const publicYachtSchema = z.object({ name: z.string().min(1).max(200), hullNumber: z.string().max(100).optional(), registration: z.string().max(100).optional(), flag: z.string().max(100).optional(), yearBuilt: z.coerce.number().int().min(1800).max(2100).optional(), lengthFt: z.coerce.number().positive().optional(), widthFt: z.coerce.number().positive().optional(), draftFt: z.coerce.number().positive().optional(), }); // Optional company block. If provided, the route upserts a company row (match // case-insensitively by (portId, name)) and adds an active membership linking // the submitting client to the company with the chosen role. const publicCompanySchema = z.object({ name: z.string().min(1).max(200), legalName: z.string().max(200).optional(), taxId: z.string().max(100).optional(), incorporationCountry: z.string().max(100).optional(), role: z .enum([ 'director', 'officer', 'broker', 'representative', 'legal_counsel', 'employee', 'shareholder', 'other', ]) .optional() .default('representative'), }); export const publicInterestSchema = z .object({ // New: first/last split firstName: z.string().min(1).max(100).optional(), lastName: z.string().min(1).max(100).optional(), // Backward compat fullName: z.string().min(1).max(200).optional(), email: z.string().email(), phone: z.string().min(1), preferredContactMethod: z.enum(['email', 'phone', 'sms']).optional(), mooringNumber: z.string().max(50).optional(), // NEW: required structured yacht block. Public submissions after the // data-model refactor MUST include yacht data. yacht: publicYachtSchema, // NEW: optional company block — creates/upserts a company and adds a // membership linking the submitting client to it. company: publicCompanySchema.optional(), source: z.literal('website').default('website'), notes: z.string().max(2000).optional(), address: addressSchema.optional(), }) .refine((data) => data.fullName || (data.firstName && data.lastName), { message: 'Either fullName or both firstName and lastName are required', path: ['fullName'], }); // ─── Reorder Waiting List ───────────────────────────────────────────────────── export const reorderWaitingListSchema = z.object({ entryId: z.string().min(1), newPosition: z.coerce.number().int().min(1), }); // ─── Types ──────────────────────────────────────────────────────────────────── export type CreateInterestInput = z.infer; export type UpdateInterestInput = z.infer; export type ChangeStageInput = z.infer; export type ListInterestsInput = z.infer; export type WaitingListAddInput = z.infer; export type PublicInterestInput = z.infer; export type ReorderWaitingListInput = z.infer;