feat(ui): מתאמי-סוכנים לטבלה + עיצוב-תגים בתור-ההלכות (קטגוריה B #2/#3) #280
@@ -27,6 +27,14 @@ import {
|
|||||||
SelectContent,
|
SelectContent,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -51,6 +59,18 @@ const ADAPTERS = [
|
|||||||
{ value: "deepseek_local", label: "DeepSeek", cls: "bg-gold-wash text-gold-deep border-gold/40" },
|
{ value: "deepseek_local", label: "DeepSeek", cls: "bg-gold-wash text-gold-deep border-gold/40" },
|
||||||
] as const;
|
] 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 {
|
function adapterCls(a: string | null | undefined): string {
|
||||||
return ADAPTERS.find((x) => x.value === a)?.cls ?? "bg-rule-soft text-ink-muted border-rule";
|
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 ? (
|
{isLoading || !data ? (
|
||||||
<Skeleton className="h-40 w-full" />
|
<Skeleton className="h-40 w-full" />
|
||||||
) : (
|
) : (
|
||||||
<div className="grid gap-2">
|
<div className="overflow-x-auto rounded-md border border-rule-soft">
|
||||||
{data.pairs.map((p) => {
|
<Table>
|
||||||
const cur = currentOf(p);
|
<TableHeader>
|
||||||
const tgt = targetFor(p);
|
<TableRow className="bg-parchment hover:bg-parchment border-rule">
|
||||||
const migrated = cur !== "claude_local" && cur !== null;
|
<TableHead className="text-start text-[0.72rem] font-medium text-ink-muted">תפקיד</TableHead>
|
||||||
const asym = isAsymmetric(p);
|
<TableHead className="text-start text-[0.72rem] font-medium text-ink-muted">מתאם נוכחי</TableHead>
|
||||||
return (
|
<TableHead className="text-start text-[0.72rem] font-medium text-ink-muted">מודל</TableHead>
|
||||||
<div key={p.name}
|
<TableHead className="text-start text-[0.72rem] font-medium text-ink-muted">העבר ל־</TableHead>
|
||||||
className="flex items-center justify-between gap-3 rounded-md border border-rule-soft bg-rule-soft/30 px-3 py-2 flex-wrap">
|
<TableHead className="text-start text-[0.72rem] font-medium text-ink-muted" aria-label="פעולה" />
|
||||||
<div className="min-w-0">
|
<TableHead className="text-start text-[0.72rem] font-medium text-ink-muted">מצב</TableHead>
|
||||||
<div className="flex items-center gap-2 flex-wrap">
|
</TableRow>
|
||||||
<span className="text-[0.85rem] text-navy font-semibold">{p.name}</span>
|
</TableHeader>
|
||||||
<Badge variant="outline" className={`font-normal ${adapterCls(cur)}`}>
|
<TableBody>
|
||||||
{adapterLabel(cur)}
|
{data.pairs.map((p) => {
|
||||||
</Badge>
|
const cur = currentOf(p);
|
||||||
<span className="text-[0.66rem] text-ink-muted font-mono" dir="ltr">
|
const tgt = targetFor(p);
|
||||||
{modelOf(p) || "(default)"}
|
const migrated = cur !== "claude_local" && cur !== null;
|
||||||
</span>
|
const asym = isAsymmetric(p);
|
||||||
{asym ? (
|
const subtitle = ROLE_SUBTITLE[p.name];
|
||||||
<Badge variant="outline" className="font-normal bg-danger-bg text-danger border-danger/40">
|
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>
|
</Badge>
|
||||||
) : migrated ? (
|
</TableCell>
|
||||||
<Badge variant="outline" className="font-normal bg-warn-bg text-warn border-warn/40">
|
<TableCell className="py-2.5 align-middle">
|
||||||
מועבר · fallback
|
<span className="text-[0.68rem] text-ink-soft font-mono" dir="ltr">
|
||||||
</Badge>
|
{modelOf(p) || "(default)"}
|
||||||
) : null}
|
</span>
|
||||||
</div>
|
</TableCell>
|
||||||
</div>
|
<TableCell className="py-2.5 align-middle">
|
||||||
<div className="flex items-center gap-1.5 shrink-0">
|
<Select value={tgt} onValueChange={(v) => setTargets((t) => ({ ...t, [p.name]: v }))}>
|
||||||
<Select value={tgt} onValueChange={(v) => setTargets((t) => ({ ...t, [p.name]: v }))}>
|
<SelectTrigger size="sm" className="w-[8.5rem]">
|
||||||
<SelectTrigger size="sm" className="w-[8.5rem]">
|
<SelectValue />
|
||||||
<SelectValue />
|
</SelectTrigger>
|
||||||
</SelectTrigger>
|
<SelectContent>
|
||||||
<SelectContent>
|
{ADAPTERS.map((a) => (
|
||||||
{ADAPTERS.map((a) => (
|
<SelectItem key={a.value} value={a.value}>{a.label}</SelectItem>
|
||||||
<SelectItem key={a.value} value={a.value}>{a.label}</SelectItem>
|
))}
|
||||||
))}
|
</SelectContent>
|
||||||
</SelectContent>
|
</Select>
|
||||||
</Select>
|
</TableCell>
|
||||||
<Button size="xs" variant="default" disabled={busy || tgt === cur}
|
<TableCell className="py-2.5 align-middle">
|
||||||
onClick={() => openMigrate(p.name, tgt, `העברת "${p.name}" → ${adapterLabel(tgt)}`)}>
|
<div className="flex items-center gap-1.5">
|
||||||
העבר
|
<Button size="xs" variant="default" disabled={busy || tgt === cur}
|
||||||
</Button>
|
onClick={() => openMigrate(p.name, tgt, `העברת "${p.name}" → ${adapterLabel(tgt)}`)}>
|
||||||
{migrated ? (
|
העבר
|
||||||
<Button size="xs" variant="ghost" disabled={busy}
|
</Button>
|
||||||
onClick={() => openRevert(p.name, `החזרת "${p.name}" למצב-מקור`)}>
|
{migrated ? (
|
||||||
החזר
|
<Button size="xs" variant="ghost" disabled={busy}
|
||||||
</Button>
|
onClick={() => openRevert(p.name, `החזרת "${p.name}" למצב-מקור`)}>
|
||||||
) : null}
|
החזר
|
||||||
</div>
|
</Button>
|
||||||
</div>
|
) : 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>
|
</div>
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
@@ -185,11 +185,16 @@ function HalachaCard({
|
|||||||
<div
|
<div
|
||||||
data-halacha-id={h.id}
|
data-halacha-id={h.id}
|
||||||
className={`
|
className={`
|
||||||
rounded-lg border bg-surface p-4 space-y-3 transition-colors
|
rounded-lg border p-4 space-y-3 transition-colors
|
||||||
|
${h.panel_round ? "bg-gold-wash" : "bg-surface"}
|
||||||
${focused ? "border-gold ring-2 ring-gold/40 shadow-md" : "border-rule"}
|
${focused ? "border-gold ring-2 ring-gold/40 shadow-md" : "border-rule"}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<div className="flex items-start gap-2 text-[0.78rem] text-ink-muted flex-wrap">
|
<div className="flex items-start gap-2 text-[0.78rem] text-ink-muted flex-wrap">
|
||||||
|
{/* gold "הלכה" tag opening the meta row (mockup 19 `.b-hal`) */}
|
||||||
|
<Badge className="rounded bg-gold text-white border-0 text-[0.62rem] font-bold tracking-wide">
|
||||||
|
הלכה
|
||||||
|
</Badge>
|
||||||
{h.page_reference && (
|
{h.page_reference && (
|
||||||
<span className="text-[0.7rem]">{h.page_reference}</span>
|
<span className="text-[0.7rem]">{h.page_reference}</span>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user