feat(ui): IA redesign → production · יישום נאמן של 16 הדפים הנותרים למוקאפים
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 6s

תיקון הגישה: יישום מלא ונאמן של עיצוב-המוקאפים המאושרים (Claude Design) על כל
הדפים — שינוי-הרכב אמיתי פר-מוקאפ, לא ליטוש-טוקנים. כל hook/query/mutation/טאב/
טופס/נתון נשמר (אומת: tsc נקי + בדיקת-נוכחות hooks קריטיים; 0 פונקציונליות נמחקה).

דפים (← מוקאפ):
- בית — לוח: KPI + "תיקים לפי סטטוס" (bars) + כרטיס-אישורים + CTA כפול.
- ארכיון — filter-bar שטוח + טבלה נקייה + צ'יפי-סוג/תוצאה.
- הערות יו״ר — פריסה דו-טורית + טופס-הוספה חי + כרטיסי-הערה.
- ספריית-פסיקה — tabs קו-תחתון + כרטיסי-תוצאה halacha/קטע + AuthorityBadge.
- דף-תקדים — באנר-meta parchment + דו-טורי + provenance pills.
- פסיקה-חסרה — pill פתוחים + צ'יפי-סטטוס + CTA העלאה.
- יומונים — אזור-העלאה מקווקו + כרטיסי-digest + "ממתין" כתווית פסיבית.
- גרף — פאנל-צד שכבות/אנליטיקה + canvas parchment.
- אימון-סגנון — פורטרט: banner + KPI + אנטומיה + ביטויי-חתימה.
- מתודולוגיה — עורך-צ'קליסט + "חל על:" + canon chip.
- מיומנויות/סקריפטים — טבלאות אמיתיות + צ'יפי-סטטוס.
- הגדרות — sidenav דו-טורי + env-rows עם "ממתין ל-redeploy".
- דף-תיק — באנר-תיק parchment + tabs + timeline + "פתח עורך החלטה".
- תפעול — SectionHeaders + טבלת-שירותים + כרטיסי-שער gold-wash.
- compose — באנר-תיק + SOT pill + פריסה דו-טורית + "השלמה והעברה".

תיקונים שלי אחרי הסוכנים: documents-panel (הוצאת רכיב Shell מ-render — React
Compiler), scripts useMemo deps. /approvals כבר נבנה מחדש נאמנה (commit קודם).

בדיקות: npx tsc --noEmit ✓ · eslint ✓ (לבד מ-learning-panel:109 קיים-מראש).
שימור-פונקציונליות אומת. CI Docker build = שער סופי לפני deploy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-11 23:00:25 +00:00
parent c53ef9a7c4
commit f3b075d282
32 changed files with 2925 additions and 1799 deletions

View File

@@ -88,8 +88,11 @@ export function GraphFilterPanel({
facets?: GraphFacets;
}) {
return (
<Card className="bg-surface border-rule shadow-sm w-72 shrink-0 overflow-y-auto">
<Card className="bg-surface border-rule shadow-sm w-[300px] shrink-0 max-h-full overflow-y-auto">
<CardContent className="space-y-5 p-4">
<div className="text-[0.8rem] font-semibold text-navy border-b border-rule-soft pb-2">
פילטרים וסינון
</div>
<div className="space-y-1.5">
<Label htmlFor="graph-search" className="text-xs text-ink-muted">
חיפוש פסיקה
@@ -250,32 +253,50 @@ export function GraphFilterPanel({
<Separator />
<div className="space-y-3">
<Label className="text-xs text-ink-muted">סוגי נקודות</Label>
<div className="text-[0.8rem] font-semibold text-navy border-b border-rule-soft pb-2">
שכבות הגרף
</div>
<ToggleRow
label="נקודות-נושא"
swatch="#a97d3a"
checked={controls.showTopics}
onCheckedChange={(v) => onChange({ showTopics: v })}
/>
<ToggleRow
label="נקודות-תחום"
swatch="#4a7c59"
checked={controls.showPracticeAreas}
onCheckedChange={(v) => onChange({ showPracticeAreas: v })}
/>
<ToggleRow
label="חוסרי מחקר (פסיקה חסרה)"
swatch="#a54242"
checked={controls.showGaps}
onCheckedChange={(v) => onChange({ showGaps: v })}
/>
<ToggleRow
label="יומונים (כל יום)"
swatch="#b8894a"
checked={controls.showDigests}
onCheckedChange={(v) => onChange({ showDigests: v })}
/>
<ToggleRow
label="הלכות"
checked={controls.showHalachot}
onCheckedChange={(v) => onChange({ showHalachot: v })}
/>
</div>
{/* Stage-2 gate — halacha layer is dense, gated by default (mockup 11) */}
<div className="rounded-lg border border-gold bg-gold-wash p-3.5 space-y-2">
<div className="flex items-center gap-2">
<h4 className="text-sm font-semibold text-navy m-0">שלב ב׳ שכבת הלכות</h4>
<Switch
className="ms-auto"
checked={controls.showHalachot}
onCheckedChange={(v) => onChange({ showHalachot: v })}
aria-label="הצגת שכבת ההלכות"
/>
</div>
<p className="text-[0.72rem] text-ink-muted leading-relaxed m-0">
הפעלת שכבת ההלכות (1,454 צמתים). מגודרת כברירת-מחדל בשל הצפיפות
הדלקה מציגה את הקשרים הלכהפסיקה.
</p>
</div>
</CardContent>
</Card>
@@ -350,18 +371,28 @@ function ToggleRow({
checked,
onCheckedChange,
disabled,
swatch,
}: {
label: string;
checked: boolean;
onCheckedChange: (v: boolean) => void;
disabled?: boolean;
swatch?: string;
}) {
return (
<div className="flex items-center justify-between">
<span className={`text-sm ${disabled ? "text-ink-muted/50" : "text-ink"}`}>
<div className={`flex items-center gap-2.5 ${disabled ? "opacity-55" : ""}`}>
{swatch ? (
<span
className="inline-block size-2.5 rounded-full shrink-0 ring-1 ring-black/10"
style={{ backgroundColor: swatch }}
aria-hidden
/>
) : null}
<span className={`text-sm ${disabled ? "text-ink-muted/50" : "text-ink-soft"}`}>
{label}
</span>
<Switch
className="ms-auto"
checked={checked}
onCheckedChange={onCheckedChange}
disabled={disabled}

View File

@@ -226,7 +226,7 @@ export function GraphView() {
return (
<div className="space-y-3">
<div className="flex items-center justify-between gap-3 text-xs text-ink-muted">
<span>
<span className="inline-flex items-center gap-2 rounded-full border border-rule bg-surface px-3 py-1 tabular-nums">
{data ? `${data.nodes.length} נקודות · ${data.edges.length} קשרים` : "—"}
</span>
<div className="flex items-center gap-3">
@@ -248,12 +248,12 @@ export function GraphView() {
</div>
</div>
<div className="flex gap-4 h-[calc(100vh-320px)] min-h-[560px]">
<div className="flex gap-5 h-[calc(100vh-320px)] min-h-[560px] items-start">
<GraphFilterPanel controls={controls} onChange={onChange} facets={facets} />
<div
ref={canvasAreaRef}
className="relative flex-1 rounded-lg border border-rule bg-surface overflow-hidden"
className="relative flex-1 h-full rounded-lg border border-rule bg-gradient-to-b from-[#f3ecda] to-[#efe6cf] shadow-sm overflow-hidden"
>
{error ? (
<div className="grid h-full place-items-center p-6 text-center">
@@ -311,6 +311,12 @@ export function GraphView() {
)}
<Legend colorBy={controls.colorBy} />
{data ? (
<div className="absolute bottom-3 start-3 rounded-full bg-surface/70 backdrop-blur px-2.5 py-1 text-[0.72rem] text-ink-muted tabular-nums">
{data.nodes.length} צמתים מוצגים
</div>
) : null}
</div>
{selectedNode ? (
@@ -340,9 +346,16 @@ function RankingPanel({
.sort((a, b) => (b.betweenness ?? 0) - (a.betweenness ?? 0))
.slice(0, 12);
const communities = new Set(
nodes.map((n) => n.community).filter((c) => c != null),
).size;
return (
<Card className="bg-surface border-rule shadow-sm w-72 shrink-0 overflow-y-auto">
<CardContent className="p-4">
<Card className="bg-surface border-rule shadow-sm w-[300px] shrink-0 max-h-full overflow-y-auto">
<CardContent className="p-4 space-y-4">
<div className="text-[0.8rem] font-semibold text-navy border-b border-rule-soft pb-2">
אנליטיקה
</div>
<Tabs defaultValue="pagerank">
<TabsList className="w-full">
<TabsTrigger value="pagerank" className="flex-1">
@@ -359,6 +372,12 @@ function RankingPanel({
<RankList items={byBetweenness} metric="betweenness" onPick={onPick} />
</TabsContent>
</Tabs>
{communities > 0 ? (
<div className="flex items-center gap-2 border-t border-rule-soft pt-3 text-sm text-ink-soft">
אשכולות:
<b className="text-navy text-lg tabular-nums">{communities}</b>
</div>
) : null}
</CardContent>
</Card>
);
@@ -377,18 +396,19 @@ function RankList({
return <p className="text-ink-muted text-xs mt-3">אין נתונים.</p>;
}
return (
<ol className="mt-2 space-y-1">
<ol className="mt-2">
{items.map((n, i) => (
<li key={n.id}>
<li key={n.id} className="border-b border-rule-soft last:border-b-0">
<button
type="button"
onClick={() => onPick(n)}
className="flex w-full items-baseline justify-between gap-2 rounded px-2 py-1 text-start text-sm hover:bg-gold-wash"
className="flex w-full items-baseline gap-2 px-1 py-1.5 text-start text-sm hover:bg-gold-wash rounded"
>
<span className="truncate">
<span className="text-ink-muted text-xs">{i + 1}.</span> {n.label}
<span className="w-4 shrink-0 text-ink-muted text-xs tabular-nums">
{i + 1}
</span>
<span className="text-ink-muted text-xs tabular-nums shrink-0">
<span className="truncate text-ink-soft">{n.label}</span>
<span className="ms-auto text-gold-deep font-semibold text-xs tabular-nums shrink-0">
{((n[metric] ?? 0) * 100).toFixed(0)}
</span>
</button>