Phase 6: polish — error boundaries, a11y, smoke test doc

Close out the read-only surface before cutover with three families of
small fixes that the previous phases left unfinished:

- Error boundaries: add src/app/error.tsx (route-segment), global-error.tsx
  (root crash fallback with its own minimal html/body — no Providers
  dependency since those may be the thing that crashed), and not-found.tsx
  for a Hebrew 404 instead of the default Next page.

- Accessibility: wire usePathname() into AppShell so the current nav item
  gets aria-current="page" and a gold underline. Add aria-label + aria-hidden
  on the icon-only buttons that Phase 5 left text-less (corpus trash,
  parties-field Plus). Nav gets an aria-label of its own.

- Metadata template: title on each route now reads "X · עוזר משפטי" via
  the layout.tsx title.template. Description localized to Jerusalem.

- README: full E2E smoke test checklist covering all 9 screens, plus a
  backend contract table so future phases know which hook wraps which
  endpoint. Documents the known Gitea→Coolify webhook issue.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-11 17:43:59 +00:00
parent fb1f73fa25
commit cbe9d60901
9 changed files with 375 additions and 55 deletions

57
web-ui/src/app/error.tsx Normal file
View File

@@ -0,0 +1,57 @@
"use client";
import { useEffect } from "react";
import Link from "next/link";
import { AlertTriangle } from "lucide-react";
import { Button } from "@/components/ui/button";
/*
* Route-segment error boundary. Next 16 App Router convention: this file
* catches render-time errors thrown below the root layout. `reset` clears
* the error and re-renders the segment; we keep the user's nav in place
* (no AppShell here — the shell re-renders from the layout above us).
*/
export default function ErrorPage({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
console.error("Route error:", error);
}, [error]);
return (
<main className="flex-1 w-full max-w-[1400px] mx-auto px-10 py-10">
<div className="max-w-xl mx-auto text-center space-y-5 py-16">
<AlertTriangle className="w-12 h-12 text-danger mx-auto" aria-hidden="true" />
<div className="space-y-2">
<h1 className="text-navy">משהו השתבש</h1>
<p className="text-ink-muted leading-relaxed">
נכשלה טעינת המסך. זה עשוי להיות כשל זמני ברשת או באחד מה-endpoints
של FastAPI.
</p>
{error.digest && (
<p className="text-[0.72rem] text-ink-light tabular-nums">
error id: {error.digest}
</p>
)}
{error.message && (
<code className="text-[0.78rem] text-ink-soft bg-rule-soft/60 rounded px-3 py-1 inline-block mt-2">
{error.message}
</code>
)}
</div>
<div className="flex items-center justify-center gap-3">
<Button onClick={reset} className="bg-navy hover:bg-navy-soft text-parchment">
נסה שוב
</Button>
<Button variant="outline" asChild>
<Link href="/">חזרה לבית</Link>
</Button>
</div>
</div>
</main>
);
}