feat(stats): переписать дашборд статистики с Flet на React
stats/ (Flet) генерировал графики через matplotlib в PNG и отдавал их через Flutter web с WebSocket — та же архитектура, что подводила основной фронтенд (см. известные ошибки в README). Плавающая версия flet>=0.82.2 недавно подтянула 0.86.0 с несовместимым протоколом фреймирования, и дашборд перестал открываться в браузере. stats-react/ — SPA по тому же паттерну, что и web-react (Vite+React+TS, свой Dockerfile+nginx, same-origin прокси /api вместо WebSocket). Функциональность 1:1: 4 summary-карточки, общие фильтры (тест/ организация/группа/год), два таба (прохождения / результаты тестов), 8 графиков, топ-10 таблица, фильтр по оси + сравнение по организациям/ группам. Круговая диаграмма заменена на 100%-stacked bar (dataviz: "part-to-whole rides on the stacked bar chart; donut stays deprioritized") с фолдингом длинного хвоста категорий в "Другое" — у оригинала было 32 категории на 8 цветов, из-за чего сегменты становились неразличимы. Категориальная палитра графиков провалидирована на CVD-безопасность (scripts/validate_palette.js, light+dark) — 8-цветная палитра из исходного designer.py не проходила проверку (hard fail по normal-vision floor). Бренд-оранжевый сохранён в палитре, остальные 7 слотов — из референсного набора skill'а. stats/ и Dockerfile.stats удалены, docker-compose.yml переключён на stats-react. Проверено сборкой всех трёх образов, полным compose up и визуально (headless Chrome, light+dark, обе вкладки, реальные данные с прод-сервера). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
43
stats-react/src/theme/palette.ts
Normal file
43
stats-react/src/theme/palette.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
// Палитра дашборда. Бренд-цвета — из web-react/src/theme/themes.ts (designer.py).
|
||||
// Категориальная 8-цветная палитра для графиков (pie/multi-bar) провалидирована
|
||||
// dataviz-скиллом (scripts/validate_palette.js) на CVD-безопасность в light и
|
||||
// dark режимах — порядок слотов и hex не менять без повторной валидации.
|
||||
// Единственная замена относительно референсного набора: слот "orange" — бренд-оранжевый.
|
||||
|
||||
export const BRAND = {
|
||||
primary: "#F85A40",
|
||||
primaryDark: "#D94530",
|
||||
primaryLight: "#FFEAB7",
|
||||
accent: "#FFC845",
|
||||
info: "#007FBD",
|
||||
success: "#85C446",
|
||||
};
|
||||
|
||||
// [PASS] все проверки, light: surface #FFFFFF (WARN contrast на magenta/yellow/aqua — компенсируется прямыми подписями значений)
|
||||
export const CATEGORICAL_LIGHT = [
|
||||
"#2a78d6", // blue
|
||||
"#008300", // green
|
||||
"#e87ba4", // magenta
|
||||
"#eda100", // yellow
|
||||
"#1baf7a", // aqua
|
||||
"#F85A40", // orange (бренд)
|
||||
"#4a3aa7", // violet
|
||||
"#e34948", // red
|
||||
];
|
||||
|
||||
// [PASS] все проверки, dark: surface #1a1a19
|
||||
export const CATEGORICAL_DARK = [
|
||||
"#3987e5",
|
||||
"#008300",
|
||||
"#d55181",
|
||||
"#c98500",
|
||||
"#199e70",
|
||||
"#D94530", // orange (бренд, тёмный шаг)
|
||||
"#9085e9",
|
||||
"#e66767",
|
||||
];
|
||||
|
||||
export function categoricalColor(index: number, dark: boolean): string {
|
||||
const arr = dark ? CATEGORICAL_DARK : CATEGORICAL_LIGHT;
|
||||
return arr[index % arr.length];
|
||||
}
|
||||
Reference in New Issue
Block a user