Files
legal-ai/web-ui/src/app/page.tsx
Chaim 81050181d7
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 4s
Lint — undefined names / undefined-names (pull_request) Successful in 11s
fix(ui): nav & layout — decision-editor in band, compose back-link, wider cases table
Chair-directed navigation/layout fixes (existing components, no new design):

1. "פתח עורך החלטה" moved into the case-page band actions (right after "העלאת
   מסמכים") so it's reachable from EVERY tab, not only סקירה. Removed the now-
   redundant full-width CTA from the overview tab.
2. Prominent "→ חזרה לדף התיק" back-link added to the /compose header (the
   breadcrumb link was too subtle).
3. Home cases table: rail trimmed 360→280px and the title cell made min-w-0 so the
   table gets the width it needs and no longer shows a horizontal scrollbar.

No backend / API change. The larger NEW citation-verification panel stays gated
behind Claude Design (preview 24-citation-verification already pushed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 17:56:21 +00:00

181 lines
8.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useMemo } from "react";
import Link from "next/link";
import { AppShell } from "@/components/app-shell";
import { KPICards } from "@/components/cases/kpi-cards";
import { StatusDonut } from "@/components/cases/status-donut";
import { AppealTypeBars, subtypeOf } from "@/components/cases/appeal-type-bars";
import { CasesTable } from "@/components/cases/cases-table";
import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { useCases, type Case } from "@/lib/api/cases";
import {
usePendingApprovals,
type ApprovalSeverity,
} from "@/lib/api/chair";
// severity dot per the approved 04-home mockup gate-list (.dot.high/.med/.ok)
const SEVERITY_DOT: Record<ApprovalSeverity, string> = {
high: "bg-danger",
medium: "bg-warn",
low: "bg-info",
ok: "bg-success",
};
export default function HomePage() {
const { data, isPending, error } = useCases(true);
// INV-IA1 pointer: surface the single approvals aggregate on the dashboard,
// deep-linking to /approvals (the owner) — never a competing client counter.
const { data: approvals } = usePendingApprovals();
const { permits, levies } = useMemo(() => {
const permits: Case[] = [];
const levies: Case[] = [];
(data ?? []).forEach((c) => {
const s = subtypeOf(c);
if (s === "building_permit") permits.push(c);
else if (s === "betterment_levy" || s === "compensation_197") levies.push(c);
else permits.push(c); // fallback bucket — keep visible
});
return { permits, levies };
}, [data]);
return (
<AppShell>
<section className="space-y-7">
<header className="flex items-end justify-between gap-6 flex-wrap">
<div className="space-y-1.5">
<div className="text-[0.75rem] uppercase tracking-[0.12em] text-gold-deep">
ועדת ערר לתכנון ובנייה · ירושלים
</div>
<h1 className="text-navy">עוזר משפטי</h1>
<p className="text-ink-muted text-base max-w-2xl leading-relaxed">
מבט-על על הוועדה תיקים, אישורים ופעילות אחרונה במקום אחד.
</p>
</div>
<div className="flex gap-2.5">
<Button asChild className="bg-gold text-white hover:bg-gold-deep border-transparent">
<Link href="/cases/new">+ תיק חדש</Link>
</Button>
<Button asChild variant="outline" className="border-rule text-navy">
<Link href="/precedents">חיפוש בקורפוס</Link>
</Button>
</div>
</header>
<div className="h-[2px] bg-gradient-to-l from-transparent via-gold to-transparent" />
{/* KPI row — mockup 04 .kpis (4-up, gold-washed "ממתינים לאישור") */}
<KPICards cases={data} loading={isPending} />
{/* two-column body — main flow + narrow gold gate rail (mockup 04 .cols).
Rail trimmed 360→280 so the cases table gets the width it needs and
no longer needs a horizontal scrollbar (chair request). */}
<div className="grid gap-6 lg:grid-cols-[1fr_280px]">
<div className="space-y-6 min-w-0">
{/* "תיקים לפי סטטוס" (פסים אופקיים) הוסר — פיזור-הסטטוסים מוצג
בדונאט "פיזור סטטוסים" בטור-הצד (#1). */}
{/* live case tables kept in full (richer than the mockup's single feed) */}
<Card className="bg-surface border-rule shadow-sm">
<CardContent className="px-6 py-5">
<div className="flex items-center justify-between gap-3 mb-4 flex-wrap">
<div className="flex items-baseline gap-3">
<h2 className="text-navy text-xl mb-0">רישוי ובנייה</h2>
<span className="text-[0.72rem] uppercase tracking-[0.08em] text-ink-muted">
עררים 1xxx
</span>
</div>
<span className="text-[0.72rem] uppercase tracking-[0.08em] text-ink-muted">
מעודכן חי
</span>
</div>
<CasesTable
cases={permits}
loading={isPending}
error={error}
emptyText="אין תיקי רישוי פעילים"
searchPlaceholder="חיפוש בעררי רישוי…"
/>
</CardContent>
</Card>
<Card className="bg-surface border-rule shadow-sm">
<CardContent className="px-6 py-5">
<div className="flex items-center justify-between gap-3 mb-4 flex-wrap">
<div className="flex items-baseline gap-3">
<h2 className="text-navy text-xl mb-0">היטל השבחה ופיצויים</h2>
<span className="text-[0.72rem] uppercase tracking-[0.08em] text-ink-muted">
עררים 8xxx · 9xxx
</span>
</div>
<span className="text-[0.72rem] uppercase tracking-[0.08em] text-ink-muted">
מעודכן חי
</span>
</div>
<CasesTable
cases={levies}
loading={isPending}
error={error}
emptyText="אין תיקי היטל השבחה או פיצויים פעילים"
searchPlaceholder="חיפוש בעררי השבחה ופיצויים…"
/>
</CardContent>
</Card>
</div>
<aside className="space-y-6 lg:sticky lg:top-6 lg:self-start">
{/* מה ממתין להכרעתך — gold gate card with dot+label+count rows (mockup 04 .gatecard) */}
<Card className="bg-gold-wash border-gold/50 shadow-sm">
<CardContent className="px-6 py-5">
<h2 className="text-navy text-lg mb-3">מה ממתין להכרעתך</h2>
{approvals && approvals.categories.length > 0 ? (
<ul className="mb-1">
{approvals.categories.map((c) => (
<li
key={c.key}
className="flex items-center gap-2.5 py-2.5 text-[0.88rem] text-ink-soft border-b border-rule-soft last:border-b-0"
>
<span
className={`h-2.5 w-2.5 shrink-0 rounded-full ${SEVERITY_DOT[c.severity]}`}
aria-hidden
/>
<span className="grow min-w-0">{c.label}</span>
<span className="font-bold text-navy tabular-nums">{c.count}</span>
</li>
))}
</ul>
) : (
<p className="text-[0.85rem] text-ink-muted mb-2">
אין פריטים הממתינים להכרעתך.
</p>
)}
<Link
href="/approvals"
className="inline-block mt-3 text-[0.85rem] font-semibold text-gold-deep hover:text-navy"
>
למרכז האישורים
</Link>
</CardContent>
</Card>
<Card className="bg-surface border-rule shadow-sm">
<CardContent className="px-6 py-5">
<h2 className="text-navy text-lg mb-4">פיזור סטטוסים</h2>
<StatusDonut cases={data} />
</CardContent>
</Card>
<Card className="bg-surface border-rule shadow-sm">
<CardContent className="px-6 py-5">
<h2 className="text-navy text-lg mb-4">פיזור לפי תחום</h2>
<AppealTypeBars cases={data} />
</CardContent>
</Card>
</aside>
</div>
</section>
</AppShell>
);
}