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));
});