'use client' import { BarChart3, TrendingUp, Users, Repeat, Activity, CalendarClock, ArrowRight, } 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 { Skeleton } from '@/components/ui/skeleton' import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs' import { PageHeader } from '../primitives' import { formatEuro, formatEuroCompact, formatDate } from '@/lib/format' import { useAos } from '@/store/aos' import { Bar, BarChart, ResponsiveContainer, Tooltip, XAxis, YAxis, Cell, Line, LineChart, CartesianGrid, Area, AreaChart, } from 'recharts' interface ReportingData { pipeline: { stages: { stage: string; label: string; count: number; valueCents: number }[] pipelineValue: number wonValue: number lostValue: number conversionRate: number avgDealSizeCents: number } utilization: { byProject: { label: string; totalHours: number; billableHours: number; utilizationPct: number }[] weekly: { label: string; hours: number }[] } recurring: { mrr: number arr: number recurringContractsMrr: number rebillMrr: number recurringExpensesMrr: number renewalsDue: { name: string; type: string; renewalDate: string; daysUntil: number }[] activeLicenses: number } revenue: { label: string; cents: number }[] activity: { group: string; count: number }[] estimateWinRate: number } export function ReportingView() { const { data, loading } = useFetch('/api/aos/reporting') const setView = useAos((s) => s.setView) if (loading || !data) { return (
) } const { pipeline, utilization, recurring, revenue, activity } = data const maxStageValue = Math.max(...pipeline.stages.map((s) => s.valueCents), 1) const maxHours = Math.max(...utilization.weekly.map((w) => w.hours), 1) const maxRev = Math.max(...revenue.map((r) => r.cents), 1) return (
{/* KPI summary */}

Offene Pipeline

{formatEuroCompact(pipeline.pipelineValue)}

Conversion: {pipeline.conversionRate}%

MRR

{formatEuroCompact(recurring.mrr)}

ARR: {formatEuroCompact(recurring.arr)}

Ø Deal-Größe

{formatEuroCompact(pipeline.avgDealSizeCents)}

Angebot-Win-Rate

{data.estimateWinRate}%

Pipeline Auslastung MRR & Erneuerungen Aktivität {/* Pipeline tab */}
Pipeline nach Phase
{pipeline.stages.filter((s) => s.stage !== 'lost').map((s) => (
{s.label} {s.count} Deals · {formatEuroCompact(s.valueCents)}
))}
Umsatz (6 Monate, bezahlt) formatEuroCompact(v).replace(' €', '')} width={50} /> [formatEuro(v), 'Umsatz']} />

Gewonnen (YTD)

{formatEuro(pipeline.wonValue)}

Verloren

{formatEuro(pipeline.lostValue)}

Conversion-Rate

{pipeline.conversionRate}%

{/* Utilization tab */}
Auslastung nach Projekt
{utilization.byProject.map((u) => (
{u.label} {u.billableHours}/{u.totalHours} h · {u.utilizationPct}%
))}
Wöchentliche Stunden (12 Wochen) [`${v} h`, 'Stunden']} /> {utilization.weekly.map((_, i) => ( ))}
{/* MRR & Renewals tab */}
MRR-Zusammensetzung
MRR gesamt {formatEuro(recurring.mrr)}
ARR (×12) {formatEuro(recurring.arr)}
Erneuerungen (60 Tage)
{recurring.renewalsDue.length === 0 && (

Keine anstehenden Erneuerungen.

)} {recurring.renewalsDue.map((r, i) => (

{r.name}

{r.type} · {formatDate(r.renewalDate)}

{r.daysUntil < 0 ? `${Math.abs(r.daysUntil)} d drüber` : `${r.daysUntil} d`}
))}

Aktive Lizenzen

{recurring.activeLicenses}

Erneuerungen ≤ 60 d

{recurring.renewalsDue.length}

MRR

{formatEuroCompact(recurring.mrr)}

ARR

{formatEuroCompact(recurring.arr)}

{/* Activity tab */}
Aktivität nach Aktionsgruppe (30 Tage) [`${v} Ereignisse`, 'Anzahl']} /> {activity.map((_, i) => ( ))} Umsatz- & Aktivitätstrend formatEuroCompact(v).replace(' €', '')} width={50} /> [formatEuro(v), 'Bezahlt']} />
) } function MrrRow({ label, cents, total, negative }: { label: string; cents: number; total: number; negative?: boolean }) { const pct = total > 0 ? Math.round((cents / total) * 100) : 0 return (
{label} {negative ? '−' : ''}{formatEuro(cents)}
) }