From f3b075d282b47b9a0a44a32bd9b87acf4591ba8e Mon Sep 17 00:00:00 2001 From: Chaim Date: Thu, 11 Jun 2026 23:00:25 +0000 Subject: [PATCH] =?UTF-8?q?feat(ui):=20IA=20redesign=20=E2=86=92=20product?= =?UTF-8?q?ion=20=C2=B7=20=D7=99=D7=99=D7=A9=D7=95=D7=9D=20=D7=A0=D7=90?= =?UTF-8?q?=D7=9E=D7=9F=20=D7=A9=D7=9C=2016=20=D7=94=D7=93=D7=A4=D7=99?= =?UTF-8?q?=D7=9D=20=D7=94=D7=A0=D7=95=D7=AA=D7=A8=D7=99=D7=9D=20=D7=9C?= =?UTF-8?q?=D7=9E=D7=95=D7=A7=D7=90=D7=A4=D7=99=D7=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit תיקון הגישה: יישום מלא ונאמן של עיצוב-המוקאפים המאושרים (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) --- web-ui/src/app/archive/page.tsx | 271 ++++++---- .../app/cases/[caseNumber]/compose/page.tsx | 491 +++++++++++------- web-ui/src/app/cases/[caseNumber]/page.tsx | 272 +++++----- web-ui/src/app/digests/page.tsx | 82 +-- web-ui/src/app/feedback/page.tsx | 298 ++++++++--- web-ui/src/app/graph/page.tsx | 14 +- web-ui/src/app/methodology/page.tsx | 6 +- web-ui/src/app/missing-precedents/page.tsx | 246 +++++---- web-ui/src/app/operations/page.tsx | 214 +++++--- web-ui/src/app/page.tsx | 145 ++++-- web-ui/src/app/precedents/[id]/page.tsx | 387 ++++++++------ web-ui/src/app/precedents/page.tsx | 166 ++++-- web-ui/src/app/scripts/page.tsx | 268 ++++++++-- .../app/settings/_components/drift-badge.tsx | 17 +- .../app/settings/_components/env-var-row.tsx | 155 +++--- .../settings/_components/environment-tab.tsx | 77 +-- web-ui/src/app/settings/page.tsx | 120 +++-- web-ui/src/app/skills/page.tsx | 206 ++++---- web-ui/src/app/training/page.tsx | 13 +- web-ui/src/components/cases/case-header.tsx | 198 ++++--- .../src/components/cases/documents-panel.tsx | 37 +- .../components/cases/workflow-timeline.tsx | 65 ++- web-ui/src/components/digests/digest-card.tsx | 90 ++-- .../components/digests/digest-list-panel.tsx | 7 +- .../digests/digest-upload-dialog.tsx | 14 +- .../components/graph/graph-filter-panel.tsx | 49 +- web-ui/src/components/graph/graph-view.tsx | 42 +- .../methodology/content-checklists-panel.tsx | 186 ++++--- .../missing-precedents-table.tsx | 103 ++-- .../precedents/library-search-panel.tsx | 170 +++--- .../precedents/link-related-dialog.tsx | 135 ++--- .../training/style-report-panel.tsx | 180 ++++--- 32 files changed, 2925 insertions(+), 1799 deletions(-) diff --git a/web-ui/src/app/archive/page.tsx b/web-ui/src/app/archive/page.tsx index 6aeaac1..06a0e41 100644 --- a/web-ui/src/app/archive/page.tsx +++ b/web-ui/src/app/archive/page.tsx @@ -26,7 +26,8 @@ import { TableRow, } from "@/components/ui/table"; import { useCases, useRestoreCase, type Case } from "@/lib/api/cases"; -import { APPEAL_SUBTYPE_LABELS } from "@/lib/practice-area"; +import { subtypeOf } from "@/components/cases/appeal-type-bars"; +import { APPEAL_SUBTYPE_LABELS, type AppealSubtype } from "@/lib/practice-area"; function formatDate(iso?: string | null) { if (!iso) return "—"; @@ -41,6 +42,20 @@ function formatDate(iso?: string | null) { } } +// type chip styling per mockup 05 (.t-lic / .t-bet / .t-comp) +const TYPE_CHIP: Record = { + building_permit: "bg-info-bg text-info", + betterment_levy: "bg-gold-wash text-gold-deep border border-rule", + compensation_197: "bg-rule-soft text-ink-soft", +}; + +// outcome chip styling per mockup 05 (.r-acc / .r-rej / .r-part) +const OUTCOME_CHIP: Record = { + full_acceptance: { label: "התקבל", cls: "bg-success-bg text-success" }, + partial_acceptance: { label: "חלקי", cls: "bg-warn-bg text-warn" }, + rejection: { label: "נדחה", cls: "bg-danger-bg text-danger" }, +}; + function RestoreButton({ caseNumber }: { caseNumber: string }) { const restore = useRestoreCase(caseNumber); return ( @@ -77,7 +92,7 @@ function RestoreButton({ caseNumber }: { caseNumber: string }) { const columns: ColumnDef[] = [ { accessorKey: "case_number", - header: "מס׳ ערר", + header: "מספר ערר", cell: ({ row }) => ( [] = [ accessorKey: "title", header: "כותרת", cell: ({ row }) => ( -
+
{row.original.title}
), }, { accessorKey: "appeal_subtype", - header: "תחום", + header: "סוג", cell: ({ row }) => { - const s = row.original.appeal_subtype; + const s = subtypeOf(row.original); if (!s || s === "unknown") return ; return ( - {APPEAL_SUBTYPE_LABELS[s]} + + {APPEAL_SUBTYPE_LABELS[s as AppealSubtype]} + + ); + }, + }, + { + accessorKey: "expected_outcome", + header: "תוצאה", + cell: ({ row }) => { + const o = row.original.expected_outcome; + const chip = o ? OUTCOME_CHIP[o] : undefined; + if (!chip) return ; + return ( + + {chip.label} + ); }, }, @@ -112,7 +149,7 @@ const columns: ColumnDef[] = [ accessorKey: "archived_at", header: "תאריך ארכוב", cell: ({ row }) => ( - + {formatDate(row.original.archived_at)} ), @@ -130,6 +167,7 @@ export default function ArchivePage() { { id: "archived_at", desc: true }, ]); const [globalFilter, setGlobalFilter] = useState(""); + const [typeFilter, setTypeFilter] = useState("all"); const rows = useMemo(() => data ?? [], [data]); @@ -152,126 +190,141 @@ export default function ArchivePage() { }, }); + // domain filter applied client-side (subtypeOf collapses בל"מ variants) + const filteredRows = useMemo(() => { + const all = table.getFilteredRowModel().rows; + if (typeFilter === "all") return all; + return all.filter((r) => subtypeOf(r.original) === typeFilter); + }, [table, typeFilter, globalFilter, sorting, rows]); + + const total = rows.length; + const shown = filteredRows.length; + return ( -
+
-
-
-
- ארכיון תיקי ערר -
-

תיקים סגורים

-

- תיקים שסגרו את הטיפול בהם. שחזור מחזיר את התיק לרשימה הראשית - ופותח מחדש את הפרויקט המקביל ב-Paperclip. -

-
-
- - {table.getFilteredRowModel().rows.length} - - תיקים בארכיון +
+
+ ארכיון תיקי ערר
+

ארכיון

+

+ כל ההחלטות שהושלמו — לחיפוש, סינון ועיון. {total} תיקים סגורים. + שחזור מחזיר את התיק לרשימה הראשית ופותח מחדש את הפרויקט המקביל ב-Paperclip. +

- - -
- setGlobalFilter(e.target.value)} - placeholder="חיפוש לפי מס׳ ערר או כותרת…" - className="max-w-sm bg-surface" - dir="rtl" - /> -
+ {/* filter bar — search + domain select + count aligned to end (mockup 05 .filters) */} +
+ setGlobalFilter(e.target.value)} + placeholder="חיפוש לפי מספר ערר, כותרת או צד…" + className="flex-1 min-w-[220px] bg-surface" + dir="rtl" + /> + + + מציג {shown} מתוך {total} + +
-
- - - {table.getHeaderGroups().map((hg) => ( - - {hg.headers.map((header) => ( - - {flexRender( - header.column.columnDef.header, - header.getContext(), - )} - {{ asc: " ▲", desc: " ▼" }[ - header.column.getIsSorted() as string - ] ?? ""} - + {/* clean bordered table — parchment header, gold-wash hover (mockup 05 .card table) */} + + +
+ + {table.getHeaderGroups().map((hg) => ( + + {hg.headers.map((header) => ( + + {flexRender( + header.column.columnDef.header, + header.getContext(), + )} + {{ asc: " ▲", desc: " ▼" }[ + header.column.getIsSorted() as string + ] ?? ""} + + ))} + + ))} + + + {isPending ? ( + Array.from({ length: 3 }).map((_, i) => ( + + {columns.map((_c, j) => ( + + + ))} - ))} - - - {isPending ? ( - Array.from({ length: 3 }).map((_, i) => ( - - {columns.map((_c, j) => ( - - - - ))} - - )) - ) : error ? ( - - - שגיאה בטעינת ארכיון: {error.message} - + )) + ) : error ? ( + + + שגיאה בטעינת ארכיון: {error.message} + + + ) : filteredRows.length === 0 ? ( + + +
+ ❦ +
+ {globalFilter || typeFilter !== "all" + ? "אין תיקים תואמים לחיפוש" + : "אין תיקים בארכיון"} +
+
+ ) : ( + filteredRows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} + + ))} - ) : table.getRowModel().rows.length === 0 ? ( - - -
- ❦ -
- {globalFilter - ? "אין תיקים תואמים לחיפוש" - : "אין תיקים בארכיון"} -
-
- ) : ( - table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext(), - )} - - ))} - - )) - )} -
-
-
+ )) + )} + +
diff --git a/web-ui/src/app/cases/[caseNumber]/compose/page.tsx b/web-ui/src/app/cases/[caseNumber]/compose/page.tsx index acc2d0d..5a389a5 100644 --- a/web-ui/src/app/cases/[caseNumber]/compose/page.tsx +++ b/web-ui/src/app/cases/[caseNumber]/compose/page.tsx @@ -2,6 +2,7 @@ import { use, useRef, useState } from "react"; import Link from "next/link"; +import { FileText } from "lucide-react"; import { AppShell } from "@/components/app-shell"; import { Card, CardContent } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; @@ -9,9 +10,51 @@ import { Skeleton } from "@/components/ui/skeleton"; import { SubsectionCard } from "@/components/compose/subsection-card"; import { PrecedentsSection } from "@/components/compose/precedents-section"; import { Markdown } from "@/components/ui/markdown"; -import { useCase } from "@/lib/api/cases"; +import { useCase, type CaseStatus } from "@/lib/api/cases"; import { useResearchAnalysis } from "@/lib/api/research"; import { useCasePrecedents } from "@/lib/api/precedents"; +import { APPEAL_SUBTYPES } from "@/lib/practice-area"; +import { DOC_TYPE_LABELS, type DocType } from "@/lib/doc-types"; + +// ── Case-status → Hebrew label + tone (mockup 03 status chip) ──────────────── +const STATUS_CHIP: Record = { + new: { label: "חדש", cls: "bg-rule-soft text-ink-muted border-rule" }, + uploading: { label: "בהעלאה", cls: "bg-info-bg text-info border-info/30" }, + processing: { label: "בעיבוד", cls: "bg-info-bg text-info border-info/30" }, + documents_ready: { label: "מסמכים מוכנים", cls: "bg-info-bg text-info border-info/30" }, + analyst_verified: { label: "אומת ע״י אנליסט", cls: "bg-info-bg text-info border-info/30" }, + research_complete: { label: "מחקר הושלם", cls: "bg-info-bg text-info border-info/30" }, + outcome_set: { label: "תוצאה נקבעה", cls: "bg-info-bg text-info border-info/30" }, + brainstorming: { label: "סיעור-מוחות", cls: "bg-info-bg text-info border-info/30" }, + direction_approved: { label: "כיוון אושר", cls: "bg-info-bg text-info border-info/30" }, + analysis_enriched: { label: "ניתוח הועשר", cls: "bg-info-bg text-info border-info/30" }, + ready_for_writing: { label: "מוכן לכתיבה", cls: "bg-gold-wash text-gold-deep border-gold/40" }, + drafting: { label: "בעריכה", cls: "bg-info-bg text-info border-info/30" }, + qa_review: { label: "בדיקת-איכות", cls: "bg-gold-wash text-gold-deep border-gold/40" }, + drafted: { label: "טיוטה", cls: "bg-gold-wash text-gold-deep border-gold/40" }, + exported: { label: "יוצא", cls: "bg-success-bg text-success border-success/40" }, + reviewed: { label: "נסקר", cls: "bg-success-bg text-success border-success/40" }, + final: { label: "סופי", cls: "bg-success-bg text-success border-success/40" }, +}; + +function StatusChip({ status }: { status?: CaseStatus }) { + const c = (status && STATUS_CHIP[status]) || { + label: "בעריכה", + cls: "bg-info-bg text-info border-info/30", + }; + return ( + + {c.label} + + ); +} + +function subtypeLabel(subtype?: string | null): string | null { + if (!subtype) return null; + return APPEAL_SUBTYPES.find((s) => s.value === subtype)?.label ?? null; +} function ProseSection({ title, content }: { title: string; content?: string }) { if (!content?.trim()) return null; @@ -25,7 +68,8 @@ function ProseSection({ title, content }: { title: string; content?: string }) { ); } -function AnalysisActions({ +// ── "השלמה והעברה" rail card — DOCX export, upload, download (all real) ────── +function FinishRail({ caseNumber, hasAnalysis, onUploaded, @@ -55,7 +99,7 @@ function AnalysisActions({ } setUploadMsg({ ok: true, - text: `הקובץ הועלה בהצלחה — ${data.sections.threshold_claims} טענות סף, ${data.sections.issues} סוגיות`, + text: `הקובץ הועלה — ${data.sections.threshold_claims} טענות סף, ${data.sections.issues} סוגיות`, }); onUploaded(); } catch { @@ -67,58 +111,79 @@ function AnalysisActions({ } return ( -
- {uploadMsg && ( - - {uploadMsg.text} - - )} - { - const f = e.target.files?.[0]; - if (f) handleUpload(f); - }} - /> - - {hasAnalysis && ( - + )} + + {hasAnalysis && ( + + )} +
+ + {uploadMsg && ( +

+ {uploadMsg.text} +

+ )} + + {/* mockup 03: stage indicators — informational pointers, not actions */} +
+
+ הרץ למידת-קול — ממתין להעלאת הסופי +
+
+ הרץ אימות-הלכות — ממתין להעלאת הסופי +
+
+ + - )} - {hasAnalysis && ( - - )} - -
+ + ); } @@ -145,6 +210,18 @@ export default function ComposePage({ } } const practiceArea = caseQuery.data?.practice_area ?? null; + const subtype = subtypeLabel(caseQuery.data?.appeal_subtype); + const parties = (() => { + const c = caseQuery.data; + if (!c) return null; + const app = c.appellants?.length ? c.appellants.join(", ") : null; + const resp = c.respondents?.length ? c.respondents.join(", ") : null; + const out: string[] = []; + if (app) out.push(`עוררים: ${app}`); + if (resp) out.push(`משיבה: ${resp}`); + return out.length ? out.join(" · ") : c.title || null; + })(); + const documents = caseQuery.data?.documents ?? []; const isNotFound = analysis.error instanceof Error && @@ -152,121 +229,84 @@ export default function ComposePage({ return ( -
- {/* Header strip */} -
-
- -

ניתוח משפטי וכתיבת עמדה

- {caseQuery.data?.title && ( -

- {caseQuery.data.title} -

- )} -
- analysis.refetch()} /> + {/* ── Case header band (mockup 03) — parchment strip, full-bleed to the + AppShell
edges (which pads px-10 py-10) ── */} +
+ +
+

ערר {caseNumber}

+ + {subtype && ( + + {subtype} + + )} + {/* INV-G10: source-of-truth pill — the blocks are the canonical text */} + + מקור-אמת: בלוקים +
+ {parties &&

{parties}

} +
-
- - {analysis.isPending ? ( - - - - - - - - - ) : isNotFound ? ( - - -
-

- טרם בוצע ניתוח משפטי לתיק זה -

-

- לאחר שקובץ analysis-and-research.md ייווצר, תוכלי - לערוך כאן את עמדת הוועדה לכל טענת סף וסוגיה. -

-
-
- ) : analysis.error ? ( - - -

{analysis.error.message}

-
-
- ) : analysis.data ? ( -
- {/* Case-level general precedents */} - - -

פסיקה כללית לדיון

-

- ציטוטים התומכים בעמדה באופן רוחבי — ישולבו בפתיחת בלוק י (דיון). -

- -
-
- - {/* Threshold claims */} - {analysis.data.threshold_claims && - analysis.data.threshold_claims.length > 0 && ( -
-
-

טענות סף

- - {analysis.data.threshold_claims.length} - -
-
- {analysis.data.threshold_claims.map((tc) => ( - - ))} -
-
- )} - - {/* Issues */} - {analysis.data.issues && analysis.data.issues.length > 0 && ( + {analysis.isPending ? ( + + + + + + + + + ) : isNotFound ? ( + + +
+

+ טרם בוצע ניתוח משפטי לתיק זה +

+

+ לאחר שקובץ analysis-and-research.md ייווצר, תוכלי + לערוך כאן את עמדת הוועדה לכל טענת סף וסוגיה. +

+
+
+ ) : analysis.error ? ( + + +

{analysis.error.message}

+
+
+ ) : analysis.data ? ( + /* ── Two-column workspace: main editor list + 320px side rail ──────── */ +
+ {/* MAIN — the block/subsection editor list */} +
+ {/* Threshold claims */} + {analysis.data.threshold_claims && + analysis.data.threshold_claims.length > 0 && (
-

סוגיות להכרעה

+

טענות סף

- {analysis.data.issues.length} + {analysis.data.threshold_claims.length}
-
- {analysis.data.issues.map((iss) => ( +
+ {analysis.data.threshold_claims.map((tc) => ( ))} @@ -274,8 +314,31 @@ export default function ComposePage({
)} - {(!analysis.data.threshold_claims?.length && - !analysis.data.issues?.length) && ( + {/* Issues */} + {analysis.data.issues && analysis.data.issues.length > 0 && ( +
+
+

סוגיות להכרעה

+ + {analysis.data.issues.length} + +
+
+ {analysis.data.issues.map((iss) => ( + + ))} +
+
+ )} + + {!analysis.data.threshold_claims?.length && + !analysis.data.issues?.length && ( לא נמצאו טענות סף או סוגיות בניתוח זה. @@ -283,42 +346,82 @@ export default function ComposePage({ )} - {/* Background prose — moved below the issues so it reads as - supporting context after the chair has seen the main - decision points, not as a wall of text beside them. */} - - -

רקע לניתוח

- - - - + {/* Background prose — supporting context after the decision points */} + + +

רקע לניתוח

+ + + + +
+
+ + {analysis.data.conclusions?.trim() && ( + + +

מסקנות

+
- - {analysis.data.conclusions?.trim() && ( - - -

מסקנות

- -
-
- )} + )}
- ) : null} -
+ + {/* SIDE RAIL — documents · attached precedents · finish-and-transfer */} + + + ) : null}
); } diff --git a/web-ui/src/app/cases/[caseNumber]/page.tsx b/web-ui/src/app/cases/[caseNumber]/page.tsx index f252362..81553ec 100644 --- a/web-ui/src/app/cases/[caseNumber]/page.tsx +++ b/web-ui/src/app/cases/[caseNumber]/page.tsx @@ -45,10 +45,10 @@ export default function CaseDetailPage({ ? EXPECTED_OUTCOME_LABELS[data.expected_outcome] ?? data.expected_outcome : null; - return ( - -
- {error ? ( + if (error) { + return ( + +

שגיאה בטעינת התיק

@@ -58,130 +58,168 @@ export default function CaseDetailPage({
+
+
+ ); + } + + const tabsList = ( + + {[ + ["overview", "סקירה"], + ["arguments", "טיעונים"], + ["decision", "ההחלטה"], + ["drafts", "טיוטות והערות"], + ["agents", "סוכנים"], + ].map(([value, label]) => ( + + {label} + + ))} + + ); + + const bandActions = ( + <> + {data && } + + + ); + + return ( + + + {/* parchment band — header (title/chips/parties/actions) + tab strip */} + {isPending ? ( +
+ + + +
) : ( - <> - {isPending ? ( - - - - - + + )} + + {/* two-column wrap — main tab content (1fr) + rail (340px) */} +
+
+ + +
+ סקירת התיק +
+ +
+

תוצאה צפויה

+

+ {expectedOutcomeLabel ?? "לא נקבעה תוצאה צפויה."} +

+
+
+
+
בעיבוד
+
+ {data?.processing_count ?? 0} +
+
+
+ {canStartWorkflow && ( +
+ +
+ )}
- ) : ( - - )} -
+ + + {/* gold CTA — open the decision editor (mockup .cta) */} + + + + - -
- - סקירה - - טיעונים - - - ההחלטה - - - טיוטות והערות - - - סוכנים - - -
- - {data && } - -
-
- - -
-

תוצאה צפויה

-

- {expectedOutcomeLabel ?? "לא נקבעה תוצאה צפויה."} -

-
-
-

סיכום מהיר

-
-
בעיבוד
-
- {data?.processing_count ?? 0} -
-
-
- {canStartWorkflow && ( -
- -
- )} - -
- - - - - - - - - - - - - - - - -
+
+
- - - -

שלב בתהליך

- - - + + + + -
- - )} -
+ + + + + + + + + + + + + + + + + + + + {/* rail — status timeline + status controls (mockup .rail) */} +
+ +
+ סטטוס התיק +
+ + + + + + +
+
+ +
); } diff --git a/web-ui/src/app/digests/page.tsx b/web-ui/src/app/digests/page.tsx index ee55e43..2caee2d 100644 --- a/web-ui/src/app/digests/page.tsx +++ b/web-ui/src/app/digests/page.tsx @@ -2,11 +2,11 @@ import Link from "next/link"; import { AppShell } from "@/components/app-shell"; -import { Card, CardContent } from "@/components/ui/card"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Badge } from "@/components/ui/badge"; import { DigestListPanel } from "@/components/digests/digest-list-panel"; import { DigestSearchPanel } from "@/components/digests/digest-search-panel"; +import { DigestUploadDialog } from "@/components/digests/digest-upload-dialog"; import { useDigestPending } from "@/lib/api/digests"; /** @@ -38,44 +38,68 @@ export default function DigestsPage() { return (
-
-
); diff --git a/web-ui/src/app/feedback/page.tsx b/web-ui/src/app/feedback/page.tsx index df624ad..b89c65d 100644 --- a/web-ui/src/app/feedback/page.tsx +++ b/web-ui/src/app/feedback/page.tsx @@ -7,12 +7,11 @@ import { toast } from "sonner"; import { AppShell } from "@/components/app-shell"; import { Card, CardContent } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; -import { Badge } from "@/components/ui/badge"; import { useFeedbackList, useResolveFeedback, + useCreateFeedback, CATEGORY_LABELS, - CATEGORY_COLORS, BLOCK_LABELS, type ChairFeedback, type FeedbackCategory, @@ -24,6 +23,16 @@ import { * "טרם יושמו" וקטגוריה, וסימון כל הערה כיושמה. מוזן מ-/api/feedback. */ +// category chip styling per mockup 06 (.c-missing / .c-tone / .c-struct / .c-fact / .c-style) +const CAT_CHIP: Record = { + missing_content: "bg-warn-bg text-warn", + wrong_tone: "bg-info-bg text-info", + wrong_structure: "bg-gold-wash text-gold-deep border border-rule", + factual_error: "bg-danger-bg text-danger", + style: "bg-rule-soft text-ink-soft", + other: "bg-rule-soft text-ink-soft", +}; + function formatDate(iso?: string | null): string { if (!iso) return ""; try { @@ -57,47 +66,51 @@ function FeedbackCard({ fb }: { fb: ChairFeedback }) { }; return ( - - -
- + + + {/* meta row — where · category chip · when (mockup 06 .meta) */} +
+ + {fb.case_number ? ( + + ערר {fb.case_number} + + ) : ( + "ללא תיק" + )} + · {BLOCK_LABELS[fb.block_id] ?? fb.block_id} + + {CATEGORY_LABELS[fb.category]} - - - {BLOCK_LABELS[fb.block_id] ?? fb.block_id} - - {fb.case_number ? ( - - תיק {fb.case_number} - - ) : ( - ללא תיק - )} - + + {formatDate(fb.created_at)}
-

+

{fb.feedback_text}

{fb.lesson_extracted ? ( -
-
לקח שהופק
-

- {fb.lesson_extracted} -

-
+

+ לקח שחולץ: {fb.lesson_extracted} +

) : null} -
+ {/* action row — gold CTA / applied pill (mockup 06 .actrow) */} +
{fb.resolved ? ( - - יושמה + + יושם ) : (