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>
181 lines
8.0 KiB
TypeScript
181 lines
8.0 KiB
TypeScript
"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>
|
||
);
|
||
}
|