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>
74 lines
2.4 KiB
TypeScript
74 lines
2.4 KiB
TypeScript
"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>
|
||
);
|
||
}
|