Files
KanzlAI-mGMT/frontend/src/components/dashboard/RecentActivityList.tsx
m 84b178edbf 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
2026-03-25 19:29:13 +01:00

81 lines
2.4 KiB
TypeScript

"use client";
import Link from "next/link";
import { formatDistanceToNow, parseISO } from "date-fns";
import { de } from "date-fns/locale";
import {
FileText,
Scale,
Calendar,
Clock,
MessageSquare,
ChevronRight,
} from "lucide-react";
import type { RecentActivity } from "@/lib/types";
const EVENT_ICONS: Record<string, typeof FileText> = {
status_changed: Scale,
deadline_created: Clock,
appointment_created: Calendar,
document_uploaded: FileText,
note_added: MessageSquare,
};
interface Props {
activities: RecentActivity[];
}
export function RecentActivityList({ activities }: Props) {
const safe = Array.isArray(activities) ? activities : [];
if (safe.length === 0) {
return null;
}
return (
<div className="rounded-xl border border-neutral-200 bg-white p-5">
<h2 className="text-sm font-semibold text-neutral-900">
Letzte Aktivität
</h2>
<div className="mt-3 divide-y divide-neutral-100">
{safe.map((activity) => {
const Icon = EVENT_ICONS[activity.event_type ?? ""] ?? FileText;
const timeAgo = activity.created_at
? formatDistanceToNow(parseISO(activity.created_at), {
addSuffix: true,
locale: de,
})
: "";
return (
<Link
key={activity.id}
href={`/cases/${activity.case_id}`}
className="group flex items-center gap-3 py-2.5 transition-colors first:pt-0 last:pb-0 hover:bg-neutral-50 -mx-5 px-5"
>
<div className="rounded-md bg-neutral-100 p-1.5">
<Icon className="h-3.5 w-3.5 text-neutral-500" />
</div>
<div className="min-w-0 flex-1">
<p className="truncate text-sm text-neutral-900">
{activity.title}
</p>
<div className="flex items-center gap-2 text-xs text-neutral-500">
<span>{activity.case_number}</span>
{timeAgo && (
<>
<span className="text-neutral-300">·</span>
<span>{timeAgo}</span>
</>
)}
</div>
</div>
<ChevronRight className="h-4 w-4 shrink-0 text-neutral-300 transition-colors group-hover:text-neutral-500" />
</Link>
);
})}
</div>
</div>
);
}