aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2026-09-25 20:43:27 +0200
committerFlorian Fischer <florian.fischer@muhq.space>2026-09-25 21:00:13 +0200
commit1f458004ee2a018cff176c9939a9e8b03425dbfa (patch)
tree52970e606354ec25554c8fe3555d63447818ad15 /src
parentbf09385a34f928e6c266d2f81337237955ee949b (diff)
downloadbycs-sol-1f458004ee2a018cff176c9939a9e8b03425dbfa.tar.gz
bycs-sol-1f458004ee2a018cff176c9939a9e8b03425dbfa.zip
mass-graduation: add list of submitted changes
Diffstat (limited to 'src')
-rw-r--r--src/graduation-mock.ts6
-rw-r--r--src/graduation-prod.ts4
-rw-r--r--src/mass-graduation.js97
-rw-r--r--src/mock.ts12
4 files changed, 115 insertions, 4 deletions
diff --git a/src/graduation-mock.ts b/src/graduation-mock.ts
index 8ddd7ef..a43ab36 100644
--- a/src/graduation-mock.ts
+++ b/src/graduation-mock.ts
@@ -1,6 +1,9 @@
import { LEVEL_NAMES, GraduationChange, Graduation } from "./graduation-common.ts";
-import { fetchGraduationMock as fetchGraduation } from "./mock.ts";
+import {
+ fetchGraduationMock as fetchGraduation,
+ fetchGraduationChangesMock as fetchGraduationChanges,
+} from "./mock.ts";
async function submitGraduationChange(c: GraduationChange, g?: Graduation) {
if (typeof g !== "undefined") g.addChange(c);
@@ -15,6 +18,7 @@ export {
GraduationChange,
Graduation,
fetchGraduation,
+ fetchGraduationChanges,
submitGraduationChange,
deleteGraduationChange,
};
diff --git a/src/graduation-prod.ts b/src/graduation-prod.ts
index 9ded9a8..0e20f8d 100644
--- a/src/graduation-prod.ts
+++ b/src/graduation-prod.ts
@@ -22,6 +22,10 @@ const _graduationChangesPromise = (async function () {
return [studentChanges, universalChanges];
})();
+export async function fetchGraduationChanges() {
+ return await _graduationChangesPromise;
+}
+
const graduations: Map<number, Graduation> = new Map();
export async function fetchGraduation(student: Student): Promise<Graduation> {
diff --git a/src/mass-graduation.js b/src/mass-graduation.js
index 3bce288..ff30675 100644
--- a/src/mass-graduation.js
+++ b/src/mass-graduation.js
@@ -1,6 +1,11 @@
-import { GraduationChange, submitGraduationChange } from "./graduation-ENV.ts";
+import {
+ GraduationChange,
+ fetchGraduationChanges,
+ submitGraduationChange,
+ deleteGraduationChange,
+} from "./graduation-ENV.ts";
import { fetchStudents } from "./student-ENV.ts";
-import { CourseLocalStorage, getName, splitmix32 } from "./utils-ENV.js";
+import { CourseLocalStorage, DATETIME_FMT, getName, splitmix32 } from "./utils-ENV.js";
let name;
@@ -197,11 +202,99 @@ function submitMassGraduation() {
});
}
+async function loadChanges() {
+ // TODO(muhq): deduplicate code with cockpit
+ const changesTbl = document.getElementById("graduation-changes");
+ const changes = await fetchGraduationChanges();
+
+ for (const change of changes) {
+ if (change.coach !== name) continue;
+
+ 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);
+ 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);
+
+ 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 init() {
document.getElementById("submit-mass-graduation").addEventListener("click", submitMassGraduation);
name = getName();
loadGroup();
+ loadChanges();
}
if (document.readyState === "loading") {
diff --git a/src/mock.ts b/src/mock.ts
index f81e901..14d408d 100644
--- a/src/mock.ts
+++ b/src/mock.ts
@@ -94,4 +94,14 @@ async function fetchGraduationMock(s: Student) {
return graduations.get(s);
}
-export { fetchStudentsMock, fetchSubjectsMock, fetchProgressMock, fetchGraduationMock };
+async function fetchGraduationChangesMock() {
+ return Array.from(graduations.values().map((g) => g.changes)).flat();
+}
+
+export {
+ fetchStudentsMock,
+ fetchSubjectsMock,
+ fetchProgressMock,
+ fetchGraduationMock,
+ fetchGraduationChangesMock,
+};