// private-tasks.jsx — ПРИВАТНІ ЗАДАЧІ ДИРЕКТОРА // ─────────────────────────────────────────────────────────────────────────── // Задачі з прапорцем private:true бачить ЛИШЕ роль "director" (у live-режимі — // лише фактичний директор). Перша категорія — автонагадування про дні народження // команди (генеруються з TEAM.birthDate). Директор позначає «привітано» — і це // зберігається між перезавантаженнями (localStorage, лише demo-режим). // ─────────────────────────────────────────────────────────────────────────── (function () { const SYS = window.SYS_DATE || "2026-05-29"; const MONTHS_GEN = ["січня", "лютого", "березня", "квітня", "травня", "червня", "липня", "серпня", "вересня", "жовтня", "листопада", "грудня"]; // Наступний день народження відносно дати ref (ISO). Повертає { iso, days, label, age }. function nextBirthday(birthISO, refISO) { if (!birthISO) return null; const ref = new Date((refISO || SYS) + "T00:00:00"); const [by, bm, bd] = birthISO.split("-").map(Number); let year = ref.getFullYear(); let next = new Date(year, bm - 1, bd); if (next < new Date(ref.getFullYear(), ref.getMonth(), ref.getDate())) { year += 1; next = new Date(year, bm - 1, bd); } const days = Math.round((next - new Date(ref.getFullYear(), ref.getMonth(), ref.getDate())) / 86400000); const iso = `${year}-${String(bm).padStart(2, "0")}-${String(bd).padStart(2, "0")}`; return { iso, days, label: `${bd} ${MONTHS_GEN[bm - 1]}`, age: year - by }; } window.nextBirthday = nextBirthday; // Список найближчих днів народження (відсортований за близькістю). window.upcomingBirthdays = function (refISO, withinDays) { return (window.DATA.TEAM || []) .map(p => { const nb = nextBirthday(p.birthDate, refISO); return nb ? { person: p, ...nb } : null; }) .filter(Boolean) .filter(x => withinDays == null || x.days <= withinDays) .sort((a, b) => a.days - b.days); }; // Видимість задач за роллю: приватні бачить лише директор. window.visibleTasks = function (list, role) { if (role === "director") return list || []; return (list || []).filter(t => !t.private); }; // Збережені «привітано» (localStorage, demo). Ключ — id особи + рік. const LS_KEY = "ubp.bdGreeted.v1"; function loadGreeted() { try { return JSON.parse(localStorage.getItem(LS_KEY) || "{}"); } catch (_) { return {}; } } function saveGreeted(map) { try { localStorage.setItem(LS_KEY, JSON.stringify(map)); } catch (_) {} } window.bdGreetedKey = (personId, iso) => `${personId}@${(iso || "").slice(0, 4)}`; window.isBirthdayGreeted = (personId, iso) => !!loadGreeted()[window.bdGreetedKey(personId, iso)]; window.setBirthdayGreeted = (personId, iso, val) => { const m = loadGreeted(); const k = window.bdGreetedKey(personId, iso); if (val) m[k] = SYS; else delete m[k]; saveGreeted(m); }; // Генеруємо приватні задачі-нагадування для ДН у вікні 35 днів. function genBirthdayTasks() { return window.upcomingBirthdays(SYS, 35).map(u => ({ id: "bd-" + u.person.id, title: `Привітати з днем народження: ${u.person.name}`, obj: null, stage: "admin", section: "Особисте", assignee: "ok", private: true, owner: "ok", kind: "birthday", bdPersonId: u.person.id, bdISO: u.iso, day: String(new Date(u.iso + "T00:00:00").getDate()), dueDate: u.iso, est: "—", status: window.isBirthdayGreeted(u.person.id, u.iso) ? "done" : "todo", note: `${u.person.name} святкує день народження ${u.label} (виповнюється ${u.age}). Особисте нагадування директора — бачите лише ви.`, })); } // Додаємо у DATA.TASKS один раз (нормалізовано). if (window.DATA && Array.isArray(window.DATA.TASKS) && window.normalizeTask) { const bd = genBirthdayTasks().map(window.normalizeTask); const existing = new Set(window.DATA.TASKS.map(t => t.id)); window.DATA.TASKS = [...window.DATA.TASKS, ...bd.filter(t => !existing.has(t.id))]; } // ── Віджет дашборду (director-only): найближчі дні народження команди ── function BirthdayCard() { const [, force] = React.useState(0); const ups = window.upcomingBirthdays(SYS, 120).slice(0, 5); const greet = (u) => { const now = !window.isBirthdayGreeted(u.person.id, u.iso); window.setBirthdayGreeted(u.person.id, u.iso, now); if (window.__erpToggleTask) window.__erpToggleTask("bd-" + u.person.id); force(x => x + 1); }; return (

Особисті нагадування

тільки ви
{ups.map(u => { const greeted = window.isBirthdayGreeted(u.person.id, u.iso); const soon = u.days <= 7; return (
{u.person.initials}
{u.person.name}
{u.label} · {u.days === 0 ? "сьогодні" : u.days === 1 ? "завтра" : `через ${u.days} дн`} · виповнюється {u.age}
); })} {ups.length === 0 &&
Найближчим часом днів народження немає.
}
Приватний блок директора. Ці нагадування також лежать у задачах (позначені «Приватне») і видні лише вам.
); } window.BirthdayCard = BirthdayCard; })();