Files
legal-ai/web-ui/src/components/training/subject-donut.tsx
Chaim 1f1a025509 fix(lint): תיקון 10 שגיאות ESLint + ניקוי directives מיותרים
10 שגיאות (כולן קיימות-מראש, לא מהפיצ'רים האחרונים):
- react/no-unescaped-entities (3): legal-arguments-panel, precedent-edit-sheet
  — escaping של מרכאות ב-JSX (“/")
- react-hooks/set-state-in-effect (6): documents-panel, chair-editor,
  content-checklists, discussion-rules, golden-ratios, documents.ts
  — disable-comment לדפוסי sync/reset לגיטימיים (false-positive ידוע)
- React Compiler reassign (1): subject-donut — refactor לחישוב prefix-sums
  ללא mutable accumulator

ניקוי: הסרת 5 eslint-disable directives מיותרים (halacha-review-panel,
precedent-upload-sheet). תוצאה: 0 errors (היה 10), 24→ warnings (היה 29).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 13:31:31 +00:00

74 lines
2.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
/*
* Corpus subject-distribution donut.
*
* Pure CSS conic-gradient — same recipe as the cases StatusDonut, but
* uses a palette-of-gold instead of a status-tone palette. Ported from
* legal-ai/web/static/index.html `renderHero`.
*/
const DONUT_COLORS = [
"var(--color-navy)",
"var(--color-gold)",
"var(--color-info)",
"var(--color-warn)",
"var(--color-success)",
"var(--color-ink-muted)",
"var(--color-gold-deep)",
];
export function SubjectDonut({
segments,
total,
}: {
segments: Array<{ label: string; count: number }>;
total: number;
}) {
// Prefix sums without a mutable outer accumulator (React Compiler rejects
// reassigning a let after render). segments is tiny, so O(n²) is fine.
const parts = segments.map((s, i) => {
const before = segments.slice(0, i).reduce((acc, x) => acc + x.count, 0);
const start = total === 0 ? 0 : (before / total) * 360;
const end = total === 0 ? 360 : ((before + s.count) / total) * 360;
return { ...s, start, end, color: DONUT_COLORS[i % DONUT_COLORS.length] };
});
const background =
total === 0
? "conic-gradient(var(--color-rule-soft) 0deg 360deg)"
: `conic-gradient(${parts
.map((p) => `${p.color} ${p.start}deg ${p.end}deg`)
.join(", ")})`;
return (
<div className="flex items-center gap-6">
<div
className="relative w-[140px] h-[140px] rounded-full shadow-sm shrink-0"
style={{ background }}
aria-label="פיזור נושאים בקורפוס"
>
<div className="absolute inset-[18px] bg-surface rounded-full flex flex-col items-center justify-center">
<span className="font-display text-2xl font-black text-navy leading-none">
{total}
</span>
<span className="text-[0.7rem] text-ink-muted mt-1">החלטות</span>
</div>
</div>
<ul className="flex flex-col gap-1.5 text-sm min-w-0">
{parts.map((p) => (
<li key={p.label} className="flex items-center gap-2">
<span
className="inline-block w-2.5 h-2.5 rounded-full shrink-0"
style={{ background: p.color }}
/>
<span className="text-ink-soft truncate">{p.label}</span>
<span className="text-ink-muted tabular-nums ms-1">{p.count}</span>
</li>
))}
</ul>
</div>
);
}