'use client' import { useState } from 'react' import { Plus, Building2, Mail, Phone, Globe, MapPin, Users as UsersIcon } from 'lucide-react' import { useFetch } from '@/hooks/use-fetch' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs' import { Skeleton } from '@/components/ui/skeleton' import { PageHeader } from '../primitives' import { CreateDealDialog } from '../create-deal-dialog' import { formatEuro, formatDate } from '@/lib/format' import { useAos } from '@/store/aos' import { cn } from '@/lib/utils' interface CrmData { companies: (Company & { persons: Person[]; deals: Deal[] })[] deals: (Deal & { company: Company })[] persons: (Person & { company: Company | null })[] pipeline: { stage: string; label: string; deals: (Deal & { company: Company })[] }[] } type Company = { id: string; name: string; legalForm: string | null; vatId: string | null; website: string | null; email: string | null; phone: string | null; city: string | null; zip: string | null; street: string | null; source: string | null; tags: string | null } type Person = { id: string; firstName: string; lastName: string; email: string | null; phone: string | null; role: string | null } type Deal = { id: string; title: string; valueCents: number; probability: number; stage: string; expectedClose: Date | string | null; lossReason: string | null } const STAGE_COLORS: Record = { lead: 'bg-muted-foreground/20', qualified: 'bg-muted-foreground/35', proposal: 'bg-muted-foreground/55', negotiation: 'bg-muted-foreground/75', won: 'bg-foreground', lost: 'bg-muted-foreground/15', } export function CrmView() { const { data, loading, refetch } = useFetch('/api/aos/crm') const setSelectedCompany = useAos((s) => s.setSelectedCompany) const [createOpen, setCreateOpen] = useState(false) if (loading || !data) { return (
{Array.from({ length: 4 }).map((_, i) => ( ))}
) } const totalPipeline = data.deals.filter((d) => d.stage !== 'lost' && d.stage !== 'won').reduce((s, d) => s + d.valueCents, 0) const wonTotal = data.deals.filter((d) => d.stage === 'won').reduce((s, d) => s + d.valueCents, 0) return (
setCreateOpen(true)}> Deal } />

Firmen

{data.companies.length}

Ansprechpartner

{data.persons.length}

Offene Pipeline

{formatEuro(totalPipeline)}

Gewonnen

{formatEuro(wonTotal)}

Pipeline Firmen Ansprechpartner ({ id: c.id, name: c.name }))} onCreated={refetch} />
) } function PipelineBoard({ pipeline }: { pipeline: CrmData['pipeline'] }) { return (
{pipeline.map((col) => { const value = col.deals.reduce((s, d) => s + d.valueCents, 0) return (
{col.label}
{col.deals.length}
{value > 0 && (
{formatEuro(value)}
)}
{col.deals.map((d) => (

{d.title}

{d.company.name}

{formatEuro(d.valueCents)} {d.probability}%
{d.expectedClose && (

⟶ {formatDate(d.expectedClose)}

)} {d.lossReason && (

Verloren: {d.lossReason}

)}
))} {col.deals.length === 0 && (
Leer
)}
) })}
) } function CompaniesTable({ companies, onSelectCompany }: { companies: CrmData['companies']; onSelectCompany: (id: string) => void }) { const [q, setQ] = useState('') const filtered = companies.filter( (c) => c.name.toLowerCase().includes(q.toLowerCase()) || (c.city ?? '').toLowerCase().includes(q.toLowerCase()) ) return ( Firmen ({filtered.length}) setQ(e.target.value)} placeholder="Filtern…" className="h-8 w-40 rounded border border-input bg-background px-2 text-xs outline-none focus-visible:ring-1 focus-visible:ring-ring" />
{filtered.map((c) => ( onSelectCompany(c.id)}> ))}
Firma Rechtsform Ort USt-IdNr. Kontakte Deals Tags
{c.legalForm && {c.legalForm}} {c.city && ( {c.zip} {c.city} )} {c.vatId ?? '—'} {c.persons.length} {c.deals.length}
{c.tags?.split(',').map((t) => ( {t.trim()} ))}
) } function PersonsTable({ persons, onSelectCompany }: { persons: CrmData['persons']; onSelectCompany: (id: string) => void }) { return (
{persons.map((p) => ( p.companyId && onSelectCompany(p.companyId)}> ))}
Name Firma Rolle Kontakt
{p.firstName} {p.lastName} {p.company?.name ?? '—'} {p.role ?? '—'}
{p.email && {p.email}} {p.phone && {p.phone}}
) }