// phase6-pages.jsx — Решта модулів: Гант, Архів, Матеріали, Лікарняні, Аванси, Бонуси, Історія ЗП, АН, Акти, ТУ, ДСНС, КЕП // ============ ГАНТ ============ function GanttPage({ tasks }) { const objects = window.DATA.OBJECTS; const stages = window.DATA.STAGES; return (

Гант-діаграма

часова шкала всіх об'єктів травень 2026 — грудень 2026
сьогодні · 26 травня 2026
Об'єкт / етап
{["Тра", "Чер", "Лип", "Сер", "Вер", "Жов", "Лис", "Гру"].map((m, i) => (
{m}
))}
{objects.map(obj => { const objStages = stages.map(s => { const planned = obj.plan?.[s.id] || 0; const isCompleted = obj.completedStages?.includes(s.id); const isActive = obj.activeStage === s.id; return { stage: s, planned, isCompleted, isActive }; }).filter(s => s.planned > 0 || s.isCompleted); return (
{obj.code}
{obj.name}
до {obj.deadline}
{objStages.map((s, i) => { const widthPct = 8 + Math.random() * 20; const startPct = i === 0 ? 0 : objStages.slice(0, i).reduce((sum) => sum + 12, 0); return (
{s.stage.short}
); })}
); })}
); } // ============ АРХІВ ПРОЄКТІВ ============ function ArchivePage({ role }) { const archived = [ { code: "УКП-2023-12", name: "ЖК «Дніпровські вежі»", grade: "СС2", client: "ТОВ «РіверБуд»", year: 2023, finalSum: 12400000, drive: true }, { code: "УКП-2023-08", name: "Школа №201", grade: "СС2", client: "Управління освіти м. Києва", year: 2023, finalSum: 3800000, drive: true }, { code: "УКП-2022-22", name: "Дитсадок «Веселка»", grade: "СС2", client: "Управління освіти", year: 2022, finalSum: 2900000, drive: true }, { code: "УКП-2022-15", name: "Котедж в Лютежі", grade: "СС1", client: "Іванов В.І.", year: 2022, finalSum: 380000, drive: true }, { code: "УКП-2022-05", name: "Реконструкція бібліотеки", grade: "СС2", client: "Мінкультури", year: 2022, finalSum: 1200000, drive: true }, { code: "УКП-2021-18", name: "Складський комплекс «ЛогіКс»", grade: "СС2", client: "ТОВ «ЛогіКс»", year: 2021, finalSum: 4500000, drive: true }, ]; return (

Архів проєктів

{archived.length} завершених довідкова база усі матеріали в Drive
Як використовувати: архів — це портфоліо для тендерів і референси для нових проєктів. Кожен проєкт містить всі креслення, експертизи, фотофіксацію в Drive. Можна копіювати рішення для подібних об'єктів.
{archived.map(a => ( ))}
Шифр Назва Клас Замовник Рік Підсумкова сума
{a.code}
{a.name}
{a.grade} {a.client} {a.year} {window.formatMoney(a.finalSum)} ₴
); } // ============ БАЗА МАТЕРІАЛІВ ============ function MaterialsPage({ role }) { const materials = [ { name: "Бетон М300", unit: "м³", price: 4500, supplier: "ТОВ «Бетон+»", updated: "2026-05-20", group: "Бетон" }, { name: "Бетон М400", unit: "м³", price: 5200, supplier: "ТОВ «Бетон+»", updated: "2026-05-20", group: "Бетон" }, { name: "Арматура А500С Ø12", unit: "т", price: 38500, supplier: "Метінвест", updated: "2026-05-15", group: "Метал" }, { name: "Арматура А500С Ø16", unit: "т", price: 36800, supplier: "Метінвест", updated: "2026-05-15", group: "Метал" }, { name: "Цегла керамічна М150", unit: "тис.шт", price: 18500, supplier: "Слобожанська цегла", updated: "2026-05-10", group: "Стіни" }, { name: "Газобетон D500", unit: "м³", price: 3200, supplier: "Стоунлайт", updated: "2026-05-12", group: "Стіни" }, { name: "Утеплювач Penoplex 50мм", unit: "м³", price: 2800, supplier: "Епіцентр", updated: "2026-05-08", group: "Утеплення" }, { name: "Мінвата Rockwool 100мм", unit: "м³", price: 3600, supplier: "Rockwool", updated: "2026-05-08", group: "Утеплення" }, { name: "Покрівля метал.черепиця", unit: "м²", price: 480, supplier: "Прушинські", updated: "2026-05-05", group: "Покрівля" }, { name: "Вікно металопластикове Rehau", unit: "м²", price: 4200, supplier: "Rehau", updated: "2026-05-18", group: "Вікна" }, ]; const totalGroups = [...new Set(materials.map(m => m.group))]; return (

База матеріалів і цін

{materials.length} позицій {totalGroups.length} груп ціни оновлено · {window.formatDate("2026-05-20")}
Для кошторисів: регулярно оновлювана база цін постачальників для швидкого формування кошторисів і тендерних пропозицій. Можна імпортувати з Excel або синхронізувати з постачальниками.
{totalGroups.map(g => )}
{materials.map((m, i) => ( ))}
Матеріал Група Одиниця Ціна, ₴ Постачальник Оновлено
{m.name}
{m.group} {m.unit} {window.formatMoney(m.price)} ₴ {m.supplier} {window.formatDate(m.updated)}
); } // ============ АВАНСИ (ЗП) ============ function AdvancePayPage({ role }) { const team = window.DATA.TEAM; const today = new Date("2026-05-26"); return (

Авансові виплати

аванс на середину місяця · 15 числа 40% від місячного окладу
Найближчий аванс
15.06
через 20 днів
Сума авансу
{window.formatMoney(80000)}
40% × штатних
Останній аванс
15.05
сплачено вчасно
{team.filter(p => p.employment !== "contractor").map(p => { const base = p.baseSalary * (p.partRate || 1); const adv = Math.round(base * 0.4); return ( ); })}
ПрацівникТипОкладАванс 40%Дата виплатиСтатус
{p.initials}
{p.name}
{p.role}
{p.employment === "staff" ? "Штат" : "Часткова"} {window.formatMoney(base)} ₴ {window.formatMoney(adv)} ₴ 15.06.2026 заплановано
); } // ============ ЛІКАРНЯНІ ============ function SickPayPage({ role }) { const sicks = [ { who: "tm", from: "2026-05-12", to: "2026-05-14", days: 3, status: "approved", certNumber: "АА-2837401", basis: "лікарняний лист", payable: 1850 }, { who: "ok", from: "2026-02-08", to: "2026-02-12", days: 5, status: "approved", certNumber: "АА-2745192", basis: "лікарняний лист", payable: 4200, paidDate: "2026-02-20" }, { who: "ap", from: "2025-11-20", to: "2025-11-24", days: 5, status: "approved", certNumber: "АА-2598437", basis: "лікарняний лист", payable: 2900, paidDate: "2025-12-05" }, ]; return (

Лікарняні

оплата перших 5 днів — компанія з 6-го дня — ФСС
Як рахується: перші 5 днів оплачуються роботодавцем у розмірі 60-100% середньоденного заробітку (залежно від стажу). З 6-го дня — Фонд соціального страхування.
{sicks.map((s, i) => { const p = window.getTeam(s.who); return ( ); })}
ПрацівникПеріодДнів№ листкаДо виплатиСтатус
{p?.initials}
{p?.name}
{window.formatDate(s.from)} – {window.formatDate(s.to)} {s.days} {s.certNumber} {window.formatMoney(s.payable)} ₴ {s.paidDate ? сплачено : до виплати }
); } // ============ 13-ТА І РІЧНІ ============ function BonusPayPage({ role }) { const team = window.DATA.TEAM; return (

13-та зарплата і річні бонуси

фінансові пакети наприкінці року правила задає директор · KPI-залежні
Бонусний пул 2026
{window.formatMoney(420000)}
~15% від чистого прибутку
Дата виплати
20.12
перед Новим роком
Учасників
{team.filter(p => p.employment !== "contractor").length}
штат і часткові
{team.filter(p => p.employment !== "contractor").map(p => { const base = p.baseSalary * (p.partRate || 1); const kpi = 0.85 + Math.random() * 0.3; const thirteenth = Math.round(base); const bonus = Math.round(base * kpi); return ( ); })}
ПрацівникБазовий окладKPI коефіцієнт13-таРічний бонусРазом
{p.initials}
{p.name}
{window.formatMoney(base)} ₴ ×{kpi.toFixed(2)} {window.formatMoney(thirteenth)} ₴ {window.formatMoney(bonus)} ₴ {window.formatMoney(thirteenth + bonus)} ₴
); } // ============ ІСТОРІЯ ВИПЛАТ ============ function SalaryHistoryPage({ role }) { const team = window.DATA.TEAM; const months = ["Грудень 2025", "Січень 2026", "Лютий 2026", "Березень 2026", "Квітень 2026"]; return (

Історія виплат

усі ЗП-виплати по людях зберігається назавжди
{months.map(m => )} {team.map(p => { const monthly = p.baseSalary * (p.partRate || 1); const total = monthly * 5; return ( {months.map(m => )} ); })}
Працівник{m}Усього
{p.initials}
{p.name}
{window.formatMoney(Math.round(monthly * (0.95 + Math.random() * 0.1)))}{window.formatMoney(total)} ₴
); } // ============ ЖУРНАЛ АВТ. НАГЛЯДУ ============ function SupervisionJournalPage({ role }) { const entries = [ { date: "2026-05-26", obj: "ukp-89", who: "ok", title: "Перевірка вузлів примикання покрівлі", photos: 8, remarks: 2 }, { date: "2026-04-15", obj: "ukp-89", who: "ok", title: "Контроль монтажу вентиляції", photos: 12, remarks: 0 }, { date: "2026-03-22", obj: "ukp-89", who: "ap", title: "Зварювальні роботи каркасу", photos: 15, remarks: 1 }, { date: "2026-02-10", obj: "ukp-89", who: "ok", title: "Бетонування плит перекриття", photos: 22, remarks: 0 }, { date: "2026-01-18", obj: "ukp-89", who: "ap", title: "Армування фундаментів", photos: 18, remarks: 3 }, ]; return (

Журнал авторського нагляду

записи виїздів з фотофіксацією і зауваженнями
Як працює: кожен виїзд на майданчик автоматично створює запис з фото з телефону, текстовими нотатками і списком зауважень. Юридично значимо — підписується КЕП.
{entries.map((e, i) => { const obj = window.getObject(e.obj); const p = window.getTeam(e.who); return (
{obj?.code} АН
{window.formatDate(e.date)}
{e.title}
{obj?.name} {p?.name}
фото
{e.photos}
зауваження
0 ? "var(--late)" : "var(--c-green-deep)", fontSize: 14, fontWeight: 600}}>{e.remarks > 0 ? `${e.remarks}` : "немає"}
); })}
); } // ============ АКТИ ВИКОНАНИХ РОБІТ ============ function CompletionActsPage({ role }) { const [printAct, setPrintAct] = React.useState(null); const acts = [ { num: "47-АКТ-03", date: "2026-05-22", obj: "ukp-47", stage: "Здача робочого проєкту, етап 1", amount: 1260000, status: "signed", signedDate: "2026-05-24" }, { num: "32-АКТ-01", date: "2025-11-15", obj: "ukp-32", stage: "Аванс після тендеру", amount: 930000, status: "signed", signedDate: "2025-11-18" }, { num: "51-АКТ-02", date: "2026-04-18", obj: "ukp-51", stage: "Ескізний проєкт", amount: 425000, status: "signed", signedDate: "2026-04-20" }, { num: "47-АКТ-04", date: "2026-05-25", obj: "ukp-47", stage: "Здача КЖ", amount: 840000, status: "draft" }, { num: "89-АКТ-04", date: "2026-05-26", obj: "ukp-89", stage: "Авт. нагляд (травень)", amount: 40000, status: "pending" }, ]; return (

Акти виконаних робіт

{acts.length} актів {acts.filter(a => a.status === "signed").length} підписано {acts.filter(a => a.status === "pending" || a.status === "draft").length} в процесі
{acts.map(a => { const obj = window.getObject(a.obj); const tone = a.status === "signed" ? "live" : a.status === "pending" ? "warn" : "neutral"; const label = a.status === "signed" ? "Підписано" : a.status === "pending" ? "Очікує підпису" : "Чернетка"; return ( ); })}
№ актаОб'єктЕтап / роботаДатаСумаСтатус
{a.num}
{obj?.name}
{obj?.code}
{a.stage} {window.formatDate(a.date)} {window.formatMoney(a.amount)} ₴ {label} {a.signedDate &&
{window.formatDate(a.signedDate)}
}
{printAct && setPrintAct(null)} />}
); } // ============ ТЕХНІЧНІ УМОВИ ============ function TuPage({ role }) { const tu = []; // демо-приклади прибрано — реальні ТУ заводитимуться пізніше return (

Технічні умови (ТУ)

{tu.length} запитів {tu.filter(t => t.status === "received").length} отримано {tu.filter(t => t.status === "pending" || t.status === "in_progress").length} в процесі
{tu.map((t, i) => { const obj = window.getObject(t.obj); const tone = t.status === "received" ? "live" : t.status === "pending" ? "warn" : "neutral"; const label = t.status === "received" ? "Отримано" : t.status === "pending" ? "Простій" : "В процесі"; return ( ); })}
Тип ТУОб'єктОрганДата запитуДата отриманняСтатус
{t.type}
{t.number &&
{t.number}
}
{obj?.code}
{obj?.name}
{t.organization} {window.formatDate(t.requestDate)} {t.receivedDate ? window.formatDate(t.receivedDate) : "—"} {label}{t.note &&
{t.note}
}
); } // ============ ДСНС / УЗГОДЖЕННЯ ============ function DsnsPage({ role }) { const approvals = []; // демо-приклади прибрано — реальні узгодження заводитимуться пізніше return (

Узгодження з органами

ДСНС · СЕС · архітектура · сільради {approvals.filter(a => a.status === "approved").length} погоджено · {approvals.filter(a => a.status !== "approved").length} в процесі
{approvals.map((a, i) => { const obj = window.getObject(a.obj); const tone = a.status === "approved" ? "live" : a.status === "pending" ? "warn" : "neutral"; const label = a.status === "approved" ? "Узгоджено" : a.status === "pending" ? "Простій" : "В процесі"; return ( ); })}
Об'єктОрганПоданоВідповідьСтатус
{obj?.name}
{obj?.code}
{a.body}
{a.number &&
{a.number}
}
{window.formatDate(a.submittedDate)} {a.responseDate ? window.formatDate(a.responseDate) : "—"} {label}{a.note &&
{a.note}
}
); } // ============ КЕП-СЕРТИФІКАТИ ============ function KepPage({ role }) { const certs = window.DATA.TEAM .filter(p => p.kep) .map(p => ({ person: p, ...p.kep, st: window.kepStatus(p.kep.validUntil) })) .sort((a, b) => a.validUntil.localeCompare(b.validUntil)); const active = certs.filter(c => c.st.state === "active").length; const expiring = certs.filter(c => c.st.state === "expiring").length; const expired = certs.filter(c => c.st.state === "expired").length; return (

КЕП-сертифікати команди

{active} активних {expiring > 0 && <>{expiring} завершуються} {expired > 0 && <>{expired} прострочено}
Чому критично: КЕП потрібен для підписання проєктної документації, договорів, подачі в ЄДЕССБ. Прострочений КЕП = неможливість працювати. Сповіщення за 30 днів до завершення. Дані синхронізовані з профілями у розділі «Команда».
{certs.map((c, i) => { const p = c.person; const danger = c.st.state !== "active"; return ( ); })}
ФахівецьТип КЕППровайдерДійсний доЗалишокСтатус
{p?.initials}
{p?.name}
{p?.role}
{c.type} {c.provider} {window.formatDate(c.validUntil)} {c.st.state === "expired" ? `прострочено ${Math.abs(c.st.days)} дн.` : `${c.st.days} дн.`} {c.st.label}
); } window.GanttPage = GanttPage; window.ArchivePage = ArchivePage; window.MaterialsPage = MaterialsPage; window.AdvancePayPage = AdvancePayPage; window.SickPayPage = SickPayPage; window.BonusPayPage = BonusPayPage; window.SalaryHistoryPage = SalaryHistoryPage; window.SupervisionJournalPage = SupervisionJournalPage; window.CompletionActsPage = CompletionActsPage; window.TuPage = TuPage; window.DsnsPage = DsnsPage; window.KepPage = KepPage;