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
This commit is contained in:
m
2026-03-25 14:16:30 +01:00
parent 2cf01073a3
commit f81a2492c6
23 changed files with 834 additions and 288 deletions

View File

@@ -0,0 +1,28 @@
import type { LucideIcon } from "lucide-react";
interface EmptyStateProps {
icon: LucideIcon;
title: string;
description?: string;
action?: React.ReactNode;
}
export function EmptyState({
icon: Icon,
title,
description,
action,
}: EmptyStateProps) {
return (
<div className="flex flex-col items-center justify-center rounded-xl border border-dashed border-neutral-300 bg-white px-6 py-12 text-center">
<div className="rounded-xl bg-neutral-100 p-3">
<Icon className="h-6 w-6 text-neutral-400" />
</div>
<h3 className="mt-3 text-sm font-medium text-neutral-900">{title}</h3>
{description && (
<p className="mt-1 max-w-sm text-sm text-neutral-500">{description}</p>
)}
{action && <div className="mt-4">{action}</div>}
</div>
);
}

View File

@@ -0,0 +1,43 @@
export function Skeleton({ className = "" }: { className?: string }) {
return (
<div
className={`animate-pulse rounded-md bg-neutral-200/60 ${className}`}
/>
);
}
export function SkeletonCard({ className = "" }: { className?: string }) {
return (
<div
className={`rounded-xl border border-neutral-200 bg-white p-5 ${className}`}
>
<Skeleton className="h-4 w-24" />
<div className="mt-4 space-y-3">
<Skeleton className="h-3 w-full" />
<Skeleton className="h-3 w-3/4" />
<Skeleton className="h-3 w-1/2" />
</div>
</div>
);
}
export function SkeletonTable({ rows = 5 }: { rows?: number }) {
return (
<div className="overflow-hidden rounded-md border border-neutral-200 bg-white">
<div className="border-b border-neutral-100 px-4 py-3">
<Skeleton className="h-3 w-full" />
</div>
{Array.from({ length: rows }).map((_, i) => (
<div
key={i}
className="flex items-center gap-4 border-b border-neutral-100 px-4 py-3 last:border-b-0"
>
<Skeleton className="h-3 w-20" />
<Skeleton className="h-3 flex-1" />
<Skeleton className="h-3 w-16" />
<Skeleton className="h-3 w-12" />
</div>
))}
</div>
);
}