// Хакатон Казахтелеком — демо-логика (localStorage, без сервера) const SEED_PARTICIPANTS = [ { id: "p1", name: "Айгерим Нурлан", email: "aigerim.nur@example.kz", phone: "+7 777 145 20 18", city: "Астана", direction: "Frontend", team: "solo" }, { id: "p2", name: "Дмитрий Козлов", email: "d.kozlov@example.com", phone: "+7 701 233 90 41", city: "Алматы", direction: "Backend", team: "team" }, { id: "p3", name: "Жанара Сәтбаева", email: "janara.sat@example.kz", phone: "+7 747 882 15 07", city: "Караганда", direction: "Data Science", team: "solo" }, { id: "p4", name: "Ержан Алиев", email: "yerzhan.aliev@example.kz", phone: "+7 705 641 33 82", city: "Астана", direction: "Frontend", team: "team" }, { id: "p5", name: "Марат Суюнбеков", email: "m.suyunbekov@example.kz", phone: "+7 708 402 77 51", city: "Алматы", direction: "Backend", team: "solo" }, { id: "p6", name: "Айлин Керимова", email: "ailin.kerimova@example.kz", phone: "+7 745 918 26 44", city: "Астана", direction: "Data Science", team: "team" }, { id: "p7", name: "Тимур Оспанов", email: "t.ospanov@example.kz", phone: "+7 702 550 19 73", city: "Шымкент", direction: "Frontend", team: "solo" } ]; function loadParticipants() { try { const raw = localStorage.getItem("kt_hackathon_v1"); const arr = raw ? JSON.parse(raw) : null; if (Array.isArray(arr) && arr.length) return arr; } catch (e) { /* повреждённые данные — пересеваем */ } localStorage.setItem("kt_hackathon_v1", JSON.stringify(SEED_PARTICIPANTS)); return SEED_PARTICIPANTS.slice(); } function saveParticipants(list) { localStorage.setItem("kt_hackathon_v1", JSON.stringify(list)); } function esc(s) { return String(s).replace(/[&<>"']/g, c => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c])); } const DIR_LABELS = { "Frontend": "Frontend", "Backend": "Backend", "Data Science": "Data Science" }; const DIR_CLASS = { "Frontend": "frontend", "Backend": "backend", "Data Science": "data" }; /* ================= Главный экран: статистика ================= */ function renderStats() { const el = document.getElementById("stats"); if (!el) return; const total = loadParticipants().length; const items = [ { icon: "calendar", value: "28.09.2026", label: "Дата проведения", hint: "суббота" }, { icon: "clock", value: "10:00", label: "Начало", hint: "длительность 5 часов" }, { icon: "building", value: "ул. Сауран, 12", label: "Место проведения", hint: "Казахстан" }, { icon: "users", value: String(total), label: "Участников", hint: "регистрация открыта" } ]; el.innerHTML = items.map(k => '
' + '' + esc(k.value) + '' + '' + esc(k.label) + '' + '' + esc(k.hint) + '' + '
' ).join(""); } /* ================= Форма регистрации ================= */ function initRegister() { const form = document.getElementById("reg-form"); if (!form) return; function setError(key, msg) { const el = form.querySelector('[data-err="' + key + '"]'); if (el) el.textContent = msg || ""; } form.addEventListener("submit", function (ev) { ev.preventDefault(); const data = { name: document.getElementById("f-name").value.trim(), email: document.getElementById("f-email").value.trim(), phone: document.getElementById("f-phone").value.trim(), city: document.getElementById("f-city").value.trim() || "—", direction: document.getElementById("f-dir").value, team: document.getElementById("f-team").value, consent: document.getElementById("f-consent").checked }; const errors = {}; if (data.name.length < 5) errors.name = "Укажите фамилию и имя полностью"; if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(data.email)) errors.email = "Укажите корректный email"; if (!/^[+\d][\d\s()-]{9,}$/.test(data.phone)) errors.phone = "Укажите телефон, например +7 700 555 12 34"; if (!data.direction) errors.dir = "Выберите направление"; if (!data.team) errors.team = "Выберите формат участия"; if (!data.consent) errors.consent = "Без согласия мы не можем отправить подтверждение"; Object.keys({ name: 1, email: 1, phone: 1, dir: 1, team: 1, consent: 1 }).forEach(k => setError(k, errors[k])); if (Object.keys(errors).length) return; const list = loadParticipants(); if (!list.some(p => p.email.toLowerCase() === data.email.toLowerCase())) { list.unshift({ id: "r" + Date.now(), name: data.name, email: data.email, phone: data.phone, city: data.city, direction: data.direction, team: data.team }); saveParticipants(list); } const succ = document.getElementById("reg-succ"); document.getElementById("reg-succ-text").textContent = "Подтверждение отправим на " + data.email + " накануне мероприятия."; form.hidden = true; succ.hidden = false; succ.scrollIntoView({ behavior: "smooth", block: "center" }); }); } /* ================= Список участников ================= */ let partState = { q: "", dir: "all" }; function initParticipants() { const tbody = document.getElementById("part-tbody"); if (!tbody) return; const searchField = document.getElementById("p-search"); const chips = document.getElementById("dir-chips"); const empty = document.getElementById("part-empty"); const strip = document.getElementById("kpi-strip"); function kpiHtml(v, l, h) { return '
' + esc(v) + '' + esc(l) + '' + esc(h) + '
'; } function render() { const all = loadParticipants(); // KPI: считаются по текущему фильтру направления const base = partState.dir === "all" ? all : all.filter(p => p.direction === partState.dir); const total = base.length; const solo = base.filter(p => p.team === "solo").length; const cities = new Set(base.map(p => p.city)).size; strip.innerHTML = kpiHtml(String(total), "Участников", partState.dir === "all" ? "всего" : "по направлению") + kpiHtml(String(solo), "Индивидуально", "остальные — с командами") + kpiHtml(String(cities), "Городов", "представлены"); // чипы направлений const dirs = ["all", "Frontend", "Backend", "Data Science"]; chips.innerHTML = dirs.map(d => { const active = partState.dir === d; const n = d === "all" ? all.length : all.filter(p => p.direction === d).length; const label = d === "all" ? "Все" : DIR_LABELS[d]; return ''; }).join(""); // строки const q = partState.q.toLowerCase(); const rows = all.filter(p => (partState.dir === "all" || p.direction === partState.dir) && (!q || p.name.toLowerCase().includes(q) || p.city.toLowerCase().includes(q) || p.email.toLowerCase().includes(q)) ); tbody.innerHTML = rows.map(p => "" + "" + esc(p.name) + '' + esc(p.email) + "" + "" + esc(p.city) + "" + '' + esc(DIR_LABELS[p.direction] || p.direction) + "" + '' + (p.team === "team" ? "команда" : "соло") + "" + "" ).join(""); empty.hidden = rows.length > 0; } chips.addEventListener("click", ev => { const btn = ev.target.closest("[data-dir]"); if (!btn) return; partState.dir = btn.getAttribute("data-dir"); render(); }); searchField.addEventListener("input", () => { partState.q = searchField.value.trim(); render(); }); render(); } /* ================= Старт ================= */ function init() { if (document.getElementById("reg-form")) { if (new URLSearchParams(location.search).has("registered")) { const succ = document.getElementById("reg-succ"); const form = document.getElementById("reg-form"); document.getElementById("reg-succ-text").textContent = "Подтверждение отправим на почту накануне мероприятия."; form.hidden = true; succ.hidden = false; } } renderStats(); initRegister(); initParticipants(); } document.addEventListener("DOMContentLoaded", init);