- 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
67 lines
2.0 KiB
TypeScript
67 lines
2.0 KiB
TypeScript
"use client";
|
|
|
|
import type { CaseEvent } from "@/lib/types";
|
|
import { format } from "date-fns";
|
|
import { de } from "date-fns/locale";
|
|
import { Activity } from "lucide-react";
|
|
|
|
const EVENT_ICONS: Record<string, string> = {
|
|
case_created: "bg-emerald-500",
|
|
status_changed: "bg-amber-500",
|
|
party_added: "bg-blue-500",
|
|
case_archived: "bg-neutral-400",
|
|
document_uploaded: "bg-violet-500",
|
|
deadline_created: "bg-red-500",
|
|
};
|
|
|
|
interface CaseTimelineProps {
|
|
events: CaseEvent[];
|
|
}
|
|
|
|
export function CaseTimeline({ events }: CaseTimelineProps) {
|
|
if (events.length === 0) {
|
|
return (
|
|
<div className="flex flex-col items-center py-8 text-center">
|
|
<div className="rounded-xl bg-neutral-100 p-3">
|
|
<Activity className="h-5 w-5 text-neutral-400" />
|
|
</div>
|
|
<p className="mt-2 text-sm text-neutral-500">
|
|
Keine Ereignisse vorhanden.
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="relative space-y-0">
|
|
{events.map((event, i) => (
|
|
<div key={event.id} className="relative flex gap-3 pb-6">
|
|
{i < events.length - 1 && (
|
|
<div className="absolute left-[7px] top-4 h-full w-px bg-neutral-200" />
|
|
)}
|
|
<div
|
|
className={`mt-1 h-[15px] w-[15px] shrink-0 rounded-full border-2 border-white ${EVENT_ICONS[event.event_type ?? ""] ?? "bg-neutral-300"}`}
|
|
/>
|
|
<div className="min-w-0 flex-1">
|
|
<p className="text-sm font-medium text-neutral-900">
|
|
{event.title}
|
|
</p>
|
|
{event.description && (
|
|
<p className="mt-0.5 text-sm text-neutral-500">
|
|
{event.description}
|
|
</p>
|
|
)}
|
|
<p className="mt-1 text-xs text-neutral-400">
|
|
{format(
|
|
new Date(event.event_date ?? event.created_at),
|
|
"d. MMM yyyy, HH:mm",
|
|
{ locale: de },
|
|
)}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|