diff --git a/conductor/superadmin-crud.md b/conductor/superadmin-crud.md new file mode 100644 index 0000000..96c8c1d --- /dev/null +++ b/conductor/superadmin-crud.md @@ -0,0 +1,56 @@ +# Superadmin CRUD Implementation Plan + +**Objective:** Implement a superadmin role to manage courses and tutors, ensuring only authorized users can perform system-wide administrative actions. This feature will be developed in an isolated git worktree. + +## Key Context & Decisions +- **Role Strategy:** A new `is_superadmin` boolean column will be added to the `tutors` database table. +- **UI Structure:** A dedicated `/admin/tutors` page will handle tutor management. Course management will remain on `/admin/courses` but will be enhanced with superadmin-only actions (e.g., assigning tutors to courses). +- **Workspace:** Development will be done in `.worktrees/feature-superadmin-crud`. + +## Implementation Steps + +### 1. Workspace Isolation via Git Worktree +- Create a new git worktree: `git worktree add .worktrees/feature-superadmin-crud -b feature-superadmin-crud` +- All subsequent steps will be performed inside this isolated workspace. + +### 2. Database & Models +- Create migration `backend/migrations/002_add_superadmin.sql` to add `is_superadmin BOOLEAN NOT NULL DEFAULT 0` to the `tutors` table. +- Update `backend/demo/demo_seed.sql` to set the default `admin@tutortool.com` as a superadmin (`is_superadmin = 1`). +- Update `backend/src/models.rs` to include `is_superadmin: bool` in the `Tutor` struct. +- Add `CreateTutor` and `TutorResponse` structs to `backend/src/models.rs`. + +### 3. Auth & Core Backend +- Modify `backend/src/auth.rs` to include `is_superadmin: bool` in `TutorClaims`. This allows auth guards to check permissions efficiently. +- Update `backend/src/routes/auth_routes.rs` login handler to fetch `is_superadmin` and encode it in the JWT. +- Add a helper function to verify superadmin access to reject unauthorized requests. + +### 4. Tutors API +- Create `backend/src/routes/tutors.rs` with endpoints: + - `GET /api/admin/tutors` (list all tutors) + - `POST /api/admin/tutors` (create a tutor, hashing their password) + - `DELETE /api/admin/tutors/:id` (delete a tutor) +- Merge these routes in `backend/src/routes/mod.rs`. + +### 5. Course Assignments API +- Modify `backend/src/routes/courses.rs`: + - Enhance `GET /api/admin/courses` to return ALL courses if `claims.is_superadmin` is true, otherwise only return assigned courses. + - Restrict `POST /api/admin/courses` to superadmins only. + - Add `POST /api/admin/courses/:id/tutors` to assign a tutor to a course (superadmin only). + - Add `DELETE /api/admin/courses/:id/tutors/:tutor_id` to remove a tutor from a course (superadmin only). + - Add `GET /api/admin/courses/:id/tutors` to list tutors assigned to a course. + +### 6. Frontend Auth & API Client +- Update `frontend/src/lib/types.ts` to include `Tutor` and the new `is_superadmin` flag in token payload or state. +- Add the new endpoints to `frontend/src/lib/api.ts` under `api.admin.tutors` and enhance `api.admin.courses`. + +### 7. Frontend UI: Tutors Management +- Update `frontend/src/lib/components/TutorShell.svelte` to conditionally render a "Tutor:innen" link in the sidebar if the user is a superadmin. +- Create `frontend/src/routes/admin/tutors/+page.svelte` following the paper-bg design system. Include a list of tutors and a form to add a new tutor. + +### 8. Frontend UI: Courses Enhancements +- Modify `frontend/src/routes/admin/courses/+page.svelte` to show a "Tutor:innen zuweisen" (Assign Tutors) section for each course if the logged-in user is a superadmin. +- Restrict the course creation form to superadmins only. + +## Verification & Testing +- Run `cargo test` in the backend to ensure existing tests pass and new route isolation works. +- Perform a manual end-to-end test using the `make dev` script in the new worktree to verify the UI. diff --git a/frontend/src/lib/RoomCanvas.svelte b/frontend/src/lib/RoomCanvas.svelte index afbff95..b784594 100644 --- a/frontend/src/lib/RoomCanvas.svelte +++ b/frontend/src/lib/RoomCanvas.svelte @@ -21,7 +21,7 @@ occupiedSeatIds = [], mySeatId = null, studentNames = {} - } = $props(); + }: Props = $props(); let draggingId = $state(null); let startX = 0; @@ -39,7 +39,7 @@ function handleMouseMove(e: MouseEvent) { if (!draggingId || !editable) return; - const index = elements.findIndex(el => el.id === draggingId); + const index = elements.findIndex((el: LayoutElement) => el.id === draggingId); if (index === -1) return; const newX = Math.round((e.clientX - startX) / 10) * 10 / 40; diff --git a/frontend/src/routes/admin/export/+page.svelte b/frontend/src/routes/admin/export/+page.svelte index b43d7e4..18e5d94 100644 --- a/frontend/src/routes/admin/export/+page.svelte +++ b/frontend/src/routes/admin/export/+page.svelte @@ -1,85 +1,134 @@ -

Export Data

+
-
-
-

Global

-
-

Full Database Backup

-

Download the latest SQLite database file.

- + +
+
+
Datenexport
+
+ Exporte +
+
+
+ +
+ + +
+
+
Global
+ +
+ +
+
+
Datenbank-Backup
+
+ Lädt die aktuelle SQLite-Datenbank herunter. +
-
+
+ +
+
+ -
-

Per Course

- {#each courses as course} - + {/each} - - - {#if selectedCourseId} -
-

Full Attendance Matrix

-

All weeks, all students, includes bonus point calculation.

-
- - -
-
- -

Weekly Exports

-
- {#each sessions as session} -
- Week {session.week_nr} ({session.date}) -
- - -
-
- {/each} -
+ {/if} -
-
+
- + {#if selectedCourseId} +
+
+
Komplette Kursmatrix
+
+ Alle Wochen, alle Studierenden, inkl. Bonuspunkte-Berechnung. +
+
+
+ + +
+
+ +
+
Wochen-Exporte
+ +
+ {#if sessions.length === 0} +
+ Keine Sitzungen vorhanden. +
+ {:else} + + + {#each sessions as session, i} + + + + + {/each} + +
+
Woche {String(session.week_nr).padStart(2, '0')}
+
{session.date}
+
+
+ + +
+
+ {/if} +
+
+ {/if} + + +
+
diff --git a/k8s/ingress.yaml b/k8s/ingress.yaml new file mode 100644 index 0000000..b476da8 --- /dev/null +++ b/k8s/ingress.yaml @@ -0,0 +1,25 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: tutortool + namespace: tutortool + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + nginx.ingress.kubernetes.io/ssl-redirect: "true" +spec: + ingressClassName: nginx + tls: + - hosts: + - tutor.puchstein.dev + secretName: tutortool-tls + rules: + - host: tutor.puchstein.dev + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: tutortool + port: + number: 80 diff --git a/k8s/service.yaml b/k8s/service.yaml new file mode 100644 index 0000000..2c421c8 --- /dev/null +++ b/k8s/service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: tutortool + namespace: tutortool +spec: + selector: + app: tutortool + ports: + - protocol: TCP + port: 80 + targetPort: 3000 + type: ClusterIP