// «Эффективность» — коэффициент, лимит нагрузки и сигналы невыполнения (Модуль 1 + 4). function EfficiencyPage() { const [data, setData] = React.useState(null); const [monitor, setMonitor] = React.useState({ rows: [] }); const [interval, setIntervalSel] = React.useState("month"); const [testFor, setTestFor] = React.useState(null); const load = React.useCallback(() => { if (!window.API) return; Promise.all([ window.API.efficiency().catch(() => ({ rows: [] })), window.API.monitoring(interval).catch(() => ({ rows: [] })), ]).then(([eff, mon]) => { setData(eff); setMonitor(mon || { rows: [] }); }); }, [interval]); React.useEffect(() => { load(); }, [load]); const sigByPerson = {}; (monitor.rows || []).forEach(m => { sigByPerson[m.person] = m; }); if (!data) { return (

Эффективность

Тест онбординга · коэффициент · загрузка

); } // Баллы/коэффициент теста видят только руководители (admin/контроль); остальным — // лишь факт прохождения (по требованию заказчика баллы скрыты). const canSeeScores = window.ME && (window.ME.role === "admin" || window.ME.role === "controller"); const rows = (data.rows || []).slice().sort((a, b) => { const oa = a.overLimit ? 1 : 0, ob = b.overLimit ? 1 : 0; if (oa !== ob) return ob - oa; return (b.coefficient || 0) - (a.coefficient || 0); }); const tested = rows.filter(r => r.hasTest).length; const over = rows.filter(r => r.overLimit).length; const signaled = (monitor.rows || []).filter(m => m.signal).length; const coefColor = (c) => c >= 70 ? "var(--ok)" : c >= 45 ? "var(--warn)" : "var(--err)"; return (

Эффективность и нагрузка

Тест онбординга · коэффициент · загрузка по дням (по трудозатратам) · {tested} протестировано

} /> } /> } />
Сотрудник
Эффективность
Загрузка (пик/день)
Невыполнение
{rows.map(r => { const sig = sigByPerson[r.person]; return (
{r.person}
{r.dept || "—"}
{r.hasTest ? ( canSeeScores ? (
{r.coefficient}% {r.takenAt ? r.takenAt.split(" ")[0] : ""}
) : ( тест пройден{r.takenAt ? ` · ${r.takenAt.split(" ")[0]}` : ""} ) ) : тест не пройден}
выполнено {r.completed} · {r.perWeek} зад/нед{r.hours ? ` · ${r.hours} ч` : ""}
{r.peakDay ? ( <> пик {r.peakHours} ч{r.peakCount > 1 ? ` · ${r.peakCount} зад.` : ""}
{r.peakDay} · норма {r.workdayHours} ч
) : нет задач со сроком}
активных всего: {r.activeTasks}
{r.overLimit &&
перегружен в пик
}
{sig && sig.considered > 0 ? = 15 ? "warn" : "ok"}`} title={`не выполнено ${sig.notDone} из ${sig.considered}${sig.excluded ? `, исключено по отсутствию ${sig.excluded}` : ""}`}>{sig.pct}%{sig.signal ? " · сигнал" : ""} : }
{r.overLimit && r.recommendation && (
{r.recommendation}
)}
); })} {rows.length === 0 &&
Нет сотрудников.
} {testFor && setTestFor(null)} onDone={() => { setTestFor(null); load(); }} />}
); } // StatCard — small KPI tile reused on the analytics tabs. function StatCard({ label, value, icon, color }) { return (
{icon} {label}
{value}
); } // EffTestModal — 15–20 вопросов онбординга; результат → коэффициент + лимит. function EffTestModal({ person, onClose, onDone }) { const [questions, setQuestions] = React.useState(null); const [answers, setAnswers] = React.useState({}); const [busy, setBusy] = React.useState(false); const [result, setResult] = React.useState(null); const [err, setErr] = React.useState(""); React.useEffect(() => { if (!window.API || !window.API.effQuestions) return; window.API.effQuestions().then(d => setQuestions(d.questions || [])).catch(() => setQuestions([])); }, []); const allAnswered = questions && questions.every(q => answers[q.id] != null); const submit = async () => { if (!allAnswered) { setErr("Ответьте на все вопросы"); return; } setBusy(true); setErr(""); try { const arr = questions.map(q => answers[q.id]); const res = await window.API.submitEffTest(person, arr); setResult(res); } catch (e) { setErr(String((e && e.message) || e)); } finally { setBusy(false); } }; return (
e.stopPropagation()}>

Тест эффективности · {person}

Оценка при приёме / повторно по инициативе сотрудника

{result ? ( /* Баллы тестируемому не показываются (скрыты по требованию): результат виден руководителю в таблице «Эффективность». */
Тест завершён
Ответы сохранены. Результаты тестирования обрабатываются системой и доступны руководителю.
) : !questions ? (
Загрузка вопросов…
) : ( questions.map((q, i) => (
{i + 1}. {q.q}
{q.options.map((o, oi) => ( ))}
)) )} {err &&
{err}
}
{questions && !result ? `Отвечено ${Object.keys(answers).length} из ${questions.length}` : ""} {result ? : }
); } window.EfficiencyPage = EfficiencyPage;