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,8 +1,9 @@
"use client";
import Link from "next/link";
import { format, parseISO, isToday, isTomorrow } from "date-fns";
import { de } from "date-fns/locale";
import { Clock, Calendar, MapPin } from "lucide-react";
import { Clock, Calendar, MapPin, ChevronRight } from "lucide-react";
import type { UpcomingDeadline, UpcomingAppointment } from "@/lib/types";
interface Props {
@@ -80,8 +81,12 @@ export function UpcomingTimeline({ deadlines, appointments }: Props) {
function TimelineEntry({ item }: { item: TimelineItem }) {
if (item.type === "deadline") {
const d = item.data;
const href = `/fristen/${d.id}`;
return (
<div className="flex items-start gap-3 rounded-lg border border-neutral-100 bg-neutral-50/50 px-3 py-2.5">
<Link
href={href}
className="group flex items-start gap-3 rounded-lg border border-neutral-100 bg-neutral-50/50 px-3 py-2.5 transition-colors hover:border-neutral-200 hover:bg-neutral-100/50"
>
<div className="mt-0.5 rounded-md bg-amber-50 p-1">
<Clock className="h-3.5 w-3.5 text-amber-500" />
</div>
@@ -90,19 +95,40 @@ function TimelineEntry({ item }: { item: TimelineItem }) {
{d.title}
</p>
<p className="mt-0.5 truncate text-xs text-neutral-500">
{d.case_number} · {d.case_title}
{d.case_id ? (
<span
onClick={(e) => e.stopPropagation()}
className="inline"
>
<Link
href={`/cases/${d.case_id}`}
className="underline decoration-neutral-300 hover:text-neutral-900 hover:decoration-neutral-500"
>
{d.case_number}
</Link>
{" · "}
</span>
) : (
<>{d.case_number} · </>
)}
{d.case_title}
</p>
</div>
<span className="shrink-0 text-xs font-medium text-amber-600">
Frist
</span>
</div>
<div className="flex shrink-0 items-center gap-1.5">
<span className="text-xs font-medium text-amber-600">Frist</span>
<ChevronRight className="h-3.5 w-3.5 text-neutral-300 transition-colors group-hover:text-neutral-500" />
</div>
</Link>
);
}
const a = item.data;
const href = `/termine/${a.id}`;
return (
<div className="flex items-start gap-3 rounded-lg border border-neutral-100 bg-neutral-50/50 px-3 py-2.5">
<Link
href={href}
className="group flex items-start gap-3 rounded-lg border border-neutral-100 bg-neutral-50/50 px-3 py-2.5 transition-colors hover:border-neutral-200 hover:bg-neutral-100/50"
>
<div className="mt-0.5 rounded-md bg-blue-50 p-1">
<Calendar className="h-3.5 w-3.5 text-blue-500" />
</div>
@@ -121,7 +147,20 @@ function TimelineEntry({ item }: { item: TimelineItem }) {
</span>
</>
)}
{a.case_number && (
{a.case_number && a.case_id && (
<>
<span className="text-neutral-300">·</span>
<span onClick={(e) => e.stopPropagation()}>
<Link
href={`/cases/${a.case_id}`}
className="underline decoration-neutral-300 hover:text-neutral-900 hover:decoration-neutral-500"
>
{a.case_number}
</Link>
</span>
</>
)}
{a.case_number && !a.case_id && (
<>
<span className="text-neutral-300">·</span>
<span>{a.case_number}</span>
@@ -129,9 +168,10 @@ function TimelineEntry({ item }: { item: TimelineItem }) {
)}
</div>
</div>
<span className="shrink-0 text-xs font-medium text-blue-600">
Termin
</span>
</div>
<div className="flex shrink-0 items-center gap-1.5">
<span className="text-xs font-medium text-blue-600">Termin</span>
<ChevronRight className="h-3.5 w-3.5 text-neutral-300 transition-colors group-hover:text-neutral-500" />
</div>
</Link>
);
}