import { z } from "zod/v4"; const CAMPAIGN_NAME_MIN = 3; const CAMPAIGN_NAME_MAX = 120; export const CAMPAIGN_COUNTRY_CODE = "DE"; export const CAMPAIGN_COUNTRY_NAME = "Deutschland"; const MIN_RADIUS_KM = 1; const MAX_RADIUS_KM = 5000; const MIN_LEADS_PER_RUN = 1; const MAX_LEADS_PER_RUN = 9999; const MIN_AUDITS_PER_RUN = 1; const MAX_AUDITS_PER_RUN = 9999; export const CAMPAIGN_CATEGORY_MODES = ["preset", "custom"] as const; export type CampaignCategoryMode = (typeof CAMPAIGN_CATEGORY_MODES)[number]; export const CAMPAIGN_RECURRENCES = [ "manual", "daily", "weekly", "monthly", ] as const; export type CampaignRecurrence = (typeof CAMPAIGN_RECURRENCES)[number]; export const CAMPAIGN_STATUSES = ["active", "paused"] as const; export type CampaignStatus = (typeof CAMPAIGN_STATUSES)[number]; export const ORTHODOX_POSTAL_CODE_MESSAGE = "Bitte eine gültige deutsche PLZ (Postleitzahl) mit genau 5 Ziffern angeben."; const postalCodeSchema = z .string() .regex(/^\d{5}$/, ORTHODOX_POSTAL_CODE_MESSAGE); const nonEmptyString = (label: string) => z .string() .trim() .min(1, `${label} ist erforderlich.`); const positiveBoundedInt = (label: string, min: number, max: number) => z .number({ message: `${label} muss eine Zahl sein.` }) .finite(`${label} muss eine Zahl sein.`) .min(min, `${label} muss mindestens ${min} sein.`) .int(`${label} muss eine ganze Zahl sein.`) .max(max, `${label} muss maximal ${max} sein.`); export const campaignFormSchema = z .object({ name: nonEmptyString("Name").min(CAMPAIGN_NAME_MIN).max(CAMPAIGN_NAME_MAX), categoryMode: z.union( CAMPAIGN_CATEGORY_MODES.map((value) => z.literal(value)), { error: "Bitte zwischen vorgegebener Kategorie oder eigener Kategorie wählen.", }, ), category: nonEmptyString("Kategorie"), customSearchTerm: z.string().optional(), postalCode: postalCodeSchema, radiusKm: positiveBoundedInt("Radius", MIN_RADIUS_KM, MAX_RADIUS_KM), maxNewLeadsPerRun: positiveBoundedInt( "Max. neue Leads", MIN_LEADS_PER_RUN, MAX_LEADS_PER_RUN, ), maxAuditsPerRun: positiveBoundedInt( "Max. Audits", MIN_AUDITS_PER_RUN, MAX_AUDITS_PER_RUN, ), recurrence: z.union( CAMPAIGN_RECURRENCES.map((value) => z.literal(value)), { error: "Bitte eine gültige Häufigkeit wählen: manuell, täglich, wöchentlich oder monatlich.", }, ), status: z.union( CAMPAIGN_STATUSES.map((value) => z.literal(value)), { error: "Status ist ungültig." }, ), }) .superRefine((values, ctx) => { const needsCustomSearchTerm = values.categoryMode === "custom" || values.category === "Anderes"; if (needsCustomSearchTerm && !values.customSearchTerm?.trim()) { ctx.addIssue({ code: "custom", path: ["customSearchTerm"], message: "Für Kategorie 'Anderes' ist ein eigener Suchbegriff erforderlich.", }); } }); export const campaignFormDefaults = { name: "", status: "active" as CampaignStatus, categoryMode: "preset" as CampaignCategoryMode, category: "Anwalt", customSearchTerm: "", recurrence: "daily" as CampaignRecurrence, radiusKm: 10, maxNewLeadsPerRun: 5, maxAuditsPerRun: 5, postalCode: "10115", }; export function mapCampaignFormToPayload(values: Record) { return { ...values, countryCode: CAMPAIGN_COUNTRY_CODE as "DE", country: CAMPAIGN_COUNTRY_NAME as "Deutschland", }; }