Merge pull request 'feat(ui): IA redesign → production · צרור-1 (אישורים + בית) + שער-Claude-Design' (#211) from worktree-ia-redesign-prod into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 2m3s
G12 Leak-Guard / leak-guard (push) Successful in 9s

This commit was merged in pull request #211.
This commit is contained in:
2026-06-11 22:11:51 +00:00
3 changed files with 90 additions and 12 deletions

View File

@@ -6,6 +6,26 @@ This version has breaking changes — APIs, conventions, and file structure may
---
## ⚠️ שער-עיצוב חובה — Claude Design קודם (הנחיית חיים, 2026-06-11)
**כל יצירת עמוד/דף חדש, וכל שינוי-UI בעמוד/דף קיים — מכל סיבה — חייב לעבור קודם דרך Claude Design לאישור, ורק לאחר אישור מטמיעים בקוד.** אין לשנות/ליצור עמוד-UI ישירות בלי מעבר דרך השער.
**הפרויקט הקנוני (כל הדפים נמצאים שם):**
- שם: **`עוזר משפטי — IA Redesign (X17)`** ב-[claude.ai/design](https://claude.ai/design).
- מזהה-פרויקט: `7a85b323-d880-4b6d-bac5-d4aa396fe93c`.
- גישה: כלי `DesignSync` (claude.ai/design). מכיל את כל 17 הדפים כ-preview-cards תחת קבוצה `IA Redesign (X17)`, בנויים מטוקני-העיצוב האמיתיים (`globals.css`: navy `#0f172a`, gold `#a97d3a`, cream `#f5f1e8`, parchment, rule…), Heebo, RTL.
**הפרוטוקול לכל בקשת-עמוד/שינוי-UI:**
1. **בקשת-עיצוב ל-Claude Design** — לבנות/לעדכן את ה-preview של העמוד בפרויקט הזה (HTML self-contained, `<!-- @dsCard group="IA Redesign (X17)" -->` כשורה ראשונה, טוקנים+Heebo+RTL זהים), `write_files` + `register_assets` דרך `DesignSync`.
2. **להציג לחיים לאישור** — ולהמתין לאישור מפורש.
3. **רק אחרי אישור** — להטמיע בקוד (Next 16 + Tailwind v4 + shadcn), לשמר hooks/לוגיקה, tsc/lint, worktree + PR.
חריג יחיד: תיקון-באג שאינו משנה את ה-UI הוויזואלי (לוגיקה/נתון/hook בלבד) — לא דורש מעבר דרך השער. בכל ספק — דרך השער.
> רקע: יוזמת ה-IA (X17, #127/#130132) → מוקאפי-Claude-Design ל-17 הדפים (מאושרים) → תרגום לפרודקשן. ראה memory `feedback_claude_design_gate` ו-`project_ia_redesign_waves`.
---
## Stack
| Layer | Technology | Version |

View File

@@ -24,6 +24,15 @@ const SEVERITY_BADGE: Record<ApprovalSeverity, string> = {
ok: "bg-emerald-50 text-emerald-800 border-emerald-300/60",
};
// Severity expressed as a colored dot next to the title (matches the approved
// IA-redesign mockup): high=danger, medium=warn, low=info, ok=success.
const SEVERITY_DOT: Record<ApprovalSeverity, string> = {
high: "bg-danger",
medium: "bg-warn",
low: "bg-info",
ok: "bg-success",
};
function formatDate(iso?: string | null): string {
if (!iso) return "";
try {
@@ -42,10 +51,14 @@ function ApprovalCard({ cat }: { cat: ApprovalCategory }) {
return (
<Card className="bg-surface border-rule shadow-sm flex flex-col">
<CardContent className="px-6 py-5 flex flex-col gap-3 grow">
<div className="flex items-start justify-between gap-3">
<h2 className="text-navy text-lg mb-0 leading-snug">{cat.label}</h2>
<div className="flex items-start gap-3">
<span
className={`inline-flex items-center justify-center min-w-[2.25rem] h-7 px-2 rounded-full border text-sm font-semibold ${SEVERITY_BADGE[cat.severity]}`}
className={`mt-2 h-2.5 w-2.5 shrink-0 rounded-full ${SEVERITY_DOT[cat.severity]}`}
aria-hidden
/>
<h2 className="text-navy text-lg mb-0 leading-snug grow">{cat.label}</h2>
<span
className={`inline-flex items-center justify-center min-w-[2.25rem] h-7 px-2 rounded-full border text-sm font-semibold tabular-nums ${SEVERITY_BADGE[cat.severity]}`}
aria-label={`${cat.count} פריטים ממתינים`}
>
{cat.count}
@@ -98,9 +111,19 @@ function ApprovalCard({ cat }: { cat: ApprovalCategory }) {
<div className="mt-auto pt-2">
{cat.href ? (
<Button asChild variant="outline" size="sm" className="border-rule">
<Link href={cat.href}>לטיפול </Link>
</Button>
cleared ? (
<Button asChild variant="outline" size="sm" className="border-rule text-ink-muted">
<Link href={cat.href}>צפייה </Link>
</Button>
) : (
<Button
asChild
size="sm"
className="bg-gold text-white hover:bg-gold-deep border-transparent"
>
<Link href={cat.href}>לטיפול </Link>
</Button>
)
) : (
<span className="text-[0.75rem] text-ink-muted">סקירה ידנית (ראה דוח FU-5)</span>
)}
@@ -133,13 +156,11 @@ export default function ApprovalsPage() {
</p>
</div>
{typeof data?.total_pending === "number" ? (
<div className="text-end">
<div className="text-3xl font-semibold text-navy leading-none">
<div className="inline-flex items-baseline gap-2 rounded-lg border border-rule bg-gold-wash px-4 py-2.5">
<span className="text-2xl font-semibold text-gold-deep leading-none tabular-nums">
{data.total_pending}
</div>
<div className="text-[0.72rem] uppercase tracking-[0.08em] text-ink-muted mt-1">
פריטים ממתינים
</div>
</span>
<span className="text-[0.85rem] text-ink-soft">פריטים ממתינים</span>
</div>
) : null}
</div>

View File

@@ -10,9 +10,13 @@ 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 } from "@/lib/api/chair";
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[] = [];
@@ -99,6 +103,39 @@ export default function HomePage() {
</div>
<aside className="space-y-6 lg:sticky lg:top-6 lg:self-start">
{approvals && approvals.total_pending > 0 ? (
<Card className="bg-gold-wash border-gold/40 shadow-sm">
<CardContent className="px-6 py-5">
<div className="flex items-center justify-between gap-3 mb-3">
<h2 className="text-navy text-lg mb-0">מה ממתין להכרעתך</h2>
<span className="text-2xl font-semibold text-gold-deep leading-none tabular-nums">
{approvals.total_pending}
</span>
</div>
<ul className="space-y-1.5 mb-4">
{approvals.categories
.filter((c) => c.count > 0)
.map((c) => (
<li
key={c.key}
className="flex items-center justify-between gap-2 text-[0.85rem] text-ink-soft"
>
<span>{c.label}</span>
<span className="text-navy font-semibold tabular-nums">{c.count}</span>
</li>
))}
</ul>
<Button
asChild
size="sm"
className="bg-gold text-white hover:bg-gold-deep border-transparent w-full"
>
<Link href="/approvals">למרכז האישורים </Link>
</Button>
</CardContent>
</Card>
) : null}
<Card className="bg-surface border-rule shadow-sm">
<CardContent className="px-6 py-5">
<h2 className="text-navy text-lg mb-4">פיזור סטטוסים</h2>