feat: workload analysis in days/weeks

Adds a "Нагрузка на сотрудника" block: days per cycle, weeks per cycle,
total days/weeks per month at the current vacancy plan, and an
overload/buffer card vs 21 working days a month.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Askarova A 2026-05-22 13:43:03 +05:00
parent f4ce3c97a2
commit 95fe078387
2 changed files with 28 additions and 1 deletions

22
app.js
View File

@ -2,6 +2,8 @@
const HOURS_MONTH = 168; // 21 рабочий день × 8 часов
const MIN_DAY = 480; // 8 × 60
const DAYS_WEEK = 5; // рабочая неделя
const DAYS_MONTH = 21; // рабочих дней в месяце
const WORDS = {
'один':1,'одна':1,'одной':1,'одного':1,
@ -131,10 +133,18 @@ function metrics(items) {
const bottleneck = total > 0 ? top3 / total * 100 : 0;
const cost = state.salary > 0 ? state.salary * hours / HOURS_MONTH : null;
// Нагрузка в днях/неделях
const daysPerCycle = total / MIN_DAY;
const weeksPerCycle = daysPerCycle / DAYS_WEEK;
const monthlyDays = state.vacancies * daysPerCycle;
const monthlyWeeks = monthlyDays / DAYS_WEEK;
const monthOverload = monthlyDays - DAYS_MONTH; // перегруз сверх месяца, дней
return {
total, hours, count: items.length, avg: valid.length ? total / valid.length : 0,
longest, top10, byGroup,
fte, capacity, bottleneck, cost,
daysPerCycle, weeksPerCycle, monthlyDays, monthlyWeeks, monthOverload,
};
}
@ -158,6 +168,18 @@ function render() {
{ value: m.cost != null ? `${fmt(Math.round(m.cost))} ₸` : '—', label: 'Стоимость 1 цикла' },
]);
renderCards('workload-kpi', [
{ value: `${m.daysPerCycle.toFixed(1)} дн`, label: 'Нагрузка на 1 цикл', sub: '8-час. рабочий день' },
{ value: `${m.weeksPerCycle.toFixed(2)} нед`, label: 'То же в неделях', sub: '5-дневка' },
{ value: `${m.monthlyDays.toFixed(1)} дн`, label: `За месяц (${state.vacancies} вак.)`, sub: `план ${state.vacancies} × ${m.daysPerCycle.toFixed(1)} дн` },
{ value: `${m.monthlyWeeks.toFixed(2)} нед`, label: 'То же в неделях', sub: `норма — ${(DAYS_MONTH/DAYS_WEEK).toFixed(1)} нед/мес` },
{
value: m.monthOverload >= 0 ? `+${m.monthOverload.toFixed(1)} дн` : `${m.monthOverload.toFixed(1)} дн`,
label: m.monthOverload > 0 ? '⚠ Перегруз сверх месяца' : '✓ Запас в месяце',
sub: m.monthOverload > 0 ? `нужно +${(m.monthOverload/DAYS_MONTH).toFixed(2)} FTE` : `свободно ${Math.abs(m.monthOverload).toFixed(1)} дн`,
},
]);
chartTop10(m.top10);
chartGroups(m.byGroup);
renderTable(items);

View File

@ -39,6 +39,11 @@
<div id="strategic-kpi" class="kpi-grid"></div>
</section>
<section class="block">
<h2>👤 Нагрузка на сотрудника (дни и недели)</h2>
<div id="workload-kpi" class="kpi-grid"></div>
</section>
<section class="charts-row">
<div class="block">
<h2>Топ-10 самых долгих функций</h2>
@ -61,6 +66,6 @@
<p id="placeholder">Загрузка данных… Если дашборд не появился — нажмите «📁 Загрузить .xlsx».</p>
<script src="./app.js?v=3"></script>
<script src="./app.js?v=4"></script>
</body>
</html>