// Dashboard — real analytics computed from the поручения register + documents. function Dashboard({ onNavigate }) { const { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, CartesianGrid } = Recharts; const YEARS = [2024, 2025, 2026]; const [year, setYear] = React.useState(2025); const [tasks, setTasks] = React.useState([]); const [docs, setDocs] = React.useState([]); const [loading, setLoading] = React.useState(true); const load = React.useCallback(() => { if (!window.API) { setLoading(false); return; } setLoading(true); Promise.all([ window.API.listTasks().catch(() => []), window.API.listDocuments().catch(() => []), ]).then(([t, d]) => { setTasks(Array.isArray(t) ? t : []); setDocs(Array.isArray(d) ? d : []); }).finally(() => setLoading(false)); }, []); React.useEffect(() => { load(); }, [load]); const eff = (t) => (typeof effStatus === "function" ? effStatus(t) : t.status); const yearTasks = tasks.filter(t => t.year === year); const stat = { total: yearTasks.length, done: 0, overdue: 0, work: 0 }; yearTasks.forEach(t => { const e = eff(t); if (e === "done") stat.done++; else if (e === "overdue") stat.overdue++; else stat.work++; }); const donePct = stat.total ? Math.round(stat.done / stat.total * 100) : 0; const months = (typeof MONTH_ORDER !== "undefined") ? MONTH_ORDER : []; const byMonth = months.map(m => ({ name: m.slice(0, 3), Поручений: yearTasks.filter(t => t.mon === m).length })); const depts = (typeof DEPARTMENTS !== "undefined") ? DEPARTMENTS : []; const byDept = depts.map(d => { const list = yearTasks.filter(t => t.dept === d); return { name: d.replace("Отдел ", "").replace(" отдел", ""), Всего: list.length, Выполнено: list.filter(t => eff(t) === "done").length, }; }).filter(x => x.Всего > 0); const recent = docs.slice(0, 5); return (

Дашборд

Аналитика по реестру поручений · {year} год{loading ? " · загрузка…" : ""}

{YEARS.map(y => )}
} foot={`за ${year} год`} deltaNeutral /> } foot={`${stat.done} из ${stat.total}`} deltaNeutral>
} foot="требуют внимания" deltaNeutral /> } foot="открытые поручения" deltaNeutral />
onNavigate("docs")}>Все документы } bodyStyle={{ padding: 0 }} >
{recent.length === 0 && ( )} {recent.map(d => ( ))}
НазваниеТипЗагруженРазмер
Документов пока нет — загрузите на странице «Документы»
{d.name}
{d.type || "file"} {d.date} {d.size}
); } function Kpi({ label, value, unit, icon, delta, deltaDir, foot, deltaNeutral, children }) { return (
{label} {icon}
{value} {unit && {unit}}
{delta && ( {!deltaNeutral && (deltaDir === "up" ? : )} {delta} )} {foot}
{children}
); } window.Dashboard = Dashboard;