'use client' import { useState } from '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 { ScrollArea } from '@/components/ui/scroll-area' import { PageHeader } from '../primitives' import { useAos } from '@/store/aos' import { cn } from '@/lib/utils' import { formatDate, formatRelative } from '@/lib/format' import { ChevronLeft, ChevronRight, CalendarDays, AlertTriangle, Clock, CheckCircle2, Landmark, FileSignature, KeyRound, FolderKanban, Receipt, LifeBuoy, CheckCircle, } from 'lucide-react' interface Deadline { id: string date: string module: string type: string title: string detail: string urgency: 'past' | 'today' | 'soon' | 'future' daysUntil: number } interface CalendarDay { date: string isCurrentMonth: boolean deadlines: Deadline[] } interface CalendarData { month: string monthLabel: string weeks: CalendarDay[][] // fixed typo upcoming: Deadline[] overdue: Deadline[] stats: { total: number; overdue: number; today: number; thisWeek: number; byModule: Record } modules: string[] } const MODULE_META: Record = { accounting: { label: 'Buchhaltung', icon: Landmark, view: 'accounting' }, contracts: { label: 'Verträge', icon: FileSignature, view: 'contracts' }, licenses: { label: 'Lizenzen', icon: KeyRound, view: 'licenses' }, projects: { label: 'Projekte', icon: FolderKanban, view: 'projects' }, finance: { label: 'Finanzen', icon: Receipt, view: 'finance' }, support: { label: 'Support', icon: LifeBuoy, view: 'support' }, portal: { label: 'Portal', icon: CheckCircle, view: 'dashboard' }, } const URGENCY_META: Record = { past: { label: 'Überfällig', cls: 'border-foreground/60 bg-foreground/5', dot: 'bg-foreground' }, today: { label: 'Heute', cls: 'border-foreground/60 bg-foreground/10', dot: 'bg-foreground' }, soon: { label: 'Bald', cls: 'border-foreground/40 bg-foreground/5', dot: 'bg-foreground/70' }, future: { label: 'Geplant', cls: 'border-border', dot: 'bg-muted-foreground/50' }, } const WEEKDAYS = ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So'] export function CalendarView() { const now = new Date() const [monthParam, setMonthParam] = useState(`${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`) const { data, loading } = useFetch(`/api/aos/calendar?month=${monthParam}`) const setView = useAos((s) => s.setView) const navigateMonth = (delta: number) => { const [y, m] = monthParam.split('-').map(Number) const d = new Date(y, m - 1 + delta, 1) setMonthParam(`${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`) } const today = new Date() const isToday = (dateStr: string) => { const d = new Date(dateStr) return d.getDate() === today.getDate() && d.getMonth() === today.getMonth() && d.getFullYear() === today.getFullYear() } if (loading || !data) { return (
) } const { weeks, upcoming, overdue, stats, monthLabel } = data return (
{/* KPIs */}

Überfällig

{stats.overdue}

Heute

{stats.today}

Diese Woche

{stats.thisWeek}

Gesamt

{stats.total}

{/* Calendar grid */} {monthLabel}
{/* Weekday header */}
{WEEKDAYS.map((d) => (
{d}
))}
{/* Calendar grid */}
{weeks.flat().map((day, i) => { const dayDate = new Date(day.date) const isTodayCell = isToday(day.date) return (
{dayDate.getDate()}
{day.deadlines.slice(0, 3).map((dl) => { const meta = MODULE_META[dl.module] ?? { label: dl.module, icon: Clock, view: 'dashboard' } const Icon = meta.icon const urg = URGENCY_META[dl.urgency] return ( ) })} {day.deadlines.length > 3 && (

+{day.deadlines.length - 3} weitere

)}
) })}
{/* Upcoming + Overdue list */}
{/* Overdue */} {overdue.length > 0 && ( Überfällig ({overdue.length}) {overdue.map((d) => setView((MODULE_META[d.module]?.view ?? 'dashboard') as never)} />)} )} {/* Upcoming */} Anstehend ({upcoming.length}) {upcoming.length === 0 &&

Keine anstehenden Fristen.

} {upcoming.map((d) => setView((MODULE_META[d.module]?.view ?? 'dashboard') as never)} />)}
{/* By module */} Verteilung nach Modul {Object.entries(stats.byModule).map(([mod, count]) => { const meta = MODULE_META[mod] ?? { label: mod, icon: Clock, view: 'dashboard' } const Icon = meta.icon return (
{meta.label} {count}
) })}
) } function DeadlineItem({ d, onClick }: { d: Deadline; onClick: () => void }) { const meta = MODULE_META[d.module] ?? { label: d.module, icon: Clock, view: 'dashboard' } const Icon = meta.icon const urg = URGENCY_META[d.urgency] return ( ) }