// phase5-pages.jsx — Онбординг · Навчання · Аудит · Сповіщення // ============ ОНБОРДИНГ ============ function OnboardingPage({ role }) { const steps = window.ONBOARDING_STEPS; const cats = window.ONBOARDING_CATEGORIES; const active = window.ACTIVE_ONBOARDINGS; const [openId, setOpenId] = React.useState(null); return (

Онбординг новачків

{active.length} {window.plural(active.length, "новачок", "новачки", "новачків")} зараз {steps.length} кроків у чек-листі щоб ніхто не загубився в перший тиждень
Як працює: при додаванні нового співробітника створюється персональний чек-лист з 20 пунктів. Менеджер і ментор галочкують виконане. Прогрес видно всім.

Сторінки для новачка

видай у перший день
Пам'ятка · Доступ до ERP
Як новачок уперше входить у систему — 3 кроки зі скріншотами реальних екранів: вхід, зміна пароля, «На сьогодні». Придатна для друку й розсилки.
Відкрити пам'ятку Роздрукувати

Активні онбординги

{active.length}
{active.map(o => { const total = steps.length; const done = Object.values(o.progress).filter(v => v).length; const pct = Math.round(done / total * 100); const mentor = window.getTeam(o.mentor); const today = new Date("2026-05-26"); const start = new Date(o.startDate); const daysIn = Math.round((today - start) / 86400000); return (
setOpenId(openId === o.id ? null : o.id)}>
{o.who.split(" ").map(s => s[0]).slice(0,2).join("")}
{o.who}
{o.role} · працює {daysIn} {window.plural(daysIn, "день", "дні", "днів")}
{done}/{total}
{pct}% готово
Ментор
{mentor?.initials}
{openId === o.id && (
{Object.entries(cats).map(([catId, cat]) => { const catSteps = steps.filter(s => s.category === catId); const catDone = catSteps.filter(s => o.progress[s.id]).length; return (
{cat.label} {catDone}/{catSteps.length}
{catSteps.map(s => { const isDone = o.progress[s.id]; return (
{isDone && } {s.label} {s.required && обов'язк.}
); })}
); })}
)}
); })}

Шаблон чек-листа

{steps.length} {window.plural(steps.length, "крок", "кроки", "кроків")}
{Object.entries(cats).map(([catId, cat]) => { const catSteps = steps.filter(s => s.category === catId); return (
{cat.label} {catSteps.length} {window.plural(catSteps.length, "крок", "кроки", "кроків")}
{catSteps.map(s => (
{s.label} {s.required && обов'язк.}
))}
); })}
); } // ============ НАВЧАННЯ ============ function TrainingPage({ role }) { const trainings = window.DATA.TRAININGS; const team = window.DATA.TEAM; const [filter, setFilter] = React.useState("active"); const planned = trainings.filter(t => t.status === "planned" || t.status === "scheduled"); const inProgress = trainings.filter(t => t.status === "in_progress"); const completed = trainings.filter(t => t.status === "completed"); const active = [...inProgress, ...planned]; let shown = trainings; if (filter === "active") shown = active; else if (filter === "completed") shown = completed; const totalCost = trainings.reduce((s, t) => s + t.cost, 0); const yearCost = trainings.filter(t => t.date.startsWith("2026")).reduce((s, t) => s + t.cost, 0); return (

Навчання команди

{trainings.length} {window.plural(trainings.length, "запис", "записи", "записів")} {inProgress.length} триває бюджет {window.formatMoney(yearCost)} ₴ на 2026
Заплановано
{planned.length}
майбутні курси
Триває
{inProgress.length}
зараз навчаються
Завершено
{completed.length}
за рік
Бюджет 2026
{window.formatMoney(yearCost)}
витрачено + заплановано
{shown.map(t => { const person = window.getTeam(t.who); const typ = window.TRAINING_TYPE_LABELS[t.type]; const st = window.TRAINING_STATUS[t.status]; return ( ); })}
Курс / захід Учасник Тип Тривалість Дата Вартість Статус
{t.title}
{t.provider}
{person?.initials}
{person?.name.split(" ")[0]} {person?.name.split(" ")[1]?.[0]}.
{typ.label} {t.duration} {window.formatDate(t.date)} {t.cost > 0 ? window.formatMoney(t.cost) + " ₴" : безкошт.}
{st.label} {t.status === "in_progress" && t.progress && (
{t.progress}% завершено
)}
); } // ============ АУДИТ ============ function AuditLogPage({ role }) { const log = window.AUDIT_LOG; const actions = window.AUDIT_ACTIONS; const entities = window.AUDIT_ENTITY_LABELS; const team = window.DATA.TEAM; const [filterWho, setFilterWho] = React.useState("all"); const [filterAction, setFilterAction] = React.useState("all"); let shown = log; if (filterWho !== "all") shown = shown.filter(l => l.who === filterWho); if (filterAction !== "all") shown = shown.filter(l => l.action === filterAction); // Group by day const byDay = {}; shown.forEach(item => { const day = item.date.substring(0, 10); if (!byDay[day]) byDay[day] = []; byDay[day].push(item); }); const days = Object.keys(byDay).sort((a, b) => b.localeCompare(a)); return (

Журнал аудиту

{log.length} {window.plural(log.length, "запис", "записи", "записів")} за {days.length} {window.plural(days.length, "день", "дні", "днів")} хто що змінив у системі усі дії незмінні · зберігаються 7 років
Для чого: вимога для відповідальних об'єктів (СС2/СС3) і для перевірок — повний слід усіх змін: підписи, узгодження, виставлення рахунків, редагування проєктної документації. Записи неможливо видалити.
{team.map(p => ( ))}
{days.map(day => (
{window.formatDate(day)}
{byDay[day].map(item => { const act = actions[item.action]; const person = window.getTeam(item.who) || { initials: "СС", name: "Система" }; const time = item.date.substring(11, 16); return (
{time}
{person.initials}
{person.name.split(" ")[0]}
{act.label.toLowerCase()}
{entities[item.entity]} {item.entityName}
{item.details}
); })}
))}
); } // ============ СПОВІЩЕННЯ ============ function NotificationsPage({ role }) { const all = window.NOTIFICATIONS; const kinds = window.NOTIF_KINDS; const [filter, setFilter] = React.useState("unread"); const unread = all.filter(n => !n.read); const important = all.filter(n => n.important); let shown = all; if (filter === "unread") shown = unread; else if (filter === "important") shown = important; return (

Сповіщення

{all.length} {window.plural(all.length, "сповіщення", "сповіщення", "сповіщень")} {unread.length > 0 && <> {unread.length} непрочитаних } {important.length > 0 && <> {important.length} важливих }

Налаштування каналів

Відправник e-mail сповіщень
Усі листи (нагадування про задачі, дедлайни, рахунки, ДН, КЕП) надходять від erp@ukrbudproiekt.ua — той самий акаунт, що й сховище Drive.
активно
{Object.entries(kinds).map(([id, k]) => (
{k.label}
))}
{shown.map(n => { const kind = kinds[n.kind]; const time = n.time.substring(11, 16); const day = n.time.substring(0, 10); const isToday = day === "2026-05-26"; return (
{n.title}
{n.text}
{isToday ? "сьогодні" : window.formatDate(day)}
{time}
{!n.read && }
); })}
); } window.OnboardingPage = OnboardingPage; window.TrainingPage = TrainingPage; window.AuditLogPage = AuditLogPage; window.NotificationsPage = NotificationsPage;