import { Gauge, MailCheck, MapPinned, ShieldCheck, UsersRound, type LucideIcon, } from "lucide-react"; export { dashboardNavigation } from "./dashboard-navigation"; export type PipelineStage = { title: string; description: string; count: number; meta: string; icon: LucideIcon; }; export type DashboardKpi = { label: string; value: string; detail: string; }; export type ReviewQueueItem = { title: string; company: string; detail: string; }; export type LeadPriority = "high" | "medium" | "low" | "defer" | "blocked"; export type LeadContactStatus = | "new" | "missing_contact" | "audit_ready" | "outreach_ready" | "contacted" | "replied" | "do_not_contact"; export type LeadBlacklistStatus = "clear" | "blocked"; export type LeadDuplicateStatus = | "unchecked" | "unique" | "possible_duplicate" | "duplicate"; export type OutreachApprovalStatus = "draft" | "approved" | "rejected"; export type OutreachSendStatus = "not_sent" | "queued" | "sent" | "failed"; export type OutreachResponseStatus = | "none" | "manual_reply_recorded" | "no_interest" | "follow_up_needed"; export type OutreachSalesStatus = | "follow_up_planned" | "follow_up_sent" | "reply_received" | "not_interested" | "later" | "meeting_scheduled" | "proposal_requested" | "proposal_sent" | "won" | "lost" | "do_not_pursue"; export type LeadFunnelStageId = | "missing_contact" | "audit_ready" | "review_open" | "contacted" | "follow_up" | "deferred"; export type LeadFunnelStage = { id: LeadFunnelStageId; title: string; description: string; }; export type LeadFunnelOutreach = { approvalStatus?: OutreachApprovalStatus | null; sendStatus?: OutreachSendStatus | null; responseStatus?: OutreachResponseStatus | null; salesStatus?: OutreachSalesStatus | null; }; export type LeadFunnelInput = { id: string; companyName: string; niche?: string | null; address?: string | null; city?: string | null; postalCode?: string | null; priority: LeadPriority; contactStatus: LeadContactStatus; blacklistStatus: LeadBlacklistStatus; email?: string | null; phone?: string | null; contactPerson?: string | null; websiteDomain?: string | null; outreach?: LeadFunnelOutreach | null; }; export type LeadFunnelCard = { id: string; stageId: LeadFunnelStageId; company: string; niche: string; location: string; priorityLabel: string; contactStatusLabel: string; nextAction: string; websiteDomain?: string | null; contactDetail: string; }; export type LeadFunnelGroup = { stage: LeadFunnelStage; cards: LeadFunnelCard[]; }; export const leadFunnelStages: LeadFunnelStage[] = [ { id: "missing_contact", title: "Kontakt fehlt", description: "Leads ohne belastbare E-Mail oder Telefonnummer.", }, { id: "audit_ready", title: "Audit bereit", description: "Analyse ist vorbereitet und braucht Einordnung.", }, { id: "review_open", title: "Freigabe offen", description: "Kontaktstrategie, Audit-Link oder Text warten auf Review.", }, { id: "contacted", title: "Kontaktiert", description: "Erstkontakt ist erfolgt; Antwort wird manuell gepflegt.", }, { id: "follow_up", title: "Follow-up", description: "Respektvolle Wiedervorlage ohne automatischen Versand.", }, { id: "deferred", title: "Zurückgestellt", description: "Nicht jetzt kontaktieren oder bewusst pausieren.", }, ]; export const leadPriorityLabels: Record = { high: "Hoch", medium: "Mittel", low: "Niedrig", defer: "Zurückstellen", blocked: "Gesperrt", }; export const leadContactStatusLabels: Record = { new: "Neu", missing_contact: "Kontakt fehlt", audit_ready: "Audit bereit", outreach_ready: "Freigabe offen", contacted: "Kontaktiert", replied: "Antwort erfasst", do_not_contact: "Nicht kontaktieren", }; export const leadDuplicateStatusLabels: Record = { unchecked: "Noch nicht geprüft", unique: "Einzigartig", possible_duplicate: "Möglicher Doppelter", duplicate: "Duplikat", }; export const leadBlacklistStatusLabels: Record = { clear: "Offen", blocked: "Gesperrt", }; export const leadPriorityOptions: LeadPriority[] = [ "high", "medium", "low", "defer", "blocked", ]; export const leadContactStatusOptions: LeadContactStatus[] = [ "new", "missing_contact", "audit_ready", "outreach_ready", "contacted", "replied", "do_not_contact", ]; export const leadDuplicateStatusOptions: LeadDuplicateStatus[] = [ "unchecked", "unique", "possible_duplicate", "duplicate", ]; export const leadBlacklistStatusOptions: LeadBlacklistStatus[] = ["clear", "blocked"]; export function getLeadPriorityLabel(priority: LeadPriority): string { return leadPriorityLabels[priority]; } export function getLeadContactStatusLabel(status: LeadContactStatus): string { return leadContactStatusLabels[status]; } export function getLeadDuplicateStatusLabel(status: LeadDuplicateStatus): string { return leadDuplicateStatusLabels[status]; } export function getLeadBlacklistStatusLabel(status: LeadBlacklistStatus): string { return leadBlacklistStatusLabels[status]; } export function toLeadFunnelCard(lead: LeadFunnelInput): LeadFunnelCard { return { id: lead.id, stageId: getLeadFunnelStageId(lead), company: lead.companyName, niche: lead.niche ?? "Nische offen", location: formatLeadLocation(lead), priorityLabel: getLeadPriorityLabel(lead.priority), contactStatusLabel: getLeadContactStatusLabel(lead.contactStatus), nextAction: getLeadNextAction(lead), websiteDomain: lead.websiteDomain, contactDetail: formatContactDetail(lead), }; } export function groupLeadFunnelCards( leads: LeadFunnelInput[], ): LeadFunnelGroup[] { const cards = leads.map(toLeadFunnelCard); return leadFunnelStages.map((stage) => ({ stage, cards: cards.filter((card) => card.stageId === stage.id), })); } function getLeadFunnelStageId(lead: LeadFunnelInput): LeadFunnelStageId { if ( lead.blacklistStatus === "blocked" || lead.priority === "defer" || lead.priority === "blocked" || lead.contactStatus === "do_not_contact" ) { return "deferred"; } if (lead.outreach?.responseStatus === "follow_up_needed") { return "follow_up"; } if ( lead.outreach?.salesStatus === "follow_up_planned" && lead.outreach.sendStatus === "sent" ) { return "follow_up"; } if ( lead.contactStatus === "contacted" || lead.contactStatus === "replied" || lead.outreach?.sendStatus === "sent" ) { return "contacted"; } if ( lead.contactStatus === "outreach_ready" || lead.outreach?.approvalStatus === "draft" ) { return "review_open"; } if (lead.contactStatus === "audit_ready") { return "audit_ready"; } return "missing_contact"; } function getLeadNextAction(lead: LeadFunnelInput): string { const stageId = getLeadFunnelStageId(lead); if (stageId === "deferred") { return "Zurückstellung prüfen"; } if (stageId === "follow_up") { return "Follow-up manuell prüfen"; } if (stageId === "contacted") { return "Antwortstatus nachtragen"; } if (stageId === "review_open") { return "Freigabe im Review öffnen"; } if (stageId === "audit_ready") { return "Audit prüfen"; } return "Kontaktquelle recherchieren"; } function formatLeadLocation(lead: LeadFunnelInput): string { if (lead.postalCode && lead.city) { return `${lead.postalCode} ${lead.city}`; } return lead.city ?? lead.postalCode ?? lead.address ?? "Ort offen"; } function formatContactDetail(lead: LeadFunnelInput): string { const details = [lead.email, lead.phone].filter(Boolean); if (details.length > 0) { return details.join(" · "); } return "Keine Kontaktdaten"; } export const pipelineStages: PipelineStage[] = [ { title: "Kampagnen", description: "Aktive Suchlaeufe nach Kategorie, PLZ und Radius.", count: 3, meta: "1 Lauf heute geplant", icon: MapPinned, }, { title: "Lead-Recherche", description: "Neue Places-Quellen, Kontaktluecken und Dubletten.", count: 18, meta: "5 Leads brauchen E-Mail-Quelle", icon: UsersRound, }, { title: "Audit-Freigabe", description: "Interne Audits warten auf manuelle Prüfung.", count: 6, meta: "2 Seiten bereit zur Veröffentlichung", icon: ShieldCheck, }, { title: "Outreach", description: "Freigegebene E-Mails und Telefon-Skripte.", count: 4, meta: "Keine automatische Kontaktaufnahme", icon: MailCheck, }, ]; export const dashboardKpis: DashboardKpi[] = [ { label: "Neue Leads", value: "18", detail: "aus 3 aktiven Kampagnen", }, { label: "Audit-Entwürfe", value: "6", detail: "manuelle Freigabe offen", }, { label: "Outreach bereit", value: "4", detail: "E-Mail und Telefon-Skript", }, { label: "Antworten", value: "2", detail: "manuell nachzutragen", }, ]; export const reviewQueue: ReviewQueueItem[] = [ { title: "Audit-Freigabe pruefen", company: "Malerbetrieb Klein", detail: "Mobile Kontaktfuehrung und lokaler CTA fehlen.", }, { title: "Kontaktstrategie bestaetigen", company: "Physio am Park", detail: "Telefon zuerst, E-Mail nach persoenlicher Einordnung.", }, { title: "Follow-up vormerken", company: "Tischlerei Weber", detail: "Respektvolles Follow-up in 5 Tagen, kein Autoversand.", }, ]; export const pipelineHealth = [ { label: "Recherche", value: "hoch", icon: Gauge, }, { label: "Freigabe", value: "manuell", icon: ShieldCheck, }, { label: "Versand", value: "gesperrt bis Review", icon: MailCheck, }, ];