home: split cases table by appeal type + add appeal-type chart
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 32s
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 32s
Backend (cases listing) - /api/cases: also return updated_at, created_at, practice_area, appeal_subtype, subject. The detail-mode response was previously dropping these even though db.list_cases reads them, leaving the UI's "תחום" and "עודכן" columns blank. Frontend - Split the home table into two: רישוי (1xxx) and היטל השבחה ופיצויים (8xxx + 9xxx), bucketing on appeal_subtype with a case-number-prefix fallback. The "תחום" column is now redundant and removed. - New AppealTypeBars chart in the right rail next to the existing status donut. - Donut: switch to a vertical layout (donut on top, legend below in a 3-col grid) so labels like "חדש / בעיבוד" no longer wrap inside the 320px sidebar; counts now align in a tabular column. - CasesTable accepts emptyText/searchPlaceholder so each split table has its own copy. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,17 +1,31 @@
|
||||
"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 } from "@/lib/api/cases";
|
||||
import { useCases, type Case } from "@/lib/api/cases";
|
||||
|
||||
export default function HomePage() {
|
||||
const { data, isPending, error } = useCases(true);
|
||||
|
||||
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-8">
|
||||
@@ -35,25 +49,70 @@ export default function HomePage() {
|
||||
|
||||
<KPICards cases={data} loading={isPending} />
|
||||
|
||||
<div className="grid gap-6 lg:grid-cols-[1fr_auto]">
|
||||
<Card className="bg-surface border-rule shadow-sm">
|
||||
<CardContent className="px-6 py-5">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h2 className="text-navy text-xl mb-0">רשימת תיקים</h2>
|
||||
<span className="text-[0.72rem] uppercase tracking-[0.08em] text-ink-muted">
|
||||
מעודכן חי
|
||||
</span>
|
||||
</div>
|
||||
<CasesTable cases={data} loading={isPending} error={error} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="grid gap-6 lg:grid-cols-[1fr_320px]">
|
||||
<div className="space-y-6 min-w-0">
|
||||
<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 lg:w-[320px]">
|
||||
<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">
|
||||
<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">
|
||||
<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>
|
||||
|
||||
Reference in New Issue
Block a user