Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM, PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source files covering clients, berths, interests/pipeline, documents/EOI, expenses/invoices, email, notifications, dashboard, admin, and client portal. CI/CD via Gitea Actions with Docker builds. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
96
src/lib/validators/interests.ts
Normal file
96
src/lib/validators/interests.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
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),
|
||||
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(),
|
||||
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 ──────────────────────────────────────────────────────────
|
||||
|
||||
export const publicInterestSchema = z.object({
|
||||
fullName: z.string().min(1).max(200),
|
||||
email: z.string().email(),
|
||||
phone: z.string().optional(),
|
||||
companyName: z.string().optional(),
|
||||
yachtName: z.string().optional(),
|
||||
yachtLengthFt: z.coerce.number().positive().optional(),
|
||||
yachtWidthFt: z.coerce.number().positive().optional(),
|
||||
yachtDraftFt: z.coerce.number().positive().optional(),
|
||||
preferredBerthSize: z.string().optional(),
|
||||
source: z.literal('website').default('website'),
|
||||
notes: z.string().max(2000).optional(),
|
||||
});
|
||||
|
||||
// ─── 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<typeof createInterestSchema>;
|
||||
export type UpdateInterestInput = z.infer<typeof updateInterestSchema>;
|
||||
export type ChangeStageInput = z.infer<typeof changeStageSchema>;
|
||||
export type ListInterestsInput = z.infer<typeof listInterestsSchema>;
|
||||
export type WaitingListAddInput = z.infer<typeof waitingListAddSchema>;
|
||||
export type PublicInterestInput = z.infer<typeof publicInterestSchema>;
|
||||
export type ReorderWaitingListInput = z.infer<typeof reorderWaitingListSchema>;
|
||||
Reference in New Issue
Block a user