import { z } from 'zod'; // Status & Enums -------------------------------------------------------------- export const leadStatusSchema = z.enum(['cold', 'warm', 'hot', 'won', 'lost']); export const LEAD_STATUSES = leadStatusSchema.options; export const contactFunktionSchema = z.enum([ 'entscheider', 'influencer', 'gatekeeper', 'end_user', 'champion', ]); export const CONTACT_FUNKTIONEN = contactFunktionSchema.options; export const touchpointKanalSchema = z.enum([ 'call', 'mail', 'linkedin', 'termin', 'event', 'sonstiges', ]); export const TOUCHPOINT_KANAELE = touchpointKanalSchema.options; // JSONB-Substrukturen --------------------------------------------------------- export const offeneFrageSchema = z.object({ text: z.string().min(1).max(500), done: z.boolean(), }); export const anhangSchema = z.object({ label: z.string().min(1).max(200), url: z.string().min(1).max(2000), }); // Lead-Kernfelder (alle optional außer firma). Wird für create + update genutzt. const leadCoreFields = { firma: z.string().min(1).max(200), status: leadStatusSchema, score: z.number().int().min(0).max(10), ownerUserId: z.string().uuid().nullable(), projectId: z.string().uuid().nullable(), branche: z.string().max(200).nullable(), subSegment: z.string().max(200).nullable(), standorte: z.string().max(500).nullable(), mitarbeiterzahl: z.string().max(100).nullable(), umsatz: z.string().max(100).nullable(), website: z.string().max(500).nullable(), quelle: z.string().max(500).nullable(), painPoint: z.string().max(2000).nullable(), statusQuoKosten: z.string().max(2000).nullable(), bestehendeLoesung: z.string().max(1000).nullable(), wechselgrund: z.string().max(2000).nullable(), persoenlicherErfolg: z.string().max(2000).nullable(), budgetIndikation: z.string().max(500).nullable(), entscheidungshorizont: z.string().max(500).nullable(), compellingEvent: z.string().max(1000).nullable(), mitbewerber: z.string().max(1000).nullable(), buyingCenter: z.string().max(1000).nullable(), beschaffungsweg: z.string().max(500).nullable(), kommunikationsstil: z.string().max(1000).nullable(), vorlieben: z.string().max(1000).nullable(), hotButtons: z.string().max(1000).nullable(), noGos: z.string().max(1000).nullable(), druckpunkte: z.string().max(1000).nullable(), anker: z.string().max(1000).nullable(), vertrauenslevel: z.number().int().min(1).max(5).nullable(), nutzenHypothese: z.string().max(1000).nullable(), differenzierer: z.string().max(1000).nullable(), naechsterSchritt: z.string().max(1000).nullable(), naechsterSchrittDatum: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).nullable(), risiken: z.string().max(1000).nullable(), planB: z.string().max(1000).nullable(), offeneFragen: z.array(offeneFrageSchema).max(100), anhaenge: z.array(anhangSchema).max(50), }; // Create: nur firma ist Pflicht, Rest optional mit sinnvollen Defaults. export const createLeadInput = z.object({ firma: leadCoreFields.firma, status: leadStatusSchema.default('cold'), score: z.number().int().min(0).max(10).default(0), ownerUserId: z.string().uuid().optional(), projectId: z.string().uuid().optional(), branche: z.string().max(200).optional(), subSegment: z.string().max(200).optional(), website: z.string().max(500).optional(), quelle: z.string().max(500).optional(), }); export type CreateLeadInput = z.infer; export const leadsForProjectInput = z.object({ projectId: z.string().uuid() }); // Update: id + beliebige Teilmenge der Kernfelder (partial patch). export const updateLeadInput = z .object({ id: z.string().uuid() }) .merge(z.object(leadCoreFields).partial()); export type UpdateLeadInput = z.infer; export const leadIdInput = z.object({ id: z.string().uuid() }); // Kontakte -------------------------------------------------------------------- export const addContactInput = z.object({ leadId: z.string().uuid(), name: z.string().min(1).max(200), rolle: z.string().max(200).optional(), funktion: contactFunktionSchema.optional(), email: z.string().max(320).optional(), telefon: z.string().max(100).optional(), linkedin: z.string().max(500).optional(), imBuyingCenterSeit: z.string().max(100).optional(), sprache: z.string().max(100).optional(), isPrimary: z.boolean().default(false), }); export type AddContactInput = z.infer; export const removeContactInput = z.object({ contactId: z.string().uuid() }); // Touchpoints (Austausch-Log) ------------------------------------------------- export const addTouchpointInput = z.object({ leadId: z.string().uuid(), datum: z.string().regex(/^\d{4}-\d{2}-\d{2}$/), kanal: touchpointKanalSchema, inhalt: z.string().min(1).max(2000), outcome: z.string().max(1000).optional(), followUpBis: z .string() .regex(/^\d{4}-\d{2}-\d{2}$/) .optional(), }); export type AddTouchpointInput = z.infer; export const removeTouchpointInput = z.object({ touchpointId: z.string().uuid() }); // Promotion → Pipeline-Deal --------------------------------------------------- export const promoteToDealInput = z.object({ leadId: z.string().uuid(), valueEur: z.number().int().min(0).default(0), probability: z.number().int().min(0).max(100).default(50), }); export type PromoteToDealInput = z.infer; // CSV-Import: rohe CSV-Datei wird serverseitig geparst (header-basiert). export const importLeadsCsvInput = z.object({ csv: z.string().min(1).max(2_000_000), projectId: z.string().uuid().optional(), // alle importierten Leads diesem Projekt zuordnen }); export type ImportLeadsCsvInput = z.infer;