feat(ui): IA redesign → production · יישום נאמן של 16 הדפים הנותרים למוקאפים
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 6s
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:
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user