feat: Phase B — interactive dashboard, breadcrumbs, clickable navigation

- Breadcrumb component: reusable nav with items array (label+href)
- DeadlineTrafficLights: buttons → Links to /fristen?status={filter}
- CaseOverviewGrid: static metrics → clickable Links to /cases?status={filter}
- UpcomingTimeline: items → clickable Links to /fristen/{id} or /termine/{id}
  with case number links and hover chevron
- QuickActions: swap CalDAV Sync for "Neuer Termin" → /termine/neu,
  fix "Frist eintragen" → /fristen/neu
- AISummaryCard: add RefreshCw button with spinning animation
- RecentActivityList: new component showing recent case events
- DeadlineList: accept initialStatus prop, add this_week/ok filters
- fristen/page.tsx: read searchParams.status for initial filter
- Add breadcrumbs to dashboard, fristen, cases, termine pages
- Add RecentActivity type, update DashboardData type
This commit is contained in:
m
2026-03-25 19:29:13 +01:00
parent 9ad58e1ba3
commit 84b178edbf
13 changed files with 357 additions and 87 deletions

View File

@@ -1,6 +1,7 @@
"use client";
import { useEffect, useRef } from "react";
import Link from "next/link";
import { AlertTriangle, Clock, CheckCircle } from "lucide-react";
import type { DeadlineSummary } from "@/lib/types";
@@ -27,10 +28,9 @@ function AnimatedCount({ value }: { value: number }) {
interface Props {
data: DeadlineSummary;
onFilter?: (filter: "overdue" | "this_week" | "ok") => void;
}
export function DeadlineTrafficLights({ data, onFilter }: Props) {
export function DeadlineTrafficLights({ data }: Props) {
const safe = data ?? { overdue_count: 0, due_this_week: 0, due_next_week: 0, ok_count: 0 };
const cards = [
{
@@ -38,6 +38,7 @@ export function DeadlineTrafficLights({ data, onFilter }: Props) {
label: "Überfällig",
count: safe.overdue_count ?? 0,
icon: AlertTriangle,
href: "/fristen?status=overdue",
bg: "bg-red-50",
border: "border-red-200",
iconColor: "text-red-500",
@@ -51,6 +52,7 @@ export function DeadlineTrafficLights({ data, onFilter }: Props) {
label: "Diese Woche",
count: safe.due_this_week ?? 0,
icon: Clock,
href: "/fristen?status=this_week",
bg: "bg-amber-50",
border: "border-amber-200",
iconColor: "text-amber-500",
@@ -64,6 +66,7 @@ export function DeadlineTrafficLights({ data, onFilter }: Props) {
label: "Im Zeitplan",
count: (safe.ok_count ?? 0) + (safe.due_next_week ?? 0),
icon: CheckCircle,
href: "/fristen?status=ok",
bg: "bg-emerald-50",
border: "border-emerald-200",
iconColor: "text-emerald-500",
@@ -77,9 +80,9 @@ export function DeadlineTrafficLights({ data, onFilter }: Props) {
return (
<div className="grid grid-cols-1 gap-4 sm:grid-cols-3">
{cards.map((card) => (
<button
<Link
key={card.key}
onClick={() => onFilter?.(card.key)}
href={card.href}
className={`group relative overflow-hidden rounded-xl border ${card.border} ${card.bg} ${card.ring} p-6 text-left transition-all hover:shadow-md active:scale-[0.98]`}
>
{card.pulse && (
@@ -99,7 +102,7 @@ export function DeadlineTrafficLights({ data, onFilter }: Props) {
<div className={`mt-4 text-4xl font-bold tracking-tight ${card.countColor}`}>
<AnimatedCount value={card.count} />
</div>
</button>
</Link>
))}
</div>
);