feat(ui): מתאמי-סוכנים לטבלה + אימוץ עיצוב-תגים בתור-ההלכות (קטגוריה B #2/#3)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 5s
Lint — undefined names / undefined-names (pull_request) Successful in 10s

הכרעות-מוצר מדוח-הנאמנות:
- agent-adapters-panel: כרטיסונים → טבלה (6 עמודות: תפקיד+תת-תפקיד / מתאם /
  מודל / העבר-ל / פעולה / מצב) לפי מוקאפ 02d. כל הלוגיקה נשמרה (preflight-dialog,
  revert, relax-tools, fallback-bar, דגל-א-סימטרי). עמודת "מצב": תקין/מועבר·
  fallback/⚠ א-סימטרי. תת-תפקיד דו-לשוני (analyst/writer/qa…).
- halacha review card: אימוץ שפת-התגים/צבעים ממוקאפ 19 — תג-זהב "הלכה" בפתח
  שורת-המטא, ורקע gold-wash לכרטיסים עם התלבטות-פאנל (כמו .rc מול .rc.plain).
  מבנה-האקורדיון נשמר (החלטת חיים).

יישור-קוד למוקאפים-מאושרים — ללא סבב-עיצוב חדש.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-17 03:33:37 +00:00
parent b4cb0a69c3
commit 85493502f0
2 changed files with 108 additions and 54 deletions

View File

@@ -27,6 +27,14 @@ import {
SelectContent,
SelectItem,
} from "@/components/ui/select";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import {
Dialog,
DialogContent,
@@ -51,6 +59,18 @@ const ADAPTERS = [
{ value: "deepseek_local", label: "DeepSeek", cls: "bg-gold-wash text-gold-deep border-gold/40" },
] as const;
// Bilingual role subtitle under the agent name (mockup 02d) — display-only,
// keyed by the agent's Hebrew name. Falls back to none for unmapped agents.
const ROLE_SUBTITLE: Record<string, string> = {
"עוזר משפטי": "CEO · מתזמר",
"מנתח משפטי": "analyst",
"כותב החלטה": "writer",
"מנהל ידע": "אוצֵר-הלכות",
"בודק איכות": "qa",
"הגהת מסמכים": "proofreader",
"שטן מליץ": "red-team (Gemini)",
};
function adapterCls(a: string | null | undefined): string {
return ADAPTERS.find((x) => x.value === a)?.cls ?? "bg-rule-soft text-ink-muted border-rule";
}
@@ -157,60 +177,89 @@ export function AgentAdaptersPanel() {
{isLoading || !data ? (
<Skeleton className="h-40 w-full" />
) : (
<div className="grid gap-2">
{data.pairs.map((p) => {
const cur = currentOf(p);
const tgt = targetFor(p);
const migrated = cur !== "claude_local" && cur !== null;
const asym = isAsymmetric(p);
return (
<div key={p.name}
className="flex items-center justify-between gap-3 rounded-md border border-rule-soft bg-rule-soft/30 px-3 py-2 flex-wrap">
<div className="min-w-0">
<div className="flex items-center gap-2 flex-wrap">
<span className="text-[0.85rem] text-navy font-semibold">{p.name}</span>
<Badge variant="outline" className={`font-normal ${adapterCls(cur)}`}>
{adapterLabel(cur)}
</Badge>
<span className="text-[0.66rem] text-ink-muted font-mono" dir="ltr">
{modelOf(p) || "(default)"}
</span>
{asym ? (
<Badge variant="outline" className="font-normal bg-danger-bg text-danger border-danger/40">
א-סימטרי בין החברות
<div className="overflow-x-auto rounded-md border border-rule-soft">
<Table>
<TableHeader>
<TableRow className="bg-parchment hover:bg-parchment border-rule">
<TableHead className="text-start text-[0.72rem] font-medium text-ink-muted">תפקיד</TableHead>
<TableHead className="text-start text-[0.72rem] font-medium text-ink-muted">מתאם נוכחי</TableHead>
<TableHead className="text-start text-[0.72rem] font-medium text-ink-muted">מודל</TableHead>
<TableHead className="text-start text-[0.72rem] font-medium text-ink-muted">העבר ל־</TableHead>
<TableHead className="text-start text-[0.72rem] font-medium text-ink-muted" aria-label="פעולה" />
<TableHead className="text-start text-[0.72rem] font-medium text-ink-muted">מצב</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data.pairs.map((p) => {
const cur = currentOf(p);
const tgt = targetFor(p);
const migrated = cur !== "claude_local" && cur !== null;
const asym = isAsymmetric(p);
const subtitle = ROLE_SUBTITLE[p.name];
return (
<TableRow key={p.name} className={`border-rule-soft ${asym ? "bg-danger-bg/25" : ""}`}>
<TableCell className="py-2.5 align-middle">
<div className="text-[0.85rem] text-navy font-semibold leading-tight">{p.name}</div>
{subtitle ? (
<div className="text-[0.68rem] text-ink-muted leading-tight">{subtitle}</div>
) : null}
</TableCell>
<TableCell className="py-2.5 align-middle">
<Badge variant="outline" className={`font-normal ${adapterCls(cur)}`}>
{adapterLabel(cur)}
</Badge>
) : migrated ? (
<Badge variant="outline" className="font-normal bg-warn-bg text-warn border-warn/40">
מועבר · fallback
</Badge>
) : null}
</div>
</div>
<div className="flex items-center gap-1.5 shrink-0">
<Select value={tgt} onValueChange={(v) => setTargets((t) => ({ ...t, [p.name]: v }))}>
<SelectTrigger size="sm" className="w-[8.5rem]">
<SelectValue />
</SelectTrigger>
<SelectContent>
{ADAPTERS.map((a) => (
<SelectItem key={a.value} value={a.value}>{a.label}</SelectItem>
))}
</SelectContent>
</Select>
<Button size="xs" variant="default" disabled={busy || tgt === cur}
onClick={() => openMigrate(p.name, tgt, `העברת "${p.name}" → ${adapterLabel(tgt)}`)}>
העבר
</Button>
{migrated ? (
<Button size="xs" variant="ghost" disabled={busy}
onClick={() => openRevert(p.name, `החזרת "${p.name}" למצב-מקור`)}>
החזר
</Button>
) : null}
</div>
</div>
);
})}
</TableCell>
<TableCell className="py-2.5 align-middle">
<span className="text-[0.68rem] text-ink-soft font-mono" dir="ltr">
{modelOf(p) || "(default)"}
</span>
</TableCell>
<TableCell className="py-2.5 align-middle">
<Select value={tgt} onValueChange={(v) => setTargets((t) => ({ ...t, [p.name]: v }))}>
<SelectTrigger size="sm" className="w-[8.5rem]">
<SelectValue />
</SelectTrigger>
<SelectContent>
{ADAPTERS.map((a) => (
<SelectItem key={a.value} value={a.value}>{a.label}</SelectItem>
))}
</SelectContent>
</Select>
</TableCell>
<TableCell className="py-2.5 align-middle">
<div className="flex items-center gap-1.5">
<Button size="xs" variant="default" disabled={busy || tgt === cur}
onClick={() => openMigrate(p.name, tgt, `העברת "${p.name}" → ${adapterLabel(tgt)}`)}>
העבר
</Button>
{migrated ? (
<Button size="xs" variant="ghost" disabled={busy}
onClick={() => openRevert(p.name, `החזרת "${p.name}" למצב-מקור`)}>
החזר
</Button>
) : null}
</div>
</TableCell>
<TableCell className="py-2.5 align-middle">
{asym ? (
<Badge variant="outline" className="font-normal bg-danger-bg text-danger border-danger/40 whitespace-nowrap">
א-סימטרי
</Badge>
) : migrated ? (
<Badge variant="outline" className="font-normal bg-warn-bg text-warn border-warn/40 whitespace-nowrap">
מועבר · fallback
</Badge>
) : (
<Badge variant="outline" className="font-normal bg-success-bg text-success border-success/40">
תקין
</Badge>
)}
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div>
)}
</CardContent>