import { route } from "../router.js"; import { Api } from "../kek.js"; import { el, formToJSON, $, fmtDateInput, loadSession } from "../utils.js"; function needSession() { const s = loadSession(); if (!s) location.hash = "#/auth/login"; return s; } function sectionTabs(active) { const tabs = document.createElement("div"); tabs.className = "tabs"; const items = [ ["#/dashboard", "Сводная"], ["#/teacher/classes", "Классы"], ["#/teacher/students", "Ученики"], ["#/teacher/lessons", "Уроки"], ]; for (const [href, title] of items) { const a = document.createElement("a"); a.href = href; a.textContent = title; if (href.endsWith(active)) a.classList.add("active"); tabs.appendChild(a); } return tabs; } function classesView(session) { const wrap = el("div", { class: "grid-two" }); const formCard = el("div", { class: "card" }, [ el("div", { class: "card-header" }, "Создать класс"), el("div", { class: "card-body" }, [ (() => { const form = el("form", { class: "form-grid" }); form.innerHTML = `
Ограничение: однобуквенное обозначение параллели.
`; form.addEventListener("submit", async (e) => { e.preventDefault(); const payload = formToJSON(form); try { await Api.createClass(session, payload); await refresh(); form.reset(); } catch (err) { const box = form.querySelector("#cls-err"); box.classList.remove("hidden"); box.textContent = err.message || "Ошибка создания класса"; } }); return form; })(), ]), ]); const listCard = el("div", { class: "card" }, [ el("div", { class: "card-header" }, "Ваши классы"), el("div", { class: "card-body" }, [ (() => { const box = el("div", { class: "form-grid", style: "margin-bottom:8px", }); box.innerHTML = `
Выберите класс в таблице и используйте кнопки «Добавить ученика»/«Убрать ученика»
`; return box; })(), el("table", { class: "table", id: "class-table" }, [ el( "thead", {}, el("tr", {}, [ el("th", {}, "ID"), el("th", {}, "Класс"), el("th", {}, "Создатель"), el("th", {}, ""), ]) ), el("tbody"), ]), ]), ]); wrap.appendChild(formCard); wrap.appendChild(listCard); async function refresh() { const { data } = await Api.listClasses(session); const tbody = listCard.querySelector("tbody"); tbody.innerHTML = ""; for (const c of data?.["классы"] || []) { const tr = el("tr"); tr.appendChild(el("td", {}, String(c["идентификатор"]))); tr.appendChild( el("td", {}, `${c["номер"] || ""}${c["буква"] ? "-" + c["буква"] : ""}`) ); tr.appendChild(el("td", {}, String(c["создатель"] ?? ""))); const actions = el("td"); const btnAdd = el("button", { class: "btn" }, "Добавить ученика"); btnAdd.addEventListener("click", async () => { const sid = Number($("#st-to-add", listCard)?.value || 0); if (!sid) return alert("Укажите ID ученика"); try { await Api.addStudentToClass(session, c["идентификатор"], sid); await refresh(); } catch (e) { alert(e.message || "Не удалось добавить ученика"); } }); const btnRm = el("button", { class: "btn secondary" }, "Убрать ученика"); btnRm.addEventListener("click", async () => { const sid = Number($("#st-to-add", listCard)?.value || 0); if (!sid) return alert("Укажите ID ученика"); try { await Api.removeStudentFromClass(session, c["идентификатор"], sid); await refresh(); } catch (e) { alert(e.message || "Не удалось удалить ученика"); } }); const btnDel = el("button", { class: "btn danger" }, "Удалить класс"); btnDel.addEventListener("click", async () => { if (!confirm("Удалить класс?")) return; await Api.deleteClass(session, c["идентификатор"]); await refresh(); }); actions.appendChild(btnAdd); actions.appendChild(btnRm); actions.appendChild(btnDel); tr.appendChild(actions); tbody.appendChild(tr); } } refresh().catch(console.error); return wrap; } function studentsView(session) { const wrap = el("div", { class: "grid-two" }); const formCard = el("div", { class: "card" }, [ el("div", { class: "card-header" }, "Создать ученика"), el("div", { class: "card-body" }, [ (() => { const form = el("form", { class: "form-grid" }); form.innerHTML = `
`; form.addEventListener("submit", async (e) => { e.preventDefault(); const payload = formToJSON(form); try { await Api.createStudent(session, payload); await refresh(); form.reset(); } catch (err) { const box = form.querySelector("#st-err"); box.classList.remove("hidden"); box.textContent = err.message || "Ошибка создания ученика"; } }); return form; })(), ]), ]); const listCard = el("div", { class: "card" }, [ el("div", { class: "card-header" }, "Ученики"), el("div", { class: "card-body" }, [ el("table", { class: "table", id: "st-table" }, [ el( "thead", {}, el("tr", {}, [ el("th", {}, "ID"), el("th", {}, "ФИО"), el("th", {}, "Имя пользователя"), el("th", {}, ""), ]) ), el("tbody"), ]), ]), ]); wrap.appendChild(formCard); wrap.appendChild(listCard); async function refresh() { const { data } = await Api.listStudents(session); const tbody = listCard.querySelector("tbody"); tbody.innerHTML = ""; for (const s of data?.["ученики"] || []) { const tr = el("tr"); tr.appendChild(el("td", {}, String(s["идентификатор"]))); tr.appendChild( el( "td", {}, `${s["фамилия"] || ""} ${s["имя"] || ""} ${ s["отчество"] || "" }`.trim() ) ); tr.appendChild(el("td", {}, s["имя пользователя"] || "")); const actions = el("td"); const btnDel = el("button", { class: "btn danger" }, "Удалить"); btnDel.addEventListener("click", async () => { if (!confirm("Удалить ученика?")) return; await Api.deleteStudent(session, s["идентификатор"]); await refresh(); }); actions.appendChild(btnDel); tr.appendChild(actions); tbody.appendChild(tr); } } refresh().catch(console.error); return wrap; } function lessonsView(session) { const wrap = el("div", { class: "grid-two" }); const formCard = el("div", { class: "card" }, [ el("div", { class: "card-header" }, "Создать урок"), el("div", { class: "card-body" }, [ (() => { const form = el("form", { class: "form-grid" }); form.innerHTML = `
`; form.addEventListener("submit", async (e) => { e.preventDefault(); const f = new FormData(form); const classId = Number(f.get("classId")); const payload = { дата: f.get("дата"), тема: f.get("тема"), домашка: f.get("домашка"), }; try { await Api.createLesson(session, classId, payload); await refresh(); } catch (err) { const box = form.querySelector("#lsn-err"); box.classList.remove("hidden"); box.textContent = err.message || "Ошибка создания урока"; } }); return form; })(), ]), ]); const listCard = el("div", { class: "card" }, [ el("div", { class: "card-header" }, "Уроки класса"), el("div", { class: "card-body" }, [ (() => { const filter = el("div", { class: "form-grid", style: "margin-bottom:8px", }); filter.innerHTML = `
`; return filter; })(), el("table", { class: "table", id: "lsn-table" }, [ el( "thead", {}, el("tr", {}, [ el("th", {}, "ID"), el("th", {}, "Класс"), el("th", {}, "Дата"), el("th", {}, "Тема"), el("th", {}, "Д/З"), el("th", {}, ""), ]) ), el("tbody"), ]), ]), ]); wrap.appendChild(formCard); wrap.appendChild(listCard); async function refresh() { const classId = Number($("#flt-class", listCard)?.value || 0); const date = $("#flt-date", listCard)?.value || undefined; if (!classId) return; const { data } = await Api.listLessons(session, classId, date); const tbody = listCard.querySelector("tbody"); tbody.innerHTML = ""; for (const l of data?.["уроки"] || []) { const tr = el("tr"); tr.appendChild(el("td", {}, String(l["идентификатор"]))); tr.appendChild(el("td", {}, String(l["идентификатор класса"]))); tr.appendChild(el("td", {}, l["дата"] || "")); tr.appendChild(el("td", {}, l["название"] || "")); tr.appendChild(el("td", {}, l["домашнее задание"] || "")); const actions = el("td"); const btnDel = el("button", { class: "btn danger" }, "Удалить"); btnDel.addEventListener("click", async () => { if (!confirm("Удалить урок?")) return; await Api.deleteLessonById( session, l["идентификатор класса"], l["идентификатор"] ); await refresh(); }); actions.appendChild(btnDel); tr.appendChild(actions); tbody.appendChild(tr); } } $("#btn-load", listCard)?.addEventListener("click", refresh); return wrap; } function dashboardView() { const card = el("div", { class: "card" }, [ el("div", { class: "card-header" }, "Сводная"), el("div", { class: "card-body" }, [ el( "p", {}, "Добро пожаловать! Используйте боковое меню, чтобы работать с данными." ), el( "p", { class: "small" }, `Стоят пассажиры в аэропорту, посадочный досмотр проходят. Дошла очередь до мужика с чемоданом. — Пожалуйста, приготовьте чемодан к осмотру. — Не могу. — Почему? — А у меня там бипки! — А что это? — Ну, пропатчите сервис на тривиле — покажу! Служба безопасности шутку не поняла, вызвала наряд полиции: — Гражданин, открывайте чемодан. — Но я не могу! — Почему не можете? — Потому что у меня там бипки! — «Бипки»? Что это? — Так реализуйте гарбадж коллектор мне, и я покажу! Отобрали чемодан, а открыть никто не может. Повезли мужика в СИЗО. В камере сидельцы расспрашивают: — Братуха, за что тебя? — Да чемодан отказался открывать. — А что там? — Да бипки там. — Какие еще «бипки»? — Ну документацию обновите мне, тогда и расскажу. И вот угодил мужик в лазарет, с побоями да синяками по всему телу, весь перебинтован, еле дышит. Следователи вызвали группу специалистов для вскрытия чемодана. Час, два, три пыхтели. Кое-как разворотили, смотрят — а там бипки.` ), ]), ]); return card; } route("/dashboard", async ({ view }) => { const s = needSession(); view.appendChild(sectionTabs("dashboard")); view.appendChild(dashboardView(s)); }); route("/teacher/classes", async ({ view }) => { const s = needSession(); view.appendChild(sectionTabs("classes")); view.appendChild(classesView(s)); }); route("/teacher/students", async ({ view }) => { const s = needSession(); view.appendChild(sectionTabs("students")); view.appendChild(studentsView(s)); }); route("/teacher/lessons", async ({ view }) => { const s = needSession(); view.appendChild(sectionTabs("lessons")); view.appendChild(lessonsView(s)); });