// Employees — real staff directory (window.PEOPLE / API) with their поручения load. function EmployeesPage({ onOpenOrders }) { const [people, setPeople] = React.useState({}); const [tasks, setTasks] = React.useState([]); const [filter, setFilter] = React.useState("Все"); const [addOpen, setAddOpen] = React.useState(false); const [loading, setLoading] = React.useState(true); const load = React.useCallback(() => { if (!window.API) { setLoading(false); return; } setLoading(true); Promise.all([ window.API.people().catch(() => ({})), window.API.listTasks().catch(() => []), ]).then(([p, t]) => { setPeople(p || {}); setTasks(Array.isArray(t) ? t : []); }).finally(() => setLoading(false)); }, []); React.useEffect(() => { load(); }, [load]); const isAdmin = window.ME && window.ME.role === "admin"; const setActive = async (name, active) => { const verb = active ? "Восстановить" : "Уволить"; if (!window.confirm(`${verb} сотрудника «${name}»?`)) return; try { if (active) await window.API.restorePerson(name); else await window.API.dismissPerson(name); load(); if (window.refreshDirectory) window.refreshDirectory(); } catch (e) { alert(String((e && e.message) || e)); } }; const eff = (t) => (typeof effStatus === "function" ? effStatus(t) : t.status); const depts = (typeof DEPARTMENTS !== "undefined") ? DEPARTMENTS : []; const filters = ["Все", ...depts]; const rows = Object.keys(people).map(name => { const p = people[name] || {}; const mine = tasks.filter(t => t.assignee === name); const total = mine.length; const done = mine.filter(t => eff(t) === "done").length; const overdue = mine.filter(t => eff(t) === "overdue").length; const pct = total ? Math.round(done / total * 100) : 0; return { name, dept: p.dept || "—", tg: p.tg || "", color: p.color, total, done, overdue, pct, active: p.active !== false }; }).filter(r => filter === "Все" || r.dept === filter) .sort((a, b) => (a.active === b.active ? b.total - a.total : (a.active ? -1 : 1))); const exportCSV = () => { const header = ["ФИО", "Отдел", "Telegram", "Всего поручений", "Выполнено", "Просрочено"]; const esc = v => `"${String(v == null ? "" : v).replace(/"/g, '""')}"`; const lines = [header.map(esc).join(";")]; rows.forEach(r => lines.push([r.name, r.dept, r.tg, r.total, r.done, r.overdue].map(esc).join(";"))); const blob = new Blob(["" + lines.join("\r\n")], { type: "text/csv;charset=utf-8" }); const a = document.createElement("a"); a.href = URL.createObjectURL(blob); a.download = "Сотрудники.csv"; a.click(); }; return (
{Object.keys(people).length} человек · {depts.length} отделов{loading ? " · загрузка…" : ""}
Добавится в справочник и станет доступен как ответственный