From 0298e03781a8a406252ce0d8b2f69246108500df Mon Sep 17 00:00:00 2001 From: "s0wlz (Matthias Puchstein)" Date: Tue, 28 Apr 2026 19:15:39 +0200 Subject: [PATCH] feat(frontend): redesign sessions, courses, live view, and student check-in --- .../src/routes/admin/courses/+page.svelte | 263 ++++------ .../routes/admin/live/[slotId]/+page.svelte | 232 ++++++++- .../src/routes/admin/sessions/+page.svelte | 423 +++++++-------- frontend/src/routes/s/[code]/+page.svelte | 481 ++++++++++++++---- 4 files changed, 883 insertions(+), 516 deletions(-) diff --git a/frontend/src/routes/admin/courses/+page.svelte b/frontend/src/routes/admin/courses/+page.svelte index ed9f149..6d20a89 100644 --- a/frontend/src/routes/admin/courses/+page.svelte +++ b/frontend/src/routes/admin/courses/+page.svelte @@ -1,188 +1,97 @@ -

Courses & Students

+
-
-
-

Manage Courses

-
- - - -
- -
- {#each courses as course} -
selectedCourseId = course.id} - > - {course.name} ({course.semester}) -
- {/each} -
+ +
+
Verwaltung
+
+ Kurse + · {courses.length}
+
-
- {#if selectedCourseId} - {@const selectedCourse = courses.find(c => c.id === selectedCourseId)} -

Students in {selectedCourse?.name}

- -
-
- - -
- -
- Import CSV (name header): - -
-
- - - - - - - - - - - {#each students as student} - - - - - - {/each} - -
IDNameActions
{student.id}{student.name} - -
- {:else} -

Select a course to manage students.

- {/if} + +
+
+
Neuen Kurs anlegen
+
+
+
+ + +
+
+ + +
+ +
+
+ + +
+ {#if courses.length === 0} +
+ Noch keine Kurse vorhanden. +
+ {:else} + + + + + + + + + + + {#each courses as course, i} + + + + + + + {/each} + +
#NameSemesterAktionen
+ {i + 1} + {course.name} + {course.semester} + + +
+ {/if} +
+
- - diff --git a/frontend/src/routes/admin/live/[slotId]/+page.svelte b/frontend/src/routes/admin/live/[slotId]/+page.svelte index e2bc8c9..3283fe5 100644 --- a/frontend/src/routes/admin/live/[slotId]/+page.svelte +++ b/frontend/src/routes/admin/live/[slotId]/+page.svelte @@ -1,10 +1,234 @@ -
- Tutor:innen-Ansicht · Live -

Slot {slotId}

+
+ + {#if loading} +
+ Wird geladen… +
+ {:else if !slot || !session} +
+ Slot nicht gefunden. +
+ {:else} + +
+
+
Tutor:innen-Ansicht · Live
+
+ {weekLabel(session.week_nr)} · {session.date} +
+
+ {slot.start_time}–{slot.end_time} +
+
+
+ {#if slot.code} + {slot.code} + {/if} + + {#if slot.code} + + {/if} + {#if slot.status === 'closed'} + + {:else if slot.status === 'open'} + + {:else if slot.status === 'locked'} + + {/if} +
+
+ + +
+ + +
+
+
Sitzplan
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+
+ + + {#if students.length > 0} +
+
+ Anwesenheit manuell +
+ + + {#each students as student, i} + {@const present = attendances.some((a: Attendance) => a.student_id === student.id)} + + + + + {/each} + +
+
+ + {student.name.split(' ').map((w: string) => w[0]).slice(0, 2).join('').toUpperCase()} + + {student.name} +
+
+ +
+
+ {/if} +
+ + +
+ { selectedStudentId = id; }} + weekNr={session.week_nr} + /> +
+
+ {/if} +
diff --git a/frontend/src/routes/admin/sessions/+page.svelte b/frontend/src/routes/admin/sessions/+page.svelte index bf4316a..ca1d714 100644 --- a/frontend/src/routes/admin/sessions/+page.svelte +++ b/frontend/src/routes/admin/sessions/+page.svelte @@ -1,241 +1,214 @@ -

Schedule Sessions & Slots

+
-
-
-
- - -
- -
-

Add Session

-
- - - -
-
- -
- {#each sessions as session} -
-
- Week {session.week_nr} ({session.date}) - -
-
- {#each session.slots || [] as slot} -
- {slot.start_time}-{slot.end_time} - -
- {/each} -
-
- {/each} -
+ +
+
+
Sitzungen
+
+ Planung + {#if selectedCourse} + · {selectedCourse.name} + {/if} +
- - {#if selectedSessionId} - + {#if courses.length > 1} + {/if} +
+ + +
+
+
Neue Sitzung anlegen
+ +
+
+
+ + +
+
+ + +
+ +
+
+ + + {#if sessions.length === 0} +
+ Noch keine Sitzungen angelegt. +
+ {:else} +
+ {#each sessions as session} +
+
+
+ W{String(session.week_nr).padStart(2, '0')} + {session.date} +
+ +
+ + {#if (session.slots ?? []).length === 0} +
+ Noch kein Slot für diese Sitzung. +
+ {:else} + + + {#each session.slots ?? [] as slot, i} + + + + + + + {/each} + +
+ {slot.start_time}–{slot.end_time} + + {#if slot.code} + {slot.code} + {:else} + + {/if} + +
+ {#if slot.status === 'open' || slot.status === 'locked'} + Anzeigen + {/if} + +
+
+ {/if} +
+ {/each} +
+ {/if}
- + +{#if selectedSessionId !== null} + {@const sess = sessions.find((s: Session) => s.id === selectedSessionId)} +
+
+
Neuer Slot
+
+ Woche {sess?.week_nr} · {sess?.date} +
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
+{/if} diff --git a/frontend/src/routes/s/[code]/+page.svelte b/frontend/src/routes/s/[code]/+page.svelte index 84e778e..80458a0 100644 --- a/frontend/src/routes/s/[code]/+page.svelte +++ b/frontend/src/routes/s/[code]/+page.svelte @@ -1,124 +1,385 @@ -
- {#if loading} -

Loading...

- {:else if error} -

{error}

- {:else if slot} -

Check-in: {slot.start_time} - {slot.end_time}

- - {#if slot.status === 'locked'} -

Check-in is currently locked by the tutor.

- {/if} +
- {#if !myAttendance && slot.status === 'open'} -
- - -
- {:else if myAttendance} -

You are checked in as {students.find(s => s.id === myAttendance.student_id)?.name || 'Student'}

- {/if} + {#if step === 'loading'} +
+ Wird geladen… +
-
-

Select a seat to check in:

- + {:else if step === 'error'} +
+
+
Fehler
+
{errorMsg}
+
+
+ + {:else if step === 'locked'} +
+
+
Anwesenheit
+
+ Erfassung abgeschlossen
- {/if} -
+
+
+ {#if myAttendance} +
✓ ANWESEND
+
Du wurdest als anwesend eingetragen.
+ {:else} +
Der Check-in-Link ist nicht mehr aktiv.
+
Wende dich an deine:n Tutor:in.
+ {/if} +
+ {#if slot} +
{slot.start_time}–{slot.end_time}
+ {/if} +
- + {:else if !isDesktop} + + + {#if step === 'name'} +
+
+
Check-in
+
Wer bist du?
+
Wähle deinen Namen aus der Liste.
+
+ + {#if errorMsg} +
+ {errorMsg} +
+ {/if} + + + +
+ {#each filteredStudents as student} + + {:else} +
+ Keine Treffer. +
+ {/each} +
+ + {#if slot} +
{slot.start_time}–{slot.end_time}
+ {/if} +
+ + {:else if step === 'seat' && selectedStudent} +
+
+
Hallo, {selectedStudent.name.split(' ')[0]} 👋
+
Wähle deinen Sitz
+
Tippe auf einen freien Platz.
+
+ + {#if errorMsg} +
+ {errorMsg} +
+ {/if} + +
+ checkin(seat.id)} + /> +
+ +
+ + Dein Platz + + + Belegt + + + Frei + +
+ + +
+ + {:else if step === 'confirmed' && (myAttendance || selectedStudent)} +
+
+
+ Du sitzt auf Platz {myAttendance?.seat_id ?? '—'} +
+
+ +
+
✓ ANWESEND
+
Eingecheckt um {myAttendance?.checked_in_at?.slice(11, 16) ?? '—'} Uhr
+
+ +
+ +
+ + +
+ {/if} + + {:else} + + + {#if step === 'name'} +
+
+
Check-in
+
Wer bist du?
+
Wähle deinen Namen aus der Liste.
+
+ + {#if errorMsg} +
+ {errorMsg} +
+ {/if} + + + +
+ {#each filteredStudents as student} + + {:else} +
+ Keine Treffer. +
+ {/each} +
+
+ + {:else if step === 'seat' && selectedStudent} +
+ +
+
+
Hallo, {selectedStudent.name.split(' ')[0]} 👋
+
Wähle deinen Sitz
+
+ + {#if errorMsg} +
+ {errorMsg} +
+ {/if} + + checkin(seat.id)} + /> + +
+ + Dein Platz + + + Belegt + + + Frei + +
+
+ + +
+
+
Sitzung
+ {#if slot} +
{slot.start_time}–{slot.end_time}
+ {/if} +
+
+ +
+
Anwesend
+
{attendances.length}
+
von {students.length} Studierenden
+
+ + +
+
+ + {:else if step === 'confirmed'} +
+ +
+
+
Eingecheckt
+
+ Du sitzt auf Platz {myAttendance?.seat_id ?? '—'} +
+
+ + + +
+ + +
+
+ + +
+
+
✓ ANWESEND
+
+ {myAttendance?.checked_in_at?.slice(11, 16) ?? '—'} Uhr +
+
+ + {#if slot} +
+
Sitzung
+
{slot.start_time}–{slot.end_time}
+
+ {/if} + +
+
Anwesende
+
{attendances.length} / {students.length}
+
+
+
+ {/if} + {/if} + +