Files
KanzlAI-mGMT/frontend/src/app/(app)/dashboard/page.tsx
m f81a2492c6 feat: UI polish — responsive, loading/empty/error states, German fixes (Phase 3Q)
- Responsive sidebar: collapses on mobile with hamburger menu, slide-in animation
- Skeleton loaders: dashboard cards, case table, case detail page
- Empty states: friendly messages with icons for cases, deadlines, parties, documents
- Error states: retry button on dashboard, proper error message on case not found
- Form validation: inline error messages on case creation form
- German language: fix all missing umlauts (Zurück, wählen, Anhängig, Verfügung, etc.)
- Status labels: display German translations instead of raw status values
- Transitions: fade-in animations on page load, hover/transition-colors on all interactive elements
- Focus states: focus-visible ring for keyboard accessibility
- Mobile layout: stacking for filters, forms, tabs; horizontal scroll for tables
- Extraction results: card layout on mobile, table on desktop
- Missing types: add DashboardData, DeadlineSummary, CaseSummary, ExtractedDeadline etc.
- Fix QuickActions links to use correct routes (/cases/new, /ai/extract)
- Consistent input focus styles across all forms
2026-03-25 14:16:30 +01:00

101 lines
3.2 KiB
TypeScript

"use client";
import { useQuery } from "@tanstack/react-query";
import { api } from "@/lib/api";
import type { DashboardData } from "@/lib/types";
import { DeadlineTrafficLights } from "@/components/dashboard/DeadlineTrafficLights";
import { CaseOverviewGrid } from "@/components/dashboard/CaseOverviewGrid";
import { UpcomingTimeline } from "@/components/dashboard/UpcomingTimeline";
import { AISummaryCard } from "@/components/dashboard/AISummaryCard";
import { QuickActions } from "@/components/dashboard/QuickActions";
import { Skeleton, SkeletonCard } from "@/components/ui/Skeleton";
import { AlertTriangle, RefreshCw } from "lucide-react";
function DashboardSkeleton() {
return (
<div className="mx-auto max-w-6xl space-y-6">
<div>
<Skeleton className="h-5 w-28" />
<Skeleton className="mt-2 h-3.5 w-52" />
</div>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-3">
{[1, 2, 3].map((i) => (
<Skeleton key={i} className="h-28 rounded-xl" />
))}
</div>
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
<div className="lg:col-span-2">
<SkeletonCard className="min-h-[200px]" />
</div>
<div className="space-y-6">
<SkeletonCard />
<SkeletonCard />
<SkeletonCard />
</div>
</div>
</div>
);
}
export default function DashboardPage() {
const { data, isLoading, error, refetch } = useQuery({
queryKey: ["dashboard"],
queryFn: () => api.get<DashboardData>("/dashboard"),
refetchInterval: 60_000,
});
if (isLoading) {
return <DashboardSkeleton />;
}
if (error || !data) {
return (
<div className="mx-auto max-w-md py-16 text-center">
<div className="mx-auto mb-3 rounded-xl bg-red-50 p-3 w-fit">
<AlertTriangle className="h-6 w-6 text-red-500" />
</div>
<h2 className="text-sm font-medium text-neutral-900">
Dashboard konnte nicht geladen werden
</h2>
<p className="mt-1 text-sm text-neutral-500">
Bitte versuchen Sie es erneut oder prüfen Sie Ihre Verbindung.
</p>
<button
onClick={() => refetch()}
className="mt-4 inline-flex items-center gap-1.5 rounded-md bg-neutral-900 px-3 py-1.5 text-sm font-medium text-white transition-colors hover:bg-neutral-800"
>
<RefreshCw className="h-3.5 w-3.5" />
Erneut laden
</button>
</div>
);
}
return (
<div className="animate-fade-in mx-auto max-w-6xl space-y-6">
<div>
<h1 className="text-lg font-semibold text-neutral-900">Dashboard</h1>
<p className="mt-0.5 text-sm text-neutral-500">
Fristenübersicht und Kanzlei-Status
</p>
</div>
<DeadlineTrafficLights data={data.deadline_summary} />
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
<div className="lg:col-span-2">
<UpcomingTimeline
deadlines={data.upcoming_deadlines}
appointments={data.upcoming_appointments}
/>
</div>
<div className="space-y-6">
<CaseOverviewGrid data={data.case_summary} />
<AISummaryCard data={data} />
<QuickActions />
</div>
</div>
</div>
);
}