import { assert } from "./assert.ts"; import { LEVEL_NAMES, GraduationChange, fetchGraduation, submitGraduationChange, deleteGraduationChange, } from "./graduation-ENV.ts"; import { fetchStudent, fetchStudents } from "./student-ENV.ts"; import { getName, DATETIME_FMT } from "./utils-ENV.js"; import { initSelection } from "./selection.js"; let name; let student; async function loadStudent(studentToLoad) { student = studentToLoad; await fetchAndUpdateStudentInfo(); // updateStudentCalendar(); // updateStudentProgress(); updateStudentGraduation(); } function initStudentSelection() { initSelection( "student-selection", fetchStudents, (student) => `${student.name} ${student.getGroup()}`, loadStudent, ); } function pointClickHandler(color) { const textareaDiv = document.getElementById("student-info-point-reason-container"); const textarea = document.getElementById("student-info-point-reason"); const submitBtn = document.getElementById("student-info-point-reason-submit"); // TODO(muhq): Prefill the textarea with common reasons switch (color) { case "green": textarea.style.background = "rgb(from green r g b / 20%)"; textarea.value = ""; break; case "orange": textarea.style.background = "rgb(from orange r g b / 20%)"; textarea.value = ""; break; case "red": textarea.style.background = "rgb(from red r g b / 20%)"; textarea.value = ""; break; } textareaDiv.style.display = "block"; textarea.focus(); const coach = name; const submit = function () { let change; switch (color) { case "green": change = GraduationChange.newGreenPointChange(false, textarea.value, coach); break; case "orange": change = GraduationChange.newOrangePointChange(false, textarea.value, coach); break; case "red": change = GraduationChange.newRedPointChange(false, textarea.value, coach); break; default: throw new Error("Invalid color"); } submitGraduationChange(change, student.graduation); textareaDiv.style.display = "none"; updateStudentInfo(); fetchGraduation(student).then(() => updateStudentGraduation()); }; // Use onEVENT to override any previous handler textarea.onkeydown = function (e) { if (e.shiftKey && e.key === "Enter") { submit(); } if (e.key === "Escape") { textareaDiv.style.display = "none"; } }; submitBtn.onclick = submit; } async function fetchAndUpdateStudentInfo() { student.graduation = await fetchGraduation(student); updateStudentInfo(); } let DEFAULT_STUDENT_ICON; function updateStudentInfo() { // Reset the student icon document.getElementById("student-info-icon").src = DEFAULT_STUDENT_ICON; // Try to replace the generic icon with the user's icon var img = document.createElement("img"); img.onload = function () { document.getElementById("student-info-icon").src = img.src; img = img.onload = null; }; student.getIconUrl().then((url) => (img.src = url)); document.getElementById("student-info-name").innerHTML = `${student.name}`; if (student.graduation) { const tag = document.getElementById("student-info-tag"); tag.innerHTML = student.graduation.getLevelName(); tag.style.display = "block"; document.getElementById("student-info-points-green").innerHTML = student.graduation.green; document.getElementById("student-info-points-orange").innerHTML = student.graduation.orange; document.getElementById("student-info-points-red").innerHTML = student.graduation.red; document.getElementById("student-info-points-sum").innerHTML = student.graduation.getNettoPoints(); document .getElementById("student-info-green") .addEventListener("click", () => pointClickHandler("green")); document .getElementById("student-info-orange") .addEventListener("click", () => pointClickHandler("orange")); document .getElementById("student-info-red") .addEventListener("click", () => pointClickHandler("red")); const icon = document.getElementById("student-level-icon"); icon.src = student.graduation.getIcon(); document.getElementById("student-info-coach").innerHTML = student.graduation.coaches[0] ?? ""; } document.getElementById("student-content").style.display = "block"; } async function updateStudentGraduation() { assert(student.graduation, "Student has no graduation"); const graduation = student.graduation; // Only the coach is allowed to graduate if (student.graduation.coaches.includes(name)) { document.getElementById("graduation-level-change").style.display = "block"; const levelChangeLvl = document.getElementById("graduation-level-change-level"); levelChangeLvl.value = Math.min(graduation.level + 1, 4); const levelChangeBtn = document.getElementById("graduation-level-change-submit"); levelChangeBtn.onclick = () => { const change = GraduationChange.newLevelChange(Number(levelChangeLvl.value), "", name); submitGraduationChange(change, student.graduation); updateStudentInfo(); updateStudentGraduation(); }; } else { document.getElementById("graduation-level-change").style.display = "hidden"; } const changesTbl = document.getElementById("graduation-changes"); changesTbl.innerHTML = ""; for (const change of graduation.changes) { const changeTr = document.createElement("tr"); switch (change.change) { case "Grün": changeTr.style.background = "rgb(from green r g b / 50%)"; break; case "Rot": changeTr.style.background = "rgb(from red r g b / 50%)"; break; case "Orange": changeTr.style.background = "rgb(from orange r g b / 50%)"; break; } const tds = []; const addTd = (s) => { const changeTd = document.createElement("td"); changeTd.innerHTML = s; changeTr.appendChild(changeTd); tds.push(changeTd); }; addTd(change.change); addTd(DATETIME_FMT.format(change.date)); addTd(change.reason); addTd(change.coach); let ctxFmt = ""; switch (change.change) { case "CoachZuweisung": ctxFmt = change.context.zuweisungen[student.id]; break; case "LevelÄnderung": ctxFmt = LEVEL_NAMES[change.context.level]; break; case "Grün": case "Orange": case "Rot": ctxFmt = change.context.mandatory ? "erforderlich" : ""; break; default: ctxFmt = JSON.stringify(change.context); break; } addTd(ctxFmt); if (change.coach === name) { const delBtn = document.createElement("button"); delBtn.innerHTML = "Löschen"; delBtn.addEventListener("click", () => { const state = document.createElement("span"); state.classList.add("graduation-delete-state"); const inprogress = document.createElement("span"); inprogress.classList.add("graduation-delete-inprogress"); state.appendChild(inprogress); delBtn.replaceWith(state); deleteGraduationChange(graduation, change) .then(() => { updateStudentInfo(); state.style.display = "inline"; state.innerHTML = "✓"; for (const td of tds) { td.style["text-decoration"] = "line-through"; } }) .catch(() => { state.style.display = "inline"; state.innerHTML = "X"; }) .finally(() => { inprogress.remove(); }); }); changeTr.appendChild(delBtn); } changesTbl.appendChild(changeTr); } } async function initCockpit() { initStudentSelection(); const params = new URLSearchParams(location.search); const userId = params.get("userToLoad"); if (userId) { student = await fetchStudent(userId); await loadStudent(student); } } window.onload = function () { DEFAULT_STUDENT_ICON = document.getElementById("student-info-icon").src; name = getName(); initCockpit(); };