diff options
| author | Florian Fischer <florian.fischer@muhq.space> | 2026-06-24 09:18:26 +0200 |
|---|---|---|
| committer | Florian Fischer <florian.fischer@muhq.space> | 2026-06-24 09:18:26 +0200 |
| commit | af8585faa1aa7c39564c583963349c4a2922df9c (patch) | |
| tree | 5a806a738a76e5f928e68e4def47e0b3a857d490 /cockpit | |
| parent | 13f9914757b2a72e77369859bbaec35339f31446 (diff) | |
| download | bycs-sol-af8585faa1aa7c39564c583963349c4a2922df9c.tar.gz bycs-sol-af8585faa1aa7c39564c583963349c4a2922df9c.zip | |
cockpit: finish prototype UI
* remove process and calendar
* add observation period
* improve mocking
Diffstat (limited to 'cockpit')
| -rw-r--r-- | cockpit/index.css | 14 | ||||
| -rw-r--r-- | cockpit/index.html | 83 | ||||
| -rw-r--r-- | cockpit/js/cockpit.js | 210 |
3 files changed, 217 insertions, 90 deletions
diff --git a/cockpit/index.css b/cockpit/index.css index 810442e..a206067 100644 --- a/cockpit/index.css +++ b/cockpit/index.css @@ -100,6 +100,11 @@ flex-direction: column; } +#student-observation { + margin-left: 2rem; + margin-bottom: 1rem; +} + .student-info-mid-element { padding: 5pt; border: black solid 0.2pt; @@ -178,3 +183,12 @@ #student-info-bonus-amount { font-size: 2rem; } + +.graduation-dropdown > summary { + background: lightgray; + border: black solid 0.2pt; + border-radius: 10px; + padding: 0.25rem; + margin-top: 1rem; + margin-bottom: 1rem; +} diff --git a/cockpit/index.html b/cockpit/index.html index d0bb0c7..ddf8005 100644 --- a/cockpit/index.html +++ b/cockpit/index.html @@ -48,6 +48,9 @@ </div> </div> + <div id="student-observation" class="student-info-banner"> + </div> + <div id="student-info-mid" class="student-info-banner"> <div id="student-info-green" class="student-info-mid-element"> ⚫Grüne Punkte <span class="student-info-points" id="student-info-points-green">0</span> @@ -70,32 +73,70 @@ <button id="student-info-point-reason-submit">↵</button> </div> - <div id="student-info-bottom" class="student-info-banner"> - <div id="student-info-stickers-container" class="student-info-bottom-element"> - Monstersticker - <div id="student-info-stickers"> - </div> - </div> - <div id="student-info-bonus-container" class="student-info-bottom-element"> - Bonuskarten - <div id="student-info-bonus-row"> - <img></img> - <span id="student-info-bonus-amount">0</span> - </div> - </div> - </div> + <!-- <div id="student-info-bottom" class="student-info-banner"> --> + <!-- <div id="student-info-stickers-container" class="student-info-bottom-element"> --> + <!-- Monstersticker --> + <!-- <div id="student-info-stickers"> --> + <!-- </div> --> + <!-- </div> --> + <!-- <div id="student-info-bonus-container" class="student-info-bottom-element"> --> + <!-- Bonuskarten --> + <!-- <div id="student-info-bonus-row"> --> + <!-- <img></img> --> + <!-- <span id="student-info-bonus-amount">0</span> --> + <!-- </div> --> + <!-- </div> --> + <!-- </div> --> </div> - <details id="progress"> - <summary>Lernfortschritt</summary> - </details> + <!-- <details id="progress"> --> + <!-- <summary>Lernfortschritt</summary> --> + <!-- </details> --> - <details id="calendar"> - <summary>Kalender</summary> - </details> + <!-- <details id="calendar"> --> + <!-- <summary>Kalender</summary> --> + <!-- </details> --> - <details id="graduation"> + <details id="graduation" data-availability="if-lehrer"> <summary>Graduierung</summary> + + <details id="graduation-changes-container" class="graduation-dropdown"> + <summary>Übersicht über Grüne, Orangene und Rote Punkte</summary> + <table class="table table-striped"> + <thead> + <tr> + <th>Datum</th> + <th>Grund</th> + <th>Coach</th> + </tr> + </thead> + <tbody id="graduation-changes"> + </tbody> + </table> + </details> + + <details id="graduation-current-observation-period-container" class="graduation-dropdown"> + <summary>Aktueller Beobachtungszeitraum</summary> + <table class="table table-striped"> + <thead> + <tr> + <th></th> + <th>Ja</th> + <th>Nein</th> + </tr> + </thead> + <tbody id="graduation-current-observation-period"> + </tbody> + </table> + </details> + + <details id="graduation-observation-periods-container" class="graduation-dropdown"> + <summary>Vergangene Beobachtungszeiträume</summary> + <table class="table table-striped"> + <tbody id="graduation-observation-periods"> + </tbody> + </table> + </details> </details> </div> </body> diff --git a/cockpit/js/cockpit.js b/cockpit/js/cockpit.js index b773f5c..f8ee8e9 100644 --- a/cockpit/js/cockpit.js +++ b/cockpit/js/cockpit.js @@ -1,16 +1,21 @@ -import { - fetchSubjectsMock as fetchSubjects, - fetchProgressMock as fetchProgress, -} from "../../js/mock.js"; +// import { + // fetchSubjectsMock as fetchSubjects, + // fetchProgressMock as fetchProgress, +// } from "../../js/mock.js"; import { + LEVEL_NAMES, GraduationChange, fetchGraduation, submitGraduationChange } from "../../js/graduation-ENV.js"; -import { Student, fetchStudent, fetchStudents } from "../../js/student-ENV.js"; -import { isTeacher as getIsTeacher } from "../../js/utils-ENV.js"; +import { fetchStudent, fetchStudents } from "../../js/student-ENV.js"; +import { + isTeacher as getIsTeacher, + getCurrentName, + getUserId, +} from "../../js/utils-ENV.js"; import { initSelection } from "../../js/selection.js"; let isTeacher; @@ -18,30 +23,20 @@ let firstname; let lastname; let student; -function initCurrentName() { - const autoNameDiv = document.getElementById("auto-name"); - firstname = autoNameDiv.children[0].innerHTML; - lastname = autoNameDiv.children[1].innerHTML; - autoNameDiv.remove(); -} - -async function loadNewStudent(id, name) { - if (isTeacher) { - student = await fetchStudent(id, name); - } else { - student = new Student(id, name); - } - await loadStudent(student); -} - async function loadStudent(studentToLoad) { student = studentToLoad; fetchAndUpdateStudentInfo(); - updateStudentCalendar(); - updateStudentProgress(); + // updateStudentCalendar(); + // updateStudentProgress(); + // TODO(muhq): fetch graduation only once updateStudentGraduation(); } +async function loadNewStudent(id) { + student = await fetchStudent(id); + await loadStudent(student); +} + function initStudentSelection() { initSelection( "student-selection", @@ -96,6 +91,7 @@ function pointClickHandler(color) { submitGraduationChange(student, change); textareaDiv.style.display = "none"; updateStudentInfo(); + updateStudentGraduation(); } // Use onEVENT to override any previous handler textarea.onkeydown = function(e) { @@ -132,7 +128,7 @@ function updateStudentInfo() { if (student.graduation) { const tag = document.getElementById("student-info-tag"); - tag.innerHTML = student.graduation.level; + tag.innerHTML = student.graduation.getLevelName(); tag.style.display = "block"; document.getElementById("student-info-points-green").innerHTML = student.graduation.green; @@ -150,59 +146,131 @@ function updateStudentInfo() { renderGraduationLevel(student.graduation, document.getElementById("student-info-level")); - renderGraduationStickers(student.graduation, document.getElementById("student-info-stickers")); - renderGraduationBonus(student.graduation, document.getElementById("student-info-stickers")); + // renderGraduationStickers(student.graduation, document.getElementById("student-info-stickers")); + // renderGraduationBonus(student.graduation, document.getElementById("student-info-stickers")); + + renderObservationStatus(student.graduation, document.getElementById("student-observation")); } document.getElementById("student-content").style.display = "block"; } function renderGraduationLevel(graduation, node) { - node.innerHTML = graduation.level; + node.innerHTML = graduation.getLevelName(); } -function renderGraduationStickers(graduation, node) { -} - -function renderGraduationBonus(graduation, node) { -} - -function newProgressBar(subject) { - let bar = document.createElement("div"); - fetchProgress(subject).then((progress) => { - bar.progress = progress; - }); - - let s1 = document.createElement("div"); - s1.onhover - - bar.appendChild(s1); - let s2 = document.createElement("div"); - bar.appendChild(s2); - let s3 = document.createElement("div"); - bar.appendChild(s3); - return bar; -} - -let selectedSubjects; -async function updateStudentProgress() { - selectedSubjects = await fetchSubjects(student); - - let prog = document.getElementById("progress"); - for(const subj of selectedSubjects) { - let subjDiv = document.createElement("div"); - subjDiv.innerHTML = subj; - subjDiv.appendChild(newProgressBar(subj)) - prog.appendChild(subjDiv); +// function renderGraduationStickers(graduation, node) { + // node.innerHTML = ""; +// } + +// function renderGraduationBonus(graduation, node) { + // node.innerHTML = ""; +// } + +function renderObservationStatus(graduation, node) { + node.innerHTML = ""; + + let status; + if (isTeacher || graduation.isObserved()) { + status = document.createElement("span"); + status.classList.add("student-observation-status"); + if (graduation.isObserved()) { + status.innerHTML = '<span style="font-size: larger;">👁</span> Ich befinde mich in der Beobachtungsphase zum Levelaufstieg.'; + } else { + status.innerHTML = "Nicht in Beobachtung."; + } + } else { + status = document.createElement("button"); + status.classList.add("student-request-observation-button"); + // Level is 1-based + status.innerHTML = `Ich will ${LEVEL_NAMES[graduation.level]} werden`; + status.addEventListener("click", async function() { + await graduation.requestObservation(); + // Rerender the observation status + renderObservationStatus(graduation, node); + }); } + node.appendChild(status); } -async function updateStudentCalendar() { - let cal = document.getElementById("calendar"); -} +// function newProgressBar(subject) { + // let bar = document.createElement("div"); + // fetchProgress(subject).then((progress) => { + // bar.progress = progress; + // }); + + // let s1 = document.createElement("div"); + // s1.onhover + + // bar.appendChild(s1); + // let s2 = document.createElement("div"); + // bar.appendChild(s2); + // let s3 = document.createElement("div"); + // bar.appendChild(s3); + // return bar; +// } + +// let selectedSubjects; +// async function updateStudentProgress() { + // selectedSubjects = await fetchSubjects(student); + + // let prog = document.getElementById("progress"); + // for(const subj of selectedSubjects) { + // let subjDiv = document.createElement("div"); + // subjDiv.innerHTML = subj; + // subjDiv.appendChild(newProgressBar(subj)) + // prog.appendChild(subjDiv); + // } +// } + +// async function updateStudentCalendar() { + // let cal = document.getElementById("calendar"); +// } async function updateStudentGraduation() { - let graduation = document.getElementById("graduation"); + if (!isTeacher) return; + const graduation = await student.getGraduation(); + + const changesTbl = document.getElementById("graduation-changes"); + changesTbl.innerHTML = ""; + for (const change of graduation.changes.filter( + (ch) => ["Grün", "Rot", "Orange"].includes(ch.change) )) + { + const changeTr = document.createElement("tr"); + for (const key in change) { + if (key == "json" || key == "id" || key == "name") continue; + + if (key == "change") { + switch(change.change) { + case "Grün": + changeTr.style.background = "green"; + break; + case "Rot": + changeTr.style.background = "red"; + break; + case "Orange": + changeTr.style.background = "orange"; + break; + default: + alert(`unexpected change: ${change.change}`); + } + continue; + } + + const value = change[key]; + let fmt = ""; + if (key == "date") + fmt = `${value.getDate()}.${value.getMonth()}.${value.getFullYear()} ${value.getHours()}:${value.getMinutes()}`; + else + fmt = value; + + const changeTd = document.createElement("td"); + changeTd.innerHTML = fmt; + changeTd.dataset.key = key; + changeTr.appendChild(changeTd); + } + changesTbl.appendChild(changeTr); + } } async function initCockpit() { @@ -213,14 +281,18 @@ async function initCockpit() { if (userId) loadNewStudent(userId); } else { - loadNewStudent(null, firstname + " " + lastname); + // Remove all elements only suitable for teachers + for(const elem of document.querySelectorAll("[data-availability=if-lehrer]")) { + elem.remove(); + } + + loadNewStudent(getUserId()); } - return; } window.onload = function() { isTeacher = getIsTeacher(); DEFAULT_STUDENT_ICON = document.getElementById("student-info-icon").src; - initCurrentName(); - initCockpit(isTeacher); + [firstname, lastname] = getCurrentName(); + initCockpit(); } |
