const STORAGE_KEY = "kindergarten_crm_children_v1"; const STATUS_LABELS = { studying: "Учится", sick: "Больничный", vacation: "Отпуск" }; const PAYMENT_LABELS = { paid: "Оплачено", current: "Актуально", overdue: "Долг" }; const DEMO_DATA = [ { id: 1, name: "Айгерим Ахметов", group: "Солнышко", birth: "2021-04-12", parent: "Ахметова Л.К.", phone: "+7 700 111 22 33", status: "studying", payment: "paid", note: "Аллергия на цитрусовые" }, { id: 2, name: "Бекзат Сауыт", group: "Солнышко", birth: "2021-07-30", parent: "Сауытов Д.Б.", phone: "+7 701 222 33 44", status: "sick", payment: "current", note: "Частые ОРВИ, нужен щадящий режим" }, { id: 3, name: "Виктория Мельник", group: "Любимчики", birth: "2020-01-25", parent: "Мельник А.С.", phone: "+7 702 333 44 55", status: "studying", payment: "overdue", note: "Долг за сентябрь" }, { id: 4, name: "Гульнара Сейитова", group: "Любимчики", birth: "2020-09-08", parent: "Сейитова Н.Ж.", phone: "+7 703 444 55 66", status: "vacation", payment: "paid", note: "Отпуск до 15 ноября" }, { id: 5, name: "Дамир Тлеуов", group: "Ромашки", birth: "2019-11-17", parent: "Тлеуов Б.С.", phone: "+7 704 555 66 77", status: "studying", payment: "paid", note: "" }, { id: 6, name: "Ева Соколова", group: "Ромашки", birth: "2019-03-04", parent: "Соколова И.А.", phone: "+7 705 666 77 88", status: "studying", payment: "current", note: "Нужен контроль приёма лекарств в 14:00" }, { id: 7, name: "Жанибек Нурланов", group: "Солнышко", birth: "2021-12-21", parent: "Нурланова С.Б.", phone: "+7 706 777 88 99", status: "studying", payment: "paid", note: "" }, { id: 8, name: "Катя Иванова", group: "Любимчики", birth: "2020-06-14", parent: "Иванов П.И.", phone: "+7 707 888 99 00", status: "sick", payment: "paid", note: "Больничный до 25 сентября" } ]; let children = []; let activeStatusFilter = "all"; let searchQuery = ""; function loadChildren() { try { const raw = localStorage.getItem(STORAGE_KEY); if (raw) { const parsed = JSON.parse(raw); if (Array.isArray(parsed) && parsed.length > 0) { children = parsed; return; } } } catch (e) { console.warn("Не удалось загрузить данные, используется демо", e); } children = JSON.parse(JSON.stringify(DEMO_DATA)); saveChildren(); } function saveChildren() { localStorage.setItem(STORAGE_KEY, JSON.stringify(children)); } function getFilteredChildren() { const q = searchQuery.toLowerCase().trim(); return children.filter((c) => { const matchesStatus = activeStatusFilter === "all" || c.status === activeStatusFilter; if (!matchesStatus) return false; if (!q) return true; const haystack = [c.name, c.group, c.parent, c.phone, c.note].filter(Boolean).join(" ").toLowerCase(); return haystack.includes(q); }); } function formatDate(iso) { if (!iso) return ""; const [y, m, d] = iso.split("-"); return `${d}.${m}.${y}`; } function renderKpis() { const total = children.length; const studying = children.filter((c) => c.status === "studying").length; const sick = children.filter((c) => c.status === "sick").length; const overdue = children.filter((c) => c.payment === "overdue").length; const attendancePct = total ? Math.round((studying / total) * 100) : 0; document.getElementById("kpis").innerHTML = `
Всего детей ${total} в саду
Сегодня в группе ${studying} ${attendancePct}% посещаемость
На больничном ${sick} отсутствуют
Долги по оплате ${overdue} запросить родителю
`; } function renderFilters() { const statuses = ["all", "studying", "sick", "vacation"]; const labels = { all: "Все", studying: "Учатся", sick: "Больничный", vacation: "Отпуск" }; document.getElementById("status-filters").innerHTML = statuses .map((s) => { const count = s === "all" ? children.length : children.filter((c) => c.status === s).length; const active = activeStatusFilter === s ? "active" : ""; return ``; }) .join(""); document.getElementById("status-filters").querySelectorAll("[data-status]").forEach((btn) => { btn.addEventListener("click", () => { activeStatusFilter = btn.dataset.status; renderAll(); }); }); } function renderTable() { const list = getFilteredChildren(); const tbody = document.getElementById("tbody"); const empty = document.getElementById("empty"); if (list.length === 0) { tbody.innerHTML = ""; empty.hidden = false; } else { empty.hidden = true; tbody.innerHTML = list .map((c) => { const birthDate = c.birth ? formatDate(c.birth) : ""; return `
${c.name}
${c.birth ? `
р. ${birthDate}
` : ""} ${c.group}
${c.parent || ""}
${c.phone ? `
${c.phone}
` : ""} ${STATUS_LABELS[c.status] || c.status} ${PAYMENT_LABELS[c.payment] || c.payment}
`; }) .join(""); } document.getElementById("count-label").textContent = `Показано ${list.length} из ${children.length}`; tbody.querySelectorAll("[data-action]").forEach((btn) => { const id = Number(btn.closest("tr").dataset.id); btn.addEventListener("click", () => { if (btn.dataset.action === "edit") openDrawer(id); if (btn.dataset.action === "delete") removeChild(id); }); }); } function renderAll() { renderKpis(); renderFilters(); renderTable(); } function openDrawer(id = null) { const drawer = document.getElementById("drawer"); const title = document.getElementById("drawer-title"); const form = document.getElementById("child-form"); form.reset(); document.getElementById("field-id").value = ""; if (id !== null) { const child = children.find((c) => c.id === id); if (!child) return; title.textContent = "Ребёнок"; document.getElementById("field-id").value = child.id; document.getElementById("field-name").value = child.name; document.getElementById("field-group").value = child.group; document.getElementById("field-birth").value = child.birth || ""; document.getElementById("field-parent").value = child.parent || ""; document.getElementById("field-phone").value = child.phone || ""; document.getElementById("field-status").value = child.status; document.getElementById("field-payment").value = child.payment; document.getElementById("field-note").value = child.note || ""; } else { title.textContent = "Новый ребёнок"; } drawer.hidden = false; document.getElementById("field-name").focus(); } function closeDrawer() { document.getElementById("drawer").hidden = true; } function saveChild(e) { e.preventDefault(); const id = document.getElementById("field-id").value; const data = { name: document.getElementById("field-name").value.trim(), group: document.getElementById("field-group").value, birth: document.getElementById("field-birth").value, parent: document.getElementById("field-parent").value.trim(), phone: document.getElementById("field-phone").value.trim(), status: document.getElementById("field-status").value, payment: document.getElementById("field-payment").value, note: document.getElementById("field-note").value.trim() }; if (!data.name) return; if (id) { const idx = children.findIndex((c) => c.id === Number(id)); if (idx !== -1) { children[idx] = { ...children[idx], ...data }; showToast("Данные обновлены"); } } else { children.push({ id: Date.now(), ...data }); showToast("Ребёнок добавлен"); } saveChildren(); renderAll(); closeDrawer(); } function removeChild(id) { const child = children.find((c) => c.id === id); if (!child) return; if (!confirm(`Удалить ${child.name}?`)) return; children = children.filter((c) => c.id !== id); saveChildren(); renderAll(); showToast("Запись удалена"); } function exportCsv() { const list = getFilteredChildren(); if (list.length === 0) { showToast("Нет данных для выгрузки"); return; } const head = ["Имя ребёнка", "Группа", "Дата рождения", "Родитель", "Телефон", "Статус", "Оплата", "Заметка"]; const body = list.map((c) => [ c.name, c.group, c.birth ? formatDate(c.birth) : "", c.parent, c.phone, STATUS_LABELS[c.status] || c.status, PAYMENT_LABELS[c.payment] || c.payment, c.note ] .map((v) => '"' + String(v || "").replace(/"/g, '""') + '"') .join(";") ); const csv = "\uFEFF" + [head.join(";"), ...body].join("\n"); const a = document.createElement("a"); a.href = URL.createObjectURL(new Blob([csv], { type: "text/csv" })); a.download = "deti-detsad.csv"; a.click(); showToast("Файл сформирован"); } function showToast(message) { const stack = document.getElementById("toasts"); const el = document.createElement("div"); el.className = "kt-ai-toast"; el.textContent = message; stack.appendChild(el); setTimeout(() => el.remove(), 2600); } function init() { loadChildren(); renderAll(); document.getElementById("btn-new").addEventListener("click", () => openDrawer()); document.getElementById("btn-drawer-close").addEventListener("click", closeDrawer); document.getElementById("btn-cancel").addEventListener("click", closeDrawer); document.getElementById("child-form").addEventListener("submit", saveChild); document.getElementById("btn-export").addEventListener("click", exportCsv); document.getElementById("btn-clear-filters").addEventListener("click", () => { activeStatusFilter = "all"; searchQuery = ""; document.getElementById("search").value = ""; renderAll(); }); document.getElementById("search").addEventListener("input", (e) => { searchQuery = e.target.value; renderTable(); }); } document.addEventListener("DOMContentLoaded", init);