88 lines
3.6 KiB
TypeScript
88 lines
3.6 KiB
TypeScript
|
|
import { z } from 'zod';
|
||
|
|
|
||
|
|
import { baseListQuerySchema } from '@/lib/api/route-helpers';
|
||
|
|
|
||
|
|
// ─── Residential client ──────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
export const createResidentialClientSchema = z.object({
|
||
|
|
fullName: z.string().min(1).max(200),
|
||
|
|
email: z
|
||
|
|
.string()
|
||
|
|
.email()
|
||
|
|
.optional()
|
||
|
|
.or(z.literal('').transform(() => undefined)),
|
||
|
|
phone: z.string().optional(),
|
||
|
|
placeOfResidence: z.string().optional(),
|
||
|
|
preferredContactMethod: z.enum(['email', 'phone']).optional(),
|
||
|
|
status: z.enum(['prospect', 'active', 'inactive']).optional().default('prospect'),
|
||
|
|
source: z.enum(['website', 'manual', 'referral', 'broker']).optional(),
|
||
|
|
notes: z.string().optional(),
|
||
|
|
});
|
||
|
|
|
||
|
|
export const updateResidentialClientSchema = createResidentialClientSchema.partial();
|
||
|
|
|
||
|
|
export const listResidentialClientsSchema = baseListQuerySchema.extend({
|
||
|
|
status: z.enum(['prospect', 'active', 'inactive']).optional(),
|
||
|
|
source: z.enum(['website', 'manual', 'referral', 'broker']).optional(),
|
||
|
|
});
|
||
|
|
|
||
|
|
// ─── Residential interest ────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
export const PIPELINE_STAGES = [
|
||
|
|
'new',
|
||
|
|
'contacted',
|
||
|
|
'viewing_scheduled',
|
||
|
|
'offer_made',
|
||
|
|
'offer_accepted',
|
||
|
|
'closed_won',
|
||
|
|
'closed_lost',
|
||
|
|
] as const;
|
||
|
|
|
||
|
|
export const createResidentialInterestSchema = z.object({
|
||
|
|
residentialClientId: z.string().min(1),
|
||
|
|
pipelineStage: z.enum(PIPELINE_STAGES).optional().default('new'),
|
||
|
|
source: z.enum(['website', 'manual', 'referral', 'broker']).optional(),
|
||
|
|
notes: z.string().optional(),
|
||
|
|
preferences: z.string().optional(),
|
||
|
|
assignedTo: z.string().optional(),
|
||
|
|
});
|
||
|
|
|
||
|
|
export const updateResidentialInterestSchema = createResidentialInterestSchema
|
||
|
|
.omit({ residentialClientId: true })
|
||
|
|
.partial();
|
||
|
|
|
||
|
|
export const listResidentialInterestsSchema = baseListQuerySchema.extend({
|
||
|
|
pipelineStage: z.enum(PIPELINE_STAGES).optional(),
|
||
|
|
assignedTo: z.string().optional(),
|
||
|
|
residentialClientId: z.string().optional(),
|
||
|
|
});
|
||
|
|
|
||
|
|
// ─── Public website inquiry ──────────────────────────────────────────────────
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Shape posted by the public website's residential interest form. Coerces
|
||
|
|
* to internal create-shapes inside the public route.
|
||
|
|
*/
|
||
|
|
export const publicResidentialInquirySchema = z.object({
|
||
|
|
firstName: z.string().min(1),
|
||
|
|
lastName: z.string().min(1),
|
||
|
|
email: z.string().email(),
|
||
|
|
phone: z.string().min(1),
|
||
|
|
placeOfResidence: z.string().optional(),
|
||
|
|
preferredContactMethod: z.enum(['email', 'phone']).optional(),
|
||
|
|
notes: z.string().optional(),
|
||
|
|
preferences: z.string().optional(),
|
||
|
|
});
|
||
|
|
|
||
|
|
// ─── Types ────────────────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
export type CreateResidentialClientInput = z.infer<typeof createResidentialClientSchema>;
|
||
|
|
export type UpdateResidentialClientInput = z.infer<typeof updateResidentialClientSchema>;
|
||
|
|
export type ListResidentialClientsInput = z.infer<typeof listResidentialClientsSchema>;
|
||
|
|
|
||
|
|
export type CreateResidentialInterestInput = z.infer<typeof createResidentialInterestSchema>;
|
||
|
|
export type UpdateResidentialInterestInput = z.infer<typeof updateResidentialInterestSchema>;
|
||
|
|
export type ListResidentialInterestsInput = z.infer<typeof listResidentialInterestsSchema>;
|
||
|
|
|
||
|
|
export type PublicResidentialInquiryInput = z.infer<typeof publicResidentialInquirySchema>;
|