Phase 3a: shadcn init + Home/Case List view
Initialize shadcn/ui (radix-nova preset) and wire its semantic tokens to the editorial navy/cream/gold palette so primitives inherit the judicial voice without per-component overrides. Replace the Phase 2 live-probe with a real dashboard: KPI tiles, conic-gradient status donut (ported from the vanilla renderHero), and a TanStack Table cases list with search + sort. Add useCase(n) hook with 5s staleTime/refetchInterval to replace the old manual polling loop when Case Detail ships next. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,18 @@
|
||||
@import "tailwindcss";
|
||||
@import "tw-animate-css";
|
||||
@import "shadcn/tailwind.css";
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════
|
||||
* Ezer Mishpati — Design Tokens (ported from legal-ai/web/static/design-system.css)
|
||||
* Ezer Mishpati — Design Tokens
|
||||
* Ported from legal-ai/web/static/design-system.css.
|
||||
* Editorial/judicial aesthetic for a Hebrew RTL judicial tool.
|
||||
* Palette: Navy + Cream + Gold.
|
||||
* Typography: Heebo (loaded via next/font/google in layout.tsx).
|
||||
* Palette: Navy + Cream + Gold. Typography: Heebo.
|
||||
*
|
||||
* shadcn semantic tokens (background, foreground, primary, etc.)
|
||||
* are mapped onto this palette in the @theme inline block + :root
|
||||
* further down, so shadcn primitives inherit the editorial voice.
|
||||
* ══════════════════════════════════════════════════════════════ */
|
||||
|
||||
@theme {
|
||||
@@ -44,18 +52,12 @@
|
||||
--color-info: #4e6a8c;
|
||||
--color-info-bg: #e6ecf3;
|
||||
|
||||
/* ── Typography — Heebo (loaded via next/font in layout.tsx) ─ */
|
||||
/* ── Typography ─────────────────────────────────────── */
|
||||
--font-sans: var(--font-heebo), -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--font-display: var(--font-heebo), -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--font-body: var(--font-heebo), -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--font-mono: ui-monospace, "Cascadia Code", "SF Mono", Menlo, monospace;
|
||||
|
||||
/* ── Radii ──────────────────────────────────────────── */
|
||||
--radius-sm: 4px;
|
||||
--radius: 6px;
|
||||
--radius-md: 8px;
|
||||
--radius-lg: 12px;
|
||||
--radius-xl: 16px;
|
||||
|
||||
/* ── Shadows — soft, editorial ──────────────────────── */
|
||||
--shadow-xs: 0 1px 2px rgba(15, 23, 42, 0.05);
|
||||
--shadow-sm: 0 1px 3px rgba(15, 23, 42, 0.06), 0 1px 2px rgba(15, 23, 42, 0.04);
|
||||
@@ -68,9 +70,93 @@
|
||||
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
|
||||
}
|
||||
|
||||
/* ── Dark theme (class-based, toggled on <html> or <body>) ── */
|
||||
:root.dark,
|
||||
body.dark {
|
||||
/* shadcn/ui semantic tokens — wired to CSS vars defined in :root / .dark */
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--color-card: var(--card);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-border: var(--border);
|
||||
--color-input: var(--input);
|
||||
--color-ring: var(--ring);
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-5: var(--chart-5);
|
||||
--color-sidebar: var(--sidebar);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
--font-heading: var(--font-display);
|
||||
--radius-sm: 4px;
|
||||
--radius-md: 8px;
|
||||
--radius-lg: 12px;
|
||||
--radius-xl: 16px;
|
||||
}
|
||||
|
||||
/*
|
||||
* shadcn token values — mapped onto the editorial palette.
|
||||
* background/foreground/primary all reference the navy-cream-gold system
|
||||
* so shadcn primitives (Button, Card, Badge…) fit the judicial tone
|
||||
* without per-component overrides.
|
||||
*/
|
||||
:root {
|
||||
--radius: 0.5rem;
|
||||
|
||||
--background: var(--color-bg); /* cream */
|
||||
--foreground: var(--color-ink); /* near-black */
|
||||
--card: var(--color-surface); /* white */
|
||||
--card-foreground: var(--color-ink);
|
||||
--popover: var(--color-surface);
|
||||
--popover-foreground: var(--color-ink);
|
||||
--primary: var(--color-navy); /* navy buttons */
|
||||
--primary-foreground: var(--color-parchment);
|
||||
--secondary: var(--color-cream-deep);
|
||||
--secondary-foreground: var(--color-navy);
|
||||
--muted: var(--color-rule-soft);
|
||||
--muted-foreground: var(--color-ink-muted);
|
||||
--accent: var(--color-gold-wash); /* subtle gold backgrounds */
|
||||
--accent-foreground: var(--color-gold-deep);
|
||||
--destructive: var(--color-danger);
|
||||
--border: var(--color-rule);
|
||||
--input: var(--color-rule);
|
||||
--ring: var(--color-gold); /* gold focus ring */
|
||||
|
||||
--chart-1: var(--color-navy);
|
||||
--chart-2: var(--color-gold);
|
||||
--chart-3: var(--color-info);
|
||||
--chart-4: var(--color-success);
|
||||
--chart-5: var(--color-warn);
|
||||
|
||||
--sidebar: var(--color-parchment);
|
||||
--sidebar-foreground: var(--color-ink);
|
||||
--sidebar-primary: var(--color-navy);
|
||||
--sidebar-primary-foreground: var(--color-parchment);
|
||||
--sidebar-accent: var(--color-gold-wash);
|
||||
--sidebar-accent-foreground: var(--color-gold-deep);
|
||||
--sidebar-border: var(--color-rule);
|
||||
--sidebar-ring: var(--color-gold);
|
||||
}
|
||||
|
||||
/* Dark theme — class-based, toggled on <html> or <body>.
|
||||
Inverts navy ↔ parchment and keeps gold as accent. */
|
||||
.dark {
|
||||
--color-navy: #f5f1e8;
|
||||
--color-navy-soft: #e8e0c8;
|
||||
--color-navy-dim: #c7bc9a;
|
||||
@@ -106,73 +192,57 @@ body.dark {
|
||||
--color-info-bg: rgba(109, 139, 171, 0.12);
|
||||
}
|
||||
|
||||
/* ── Base overrides ──────────────────────────────────── */
|
||||
/* ══════════════════════════════════════════════════════════════
|
||||
* Base layer — editorial typography + Hebrew prose defaults
|
||||
* ══════════════════════════════════════════════════════════════ */
|
||||
|
||||
html {
|
||||
font-size: 16px;
|
||||
}
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-body);
|
||||
font-weight: 400;
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.65;
|
||||
color: var(--color-ink);
|
||||
background: var(--color-bg);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-feature-settings: "kern", "liga", "clig", "calt";
|
||||
}
|
||||
html {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* Display typography — headings use the same Heebo family but bolder */
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
line-height: 1.25;
|
||||
color: var(--color-navy);
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
font-family: var(--font-body);
|
||||
font-weight: 400;
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.65;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-feature-settings: "kern", "liga", "clig", "calt";
|
||||
}
|
||||
|
||||
h1 { font-size: 2.3rem; font-weight: 900; }
|
||||
h2 { font-size: 1.8rem; }
|
||||
h3 { font-size: 1.45rem; }
|
||||
h4 { font-size: 1.2rem; }
|
||||
h5 { font-size: 1.05rem; }
|
||||
h6 { font-size: 0.95rem; }
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
line-height: 1.25;
|
||||
color: var(--color-navy);
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
/* Prose paragraphs — justify both sides for Hebrew legal text */
|
||||
p.prose,
|
||||
.prose p {
|
||||
text-align: justify;
|
||||
text-justify: inter-word;
|
||||
hyphens: auto;
|
||||
line-height: 1.65;
|
||||
}
|
||||
h1 { font-size: 2.3rem; font-weight: 900; }
|
||||
h2 { font-size: 1.8rem; }
|
||||
h3 { font-size: 1.45rem; }
|
||||
h4 { font-size: 1.2rem; }
|
||||
h5 { font-size: 1.05rem; }
|
||||
h6 { font-size: 0.95rem; }
|
||||
|
||||
/* Links */
|
||||
a {
|
||||
color: var(--color-gold-deep);
|
||||
text-decoration: none;
|
||||
transition: color 120ms var(--ease-out);
|
||||
}
|
||||
a:hover {
|
||||
color: var(--color-gold);
|
||||
}
|
||||
/* Prose paragraphs — justified for Hebrew legal text */
|
||||
p.prose,
|
||||
.prose p {
|
||||
text-align: justify;
|
||||
text-justify: inter-word;
|
||||
hyphens: auto;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
/* Focus rings — gold, subtle */
|
||||
*:focus-visible {
|
||||
outline: 2px solid var(--color-gold);
|
||||
outline-offset: 2px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* Selection */
|
||||
::selection {
|
||||
background: var(--color-gold-wash);
|
||||
color: var(--color-navy);
|
||||
/* Selection */
|
||||
::selection {
|
||||
background: var(--color-gold-wash);
|
||||
color: var(--color-navy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Heebo } from "next/font/google";
|
||||
import { Heebo, Geist } from "next/font/google";
|
||||
import { Providers } from "@/lib/providers";
|
||||
import "./globals.css";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const geist = Geist({subsets:['latin'],variable:'--font-sans'});
|
||||
|
||||
const heebo = Heebo({
|
||||
variable: "--font-heebo",
|
||||
@@ -21,7 +24,7 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="he" dir="rtl" className={`${heebo.variable} h-full antialiased`}>
|
||||
<html lang="he" dir="rtl" className={cn("h-full", "antialiased", heebo.variable, "font-sans", geist.variable)}>
|
||||
<body className="min-h-full flex flex-col">
|
||||
<Providers>{children}</Providers>
|
||||
</body>
|
||||
|
||||
@@ -1,23 +1,54 @@
|
||||
"use client";
|
||||
|
||||
import { AppShell } from "@/components/app-shell";
|
||||
import { CasesLiveProbe } from "@/components/cases/cases-live-probe";
|
||||
import { KPICards } from "@/components/cases/kpi-cards";
|
||||
import { StatusDonut } from "@/components/cases/status-donut";
|
||||
import { CasesTable } from "@/components/cases/cases-table";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { useCases } from "@/lib/api/cases";
|
||||
|
||||
export default function HomePage() {
|
||||
const { data, isPending, error } = useCases(true);
|
||||
|
||||
return (
|
||||
<AppShell>
|
||||
<section className="space-y-6">
|
||||
<header className="space-y-2">
|
||||
<h1 className="text-navy">עוזר משפטי</h1>
|
||||
<p className="text-ink-muted text-base max-w-2xl">
|
||||
מערכת סיוע בניסוח החלטות לוועדת ערר לתכנון ובנייה — ניהול תיקים, חיפוש
|
||||
סמנטי, וכלי כתיבה לפי ארכיטקטורת 12 הבלוקים.
|
||||
</p>
|
||||
<section className="space-y-8">
|
||||
<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">
|
||||
לוח בקרה לניהול תיקי ערר, ניתוח סגנון, וכתיבת החלטות לפי ארכיטקטורת
|
||||
12 הבלוקים.
|
||||
</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<hr className="border-0 h-[2px] bg-gradient-to-l from-transparent via-gold to-transparent" />
|
||||
<div className="h-[2px] bg-gradient-to-l from-transparent via-gold to-transparent" />
|
||||
|
||||
<div className="rounded-lg bg-surface shadow-sm p-6 border border-rule">
|
||||
<h2 className="text-navy text-xl mb-3">פאזה 2 — חיבור חי ל-API</h2>
|
||||
<CasesLiveProbe />
|
||||
<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>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
</section>
|
||||
</AppShell>
|
||||
|
||||
Reference in New Issue
Block a user