diff --git a/index.html b/index.html new file mode 100644 index 0000000..0a5ea92 --- /dev/null +++ b/index.html @@ -0,0 +1,289 @@ + + + + + + +Тимбилдинг ДЦБ Астана — регистрация + + + + + + + + + + + + + + +
+ +
+
+ + + Тимбилдинг ДЦБ Астана + + + Регистрация +
+
+ +
+
+ 18 сентября · суббота +

Тимбилдинг ДЦБ Астана

+

День вне привычной рабочей обстановки: командные активности, общение, отдых — и просто классное время вместе. Место: зона отдыха «Лазурный берег», Косшы.

+ +
+
+ +
+
+
+

Как всё будет проходить

+

Три остановки — офис, дорога, база. Всё остальное позади.

+
+
+
+ +

08:30

+

Приходим на работу как обычно — БЦ «Асылтау».

+
+
+ +

10:30

+

Выезжаем от БЦ «Асылтау», ул. Алматы, 1.

+
+
+ +

День на базе

+

Зона отдыха «Лазурный берег», г. Косшы, ул. Айтебе би, 32.

+
+
+ +

18:00

+

Обратный выезд с базы отдыха — ориентировочно.

+
+
+
+

Берите с собой хорошее настроение — остальное организуем на месте.

+
+
+
+ +
+
+
+

Регистрация

+

Оставьте контакты — и вы в списке участников. Подтверждение появится у администратора мероприятия.

+
+
+
+
+ + +
+
+ + +
+
+ + + Номер для связи в день мероприятия. +
+ +
+ +
+
+
+ + + +
+ + + + + + + +
+ + + + + diff --git a/script.js b/script.js new file mode 100644 index 0000000..4c1cf49 --- /dev/null +++ b/script.js @@ -0,0 +1,323 @@ +/* Тимбилдинг ДЦБ Астана — регистрация участников и список администратора. + Данные живут в localStorage (общий хранилище страницы); при первом заходе + список наполняется примерами участников для показа структуры. */ +(function () { + "use strict"; + + var LS_KEY = "teambuilding_dcb_participants"; + + var STATUS = { + waiting: { label: "Ждёт подтверждения", tone: "warn" }, + confirmed: { label: "Подтверждён", tone: "ok" } + }; + + var SEED = [ + { id: 1, name: "Данияр", lastname: "Орынбаев", phone: "+7 705 214 88 30", status: "confirmed", date: "08.09.2026" }, + { id: 2, name: "Айгерим", lastname: "Сатыбаева", phone: "+7 702 551 40 12", status: "confirmed", date: "09.09.2026" }, + { id: 3, name: "Ержан", lastname: "Абдрахимов", phone: "+7 771 902 33 56", status: "waiting", date: "09.09.2026" }, + { id: 4, name: "Гульнара", lastname: "Касымжанова", phone: "+7 747 635 21 08", status: "confirmed", date: "10.09.2026" }, + { id: 5, name: "Мадина", lastname: "Турсынбаева", phone: "+7 708 442 17 65", status: "confirmed", date: "11.09.2026" }, + { id: 6, name: "Асхат", lastname: "Жаманкулов", phone: "+7 700 118 92 44", status: "waiting", date: "12.09.2026" }, + { id: 7, name: "Сейіл", lastname: "Кенжарик", phone: "+7 748 276 50 91", status: "confirmed", date: "13.09.2026" }, + { id: 8, name: "Жанна", lastname: "Мырзахметова", phone: "+7 706 390 77 14", status: "confirmed", date: "15.09.2026" }, + { id: 9, name: "Бахыт", lastname: "Айтимов", phone: "+7 775 813 26 40", status: "confirmed", date: "15.09.2026" }, + { id: 10, name: "Алия", lastname: "Исмухамбетова", phone: "+7 707 564 38 29", status: "waiting", date: "16.09.2026" }, + { id: 11, name: "Нурлан", lastname: "Сейткалиев", phone: "+7 702 837 15 73", status: "confirmed", date: "16.09.2026" }, + { id: 12, name: "Карлыгаш", lastname: "Ералиева", phone: "+7 749 620 84 57", status: "waiting", date: "17.09.2026" } + ]; + + /* ── Хранилище ── */ + function load() { + try { + var raw = localStorage.getItem(LS_KEY); + if (raw) { + var arr = JSON.parse(raw); + if (Array.isArray(arr)) return arr; + } + } catch (e) { /* повреждённые данные — пересеять */ } + localStorage.setItem(LS_KEY, JSON.stringify(SEED)); + return SEED.slice(); + } + function save(list) { + try { localStorage.setItem(LS_KEY, JSON.stringify(list)); } catch (e) {} + } + + var participants = load(); + var nextId = 1; + for (var i = 0; i < participants.length; i++) { + if (participants[i].id >= nextId) nextId = participants[i].id + 1; + } + + /* ── Хелперы ── */ + function $(sel, root) { return (root || document).querySelector(sel); } + function esc(s) { + return String(s == null ? "" : s).replace(/[&<>"']/g, function (c) { + return { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c]; + }); + } + function todayStr() { + var d = new Date(); + var m = ("0" + (d.getMonth() + 1)).slice(-2); + var day = ("0" + d.getDate()).slice(-2); + return day + "." + m + "." + d.getFullYear(); + } + + var toastsRoot = $("#toasts"); + function toast(text, tone) { + var el = document.createElement("div"); + el.className = "kt-ai-toast"; + if (tone) el.setAttribute("data-tone", tone); + el.textContent = text; + toastsRoot.appendChild(el); + setTimeout(function () { + el.style.opacity = "0"; + el.style.transition = "opacity 200ms"; + setTimeout(function () { el.remove(); }, 220); + }, 3200); + } + + /* ── Переключение видов ── */ + function showView(name) { + var p = $("#view-participant"); + var a = $("#view-admin"); + if (name === "admin") { + p.hidden = true; + a.hidden = false; + } else { + a.hidden = true; + p.hidden = false; + } + if (name === "admin") renderAdmin(); + try { location.hash = name; } catch (e) {} + } + + /* ── Форма регистрации ── */ + var form = $("#regform"); + var done = $("#regdone"); + var fName = $("#f-name"); + var fLast = $("#f-lastname"); + var fPhone = $("#f-phone"); + + form.addEventListener("submit", function (ev) { + ev.preventDefault(); + var name = fName.value.trim(); + var lastname = fLast.value.trim(); + var phone = fPhone.value.trim(); + + if (!name) { flashField(fName, "Введите имя"); return; } + if (!lastname) { flashField(fLast, "Введите фамилию"); return; } + if (!/\+?[0-9](?:[0-9 ()\-]{6,14})/.test(phone)) { + flashField(fPhone, "Укажите телефон в формате +7 XXX XXX XX XX"); + return; + } + var dup = null; + for (var i = 0; i < participants.length; i++) { + var p = participants[i]; + var key = (p.name + " " + p.lastname).trim().replace(/\s+/g, " ").toLowerCase(); + if (key === (name + " " + lastname).replace(/\s+/g, " ").toLowerCase() && + p.phone.replace(/\D/g, "") === phone.replace(/\D/g, "")) { dup = p; break; } + } + if (dup) { + toast("Этот участник уже зарегистрирован", "warn"); + return; + } + + var rec = { + id: nextId++, + name: name, + lastname: lastname, + phone: phone, + status: "waiting", + date: todayStr() + }; + participants.unshift(rec); + save(participants); + + form.hidden = true; + done.hidden = false; + $("#regdone-text").textContent = + name + ", вы в списке участников. Администратор подтвердит участие до 18 сентября."; + toast("Регистрация принята", "ok"); + }); + + function flashField(input, msg) { + input.focus(); + toast(msg, "warn"); + } + + $("#regdone-again").addEventListener("click", function () { + form.reset(); + done.hidden = true; + form.hidden = false; + fName.focus(); + }); + + /* ── Админка ── */ + var statusFilter = "all"; + var searchQuery = ""; + var selectedId = null; + + function filtered() { + return participants.filter(function (p) { + if (statusFilter !== "all" && p.status !== statusFilter) return false; + if (searchQuery) { + var hay = (p.name + " " + p.lastname + " " + p.phone).toLowerCase(); + if (hay.indexOf(searchQuery) === -1) return false; + } + return true; + }); + } + + function statusPill(status) { + var s = STATUS[status] || STATUS.waiting; + return '' + esc(s.label) + ""; + } + + function renderAdmin() { + /* навигация-счётчик */ + $("#nav-count").textContent = participants.length; + + /* KPI */ + var total = participants.length; + var confirmed = 0, waiting = 0; + for (var i = 0; i < participants.length; i++) { + if (participants[i].status === "confirmed") confirmed++; else waiting++; + } + $("#kpi-total").textContent = total; + $("#kpi-confirmed").textContent = confirmed; + $("#kpi-waiting").textContent = waiting; + + /* фильтр-чипы */ + var chips = document.querySelectorAll(".kt-ai-filter-chip"); + for (var j = 0; j < chips.length; j++) { + chips[j].setAttribute("data-active", chips[j].getAttribute("data-status") === statusFilter ? "true" : "false"); + } + + /* таблица */ + var list = filtered(); + var tbody = $("#adm-tbody"); + var html = ""; + for (var k = 0; k < list.length; k++) { + var p = list[k]; + html += "" + + '' + esc(p.lastname) + " " + esc(p.name) + "" + + 'ДЦБ Астана' + + '' + esc(p.phone) + "" + + '' + esc(p.date) + "" + + '' + statusPill(p.status) + "" + + '' + + ""; + } + tbody.innerHTML = html; + + /* пустое состояние */ + var empty = $("#adm-empty"); + if (list.length === 0) { + empty.hidden = false; + var hasFilter = statusFilter !== "all" || searchQuery !== ""; + $("#adm-empty-title").textContent = hasFilter ? "Никого не нашлось" : "Пока никто не зарегистрировался"; + $("#adm-empty-text").textContent = hasFilter + ? "Попробуйте изменить запрос или фильтр." + : "Регистрации участников появятся здесь, как только коллеги отправят форму."; + } else { + empty.hidden = true; + } + } + + /* клики по строке и по «открыть» */ + $("#adm-tbody").addEventListener("click", function (ev) { + var tr = ev.target.closest("tr[data-id]"); + if (!tr) return; + openDrawer(parseInt(tr.getAttribute("data-id"), 10)); + }); + + /* поиск + фильтры */ + $("#adm-search").addEventListener("input", function () { + searchQuery = this.value.trim().toLowerCase(); + renderAdmin(); + }); + document.querySelectorAll(".kt-ai-filter-chip").forEach(function (chip) { + chip.addEventListener("click", function () { + statusFilter = this.getAttribute("data-status"); + renderAdmin(); + }); + }); + + /* ── Drawer ── */ + var drawer = $("#drawer"); + + function openDrawer(id) { + var p = null; + for (var i = 0; i < participants.length; i++) if (participants[i].id === id) { p = participants[i]; break; } + if (!p) return; + selectedId = id; + + $("#d-name").textContent = p.lastname + " " + p.name; + var phoneEl = $("#d-phone"); + phoneEl.textContent = p.phone; + phoneEl.toggleAttribute("data-empty", !p.phone); + $("#d-date").textContent = p.date; + $("#d-status").innerHTML = statusPill(p.status); + $("#d-confirm").hidden = p.status === "confirmed"; + $("#d-confirm").setAttribute("data-tone", p.status === "confirmed" ? "ok" : ""); + + drawer.hidden = false; + document.documentElement.classList.add("kt-ai-scroll-locked"); + renderAdmin(); + } + function closeDrawer() { + drawer.hidden = true; + selectedId = null; + document.documentElement.classList.remove("kt-ai-scroll-locked"); + renderAdmin(); + } + $(".drawer-close").addEventListener("click", closeDrawer); + $("#d-close").addEventListener("click", closeDrawer); + document.addEventListener("keydown", function (e) { + if (e.key === "Escape" && !drawer.hidden) closeDrawer(); + }); + + $("#d-confirm").addEventListener("click", function () { + var p = null; + for (var i = 0; i < participants.length; i++) if (participants[i].id === selectedId) { p = participants[i]; break; } + if (!p) return; + p.status = "confirmed"; + save(participants); + toast(p.lastname + " " + p.name + ": участие подтверждено", "ok"); + openDrawer(p.id); + }); + + $("#d-unlink").addEventListener("click", function () { + var p = null; + for (var i = 0; i < participants.length; i++) if (participants[i].id === selectedId) { p = participants[i]; break; } + if (!p) return; + var idx = participants.indexOf(p); + participants.splice(idx, 1); + save(participants); + closeDrawer(); + toast(p.lastname + " " + p.name + " убран из списка"); + }); + + /* ── Навигация ── */ + document.querySelectorAll("[data-nav]").forEach(function (el) { + el.addEventListener("click", function (ev) { + ev.preventDefault(); + showView("participant"); + var target = this.getAttribute("data-nav"); + if (target && target !== "reg") { + setTimeout(function () { + var sec = document.getElementById(target); + if (sec) sec.scrollIntoView({ behavior: "smooth", block: "start" }); + }, 60); + } else { + window.scrollTo({ top: 0, behavior: "smooth" }); + } + }); + }); + var adminLink = document.querySelector(".tb-admin-link-strong"); + if (adminLink) adminLink.addEventListener("click", function () { showView("admin"); }); + + /* стартовый вид по location.hash */ + if (location.hash === "#admin" || location.hash === "#view-admin") showView("admin"); + else showView("participant"); +})(); diff --git a/style.css b/style.css new file mode 100644 index 0000000..5c8efc1 --- /dev/null +++ b/style.css @@ -0,0 +1,114 @@ +/* Страница «Тимбилдинг» — только раскладка и мелочи; цвета и компоненты рисует ДС. */ + +/* ── Шапка участника: логотип + название рядом ── */ +.tb-brand { + display: inline-flex; + align-items: center; + gap: var(--kt-ai-space-4); + min-width: 0; +} +.tb-logo { display: block; flex: 0 0 auto; } +.tb-brand-name { + font-size: var(--kt-ai-text-md); + font-weight: var(--kt-ai-weight-medium); + color: var(--kt-ai-fg); + white-space: nowrap; +} +.tb-pagehead-in { gap: var(--kt-ai-space-5); } + +/* ── Chip в hero: отбить от заголовка ── */ +.kt-ai-hero .kt-ai-chip { margin-top: calc(-1 * var(--kt-ai-space-9)); } + +/* ── Иконка в плитке: спокойный контейнер ── */ +.kt-ai-tile-icon { + display: inline-grid; + place-items: center; + width: 40px; + height: 40px; + margin-bottom: var(--kt-ai-space-4); + border-radius: var(--vibe-card-radius, var(--kt-ai-radius-xl)); + background: var(--vibe-tint, var(--kt-ai-primary-subtle)); + color: var(--kt-ai-fg); +} +.kt-icon { width: 18px; height: 18px; } + +/* ── Примечание под расписанием ── */ +.tb-note { padding: var(--kt-ai-space-5) var(--kt-ai-space-6); } + +/* ── Форма регистрации ── */ +#regform .kt-ai-btn { width: 100%; margin-top: var(--kt-ai-space-2); } +#regdone { + border: 1px dashed var(--kt-ai-border-strong); + margin-top: 0; +} +.kt-ai-empty-ic { + display: inline-grid; + place-items: center; + width: 44px; + height: 44px; + border-radius: var(--kt-ai-radius-full); + background: var(--kt-ai-status-ok-bg, var(--kt-ai-primary-subtle)); + color: var(--kt-ai-status-ok-fg, var(--kt-ai-fg)); +} +.kt-ai-empty-ic .kt-icon { width: 20px; height: 20px; } + +/* ── Подвал ── */ +.tb-admin-link { + background: none; + border: none; + cursor: pointer; + font-size: var(--kt-ai-text-xs); + color: var(--kt-ai-fg-muted); + padding: var(--kt-ai-space-2) var(--kt-ai-space-3); + border-radius: var(--kt-ai-radius-md); +} +.tb-admin-link:hover { background: var(--kt-ai-bg-hover); color: var(--kt-ai-fg); } + +/* ── Сайдбар админки ── */ +.tb-brand-v { padding: 0 var(--kt-ai-space-5); } +.kt-ai-spacer-tb { flex: 1; min-height: var(--kt-ai-space-8); } +.tb-admin-card { margin: 0 var(--kt-ai-space-5); background: var(--kt-ai-bg) !important; border: 1px solid var(--kt-ai-border); padding: var(--kt-ai-space-4) var(--kt-ai-space-5); } +.tb-admin-card p { margin: var(--kt-ai-space-2) 0 0; font-size: var(--kt-ai-text-2xs); line-height: var(--kt-ai-leading-normal); } + +/* ── Topbar: хлебные крошки ── */ +.kt-ai-breadcrumb { + display: inline-flex; + align-items: center; + gap: var(--kt-ai-space-3); + font-size: var(--kt-ai-text-sm); + min-width: 0; +} +.kt-ai-breadcrumb .kt-icon { width: 14px; height: 14px; opacity: .6; } +.kt-ai-breadcrumb strong { font-weight: var(--kt-ai-weight-semibold); } + +/* KPI-полоса админки: плотнее дефолта */ +#adm-kpis { margin-top: var(--kt-ai-space-6); } + +/* ── Tabula: строки кликабельны ── */ +#adm-tbody tr { cursor: pointer; } +#adm-tbody .cell-name { font-weight: var(--kt-ai-weight-medium); } +#adm-tbody .cell-sub { display: block; font-size: var(--kt-ai-text-2xs); color: var(--kt-ai-fg-faint); margin-top: 1px; } +#adm-tbody td.num { white-space: nowrap; } + +/* ── Drawer ── */ +.drawer-head { display: flex; align-items: center; gap: var(--kt-ai-space-4); margin-bottom: var(--kt-ai-space-6); } +.drawer-head > .kt-ai-t-muted { font-size: var(--kt-ai-text-2xs); text-transform: uppercase; letter-spacing: .8px; white-space: nowrap; } +.drawer-head strong { font-size: var(--kt-ai-text-xl); font-weight: var(--kt-ai-weight-semibold); flex: 1; min-width: 0; } +.drawer-close { + display: inline-grid; place-items: center; + width: 32px; height: 32px; flex: 0 0 auto; + border-radius: var(--kt-ai-radius-lg); + color: var(--kt-ai-fg-muted); cursor: pointer; + border: 1px solid var(--kt-ai-border); +} +.drawer-close:hover { background: var(--kt-ai-bg-hover); color: var(--kt-ai-fg); } +.drawer-body .kt-ai-prop { border-bottom: 1px solid var(--kt-ai-divider); padding-bottom: var(--kt-ai-space-4); } +.drawer-body .kt-ai-prop:last-child { border-bottom: none; padding-bottom: 0; } +#d-phone { font-variant-numeric: tabular-nums; } + +/* ── Мобильные ── */ +@media (max-width: 639.98px) { + .tb-brand-name { font-size: var(--kt-ai-text-sm); } + .kt-ai-kpi-strip { gap: var(--kt-ai-space-6); } + .kt-ai-kpi .hint { max-width: 100%; } +}