feat: Dunkelmodus, Fenstergröße merken, Papierkorb für Löschvorgänge (12.4, 14.3, 14.6)
Dunkelmodus über neuen Einstellungen-Tab "Darstellung" (Systemvorgabe/Hell/Dunkel), Fenstergröße/Maximiert-Status wird über Sitzungen hinweg gemerkt (bewusst ohne Fensterposition), und ein generischer Snapshot-basierter Papierkorb (30 Tage) für Sitzpläne, Noten, Notenschlüssel-Vorlagen, Aufgaben und Zeiteinträge. Details und bewusste Scope-Entscheidungen (Spaltenbreiten zurückgestellt, Farb-Audit für Dunkelmodus offen, welche Entitäten der Papierkorb abdeckt) in TODO.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2217,7 +2217,20 @@ von 4.2.2) der Tab "Kürzel-Katalog" für die Von/Nach-Kürzel des Stundenverlau
|
||||
- [ ] **12.2** Schuljahr-Einstellungen: Beginn/Ende, Halbjahresgrenze, Ferien
|
||||
(`SchoolYearService` erweitern).
|
||||
- [ ] **12.3** Standard-Notenschlüssel und Standard-Gewichtungsschema hinterlegen (2.3.3, 1.3.2).
|
||||
- [ ] **12.4** Darstellung: Hell/Dunkel-Modus, Schriftgröße.
|
||||
- [~] **12.4** Darstellung: Hell/Dunkel-Modus, Schriftgröße.
|
||||
|
||||
**Umsetzung (Hell/Dunkel):** neuer Tab "Darstellung" in
|
||||
[SettingsView.axaml](LehrerApp.Desktop/Views/Settings/SettingsView.axaml) mit Auswahl
|
||||
Systemvorgabe/Hell/Dunkel, gespeichert über
|
||||
[AppearanceSettingsService.cs](LehrerApp.Desktop/Services/AppearanceSettingsService.cs)
|
||||
(gleiches Muster wie `DashboardSettingsService`). `App.axaml` folgte über
|
||||
`RequestedThemeVariant="Default"` ohnehin schon der Systemvorgabe; neu ist die manuelle
|
||||
Umschaltung zur Laufzeit über `App.ApplyTheme(...)` (setzt `Application.Current!.
|
||||
RequestedThemeVariant`), ohne Neustart. Der gespeicherte Stand wird beim App-Start noch
|
||||
vor dem ersten Fenster angewendet. **Bewusst nicht Teil dieser Umsetzung:** ein Audit der
|
||||
ca. 23 XAML-Dateien mit fest codierten Hex-Farben statt Theme-Ressourcen — die
|
||||
Umschaltung selbst funktioniert, aber einzelne Stellen dürften im Dunkelmodus optisch
|
||||
nicht passen. Eigene, größere Aufgabe. Schriftgröße nicht umgesetzt.
|
||||
- [ ] **12.5** Speicherort der Datenbank anzeigen und ändern.
|
||||
- [ ] **12.6** Backup-Verwaltung (siehe 13.3) in den Einstellungen zugänglich machen.
|
||||
|
||||
@@ -2397,10 +2410,70 @@ von 4.2.2) der Tab "Kürzel-Katalog" für die Von/Nach-Kürzel des Stundenverlau
|
||||
- [ ] **14.1** Tastaturbedienung durchgängig: alle Hauptfunktionen ohne Maus erreichbar
|
||||
(Vorbild: Mitarbeit-Schnelleingabe).
|
||||
- [ ] **14.2** Globale Suche (Schüler, Gruppe, Klausur) über Tastenkürzel.
|
||||
- [ ] **14.3** Rückgängig-Funktion für Löschvorgänge (mindestens Bestätigungsdialog überall).
|
||||
- [~] **14.3** Rückgängig-Funktion für Löschvorgänge (mindestens Bestätigungsdialog überall).
|
||||
|
||||
**Umsetzung:** generischer, nicht-invasiver "Papierkorb light" statt einer
|
||||
`IsDeleted`-Markierung pro Modell (Letzteres hätte jede Lese-Abfrage der betroffenen
|
||||
Collections ändern müssen — bereits bei `Documentation.IsDeleted` sichtbar, das genau
|
||||
dieses Muster nutzt und **keine** Wiederherstellungs-Oberfläche hat). Stattdessen:
|
||||
[Trash.cs](LehrerApp.Core/Models/Trash.cs) definiert `TrashedItem` (Id, EntityType,
|
||||
EntityId, JSON-Snapshot, Anzeigetext, Löschzeitpunkt) als eigene LiteDB-Collection.
|
||||
`LiteDbContext.MoveToTrash<T>()`/`RestoreFromTrash<T>()` sind neue interne Hilfsmethoden,
|
||||
die von den bestehenden `Delete(id)`-Methoden einzelner Repositories vor dem eigentlichen
|
||||
Löschen aufgerufen werden — **keine neue Konstruktor-Abhängigkeit** für diese
|
||||
Repositories. `Restore(Guid trashId)` (neu auf den betroffenen Repository-Interfaces)
|
||||
dedserialisiert den Snapshot und ruft die **eigene** `Save()`-Methode auf, damit
|
||||
Validierung und `OnChange`-Sync-Ereignis wie bei jedem normalen Speichern greifen. Neuer
|
||||
Tab "Papierkorb" in
|
||||
[SettingsView.axaml](LehrerApp.Desktop/Views/Settings/SettingsView.axaml)
|
||||
([TrashViewModel.cs](LehrerApp.Desktop/ViewModels/Settings/TrashViewModel.cs)) listet alle
|
||||
Einträge mit Typ, Löschzeitpunkt und Restlaufzeit; "Wiederherstellen" dispatcht anhand des
|
||||
`EntityType` an das passende Repository. Aufbewahrung 30 Tage, Bereinigung
|
||||
(`ITrashRepository.PurgeOlderThan`) läuft beim App-Start.
|
||||
|
||||
**Bewusst nur für 5 Entitäten ohne Kaskaden umgesetzt:** `SeatingPlan`, `Grade`,
|
||||
`GradingKeyTemplate`, `WorkTask`, `TimeEntry` — jeweils hoher Alltagswert (Fehlklick beim
|
||||
Löschen eines Sitzplans/einer Note ist besonders ärgerlich) bei überschaubarem Risiko
|
||||
(keine abhängigen Collections). **Bewusst ausgeschlossen:** `LearningGroup` (14
|
||||
kaskadierende Collections beim Löschen — deutlich riskanter, eigener Umbau nötig),
|
||||
`Exam` (kaskadiert `ExamResult`), `ParticipationSession` (kaskadiert
|
||||
`ParticipationEntry`), sowie alle übrigen, einfacheren Entitäten (u. a. `ShorthandCode`,
|
||||
`SubstitutionEntry`, `SupervisionDuty`, `TimetableSlot`, `SchoolHoliday`,
|
||||
`CompetencyDomain`, `Subject`, `Student`, `Unit`, `GroupMembership`,
|
||||
`ParticipationAspect`, `ParticipationSection`, `AlternativeLessonPath`, `GradingScheme`,
|
||||
`ReportGrade`) — der Mechanismus ist bewusst so gebaut, dass sich weitere Entitäten später
|
||||
mit demselben Muster (drei Zeilen in `Delete()`, eine `Restore()`-Methode) ergänzen lassen.
|
||||
Der Papierkorb ist **rein lokal, nicht Teil des Geräte-Sync** — `TrashedItems` löst nie
|
||||
`db.OnChange` aus und landet damit nie im Sync-Ereignisstrom; nur das eigentliche
|
||||
Save-/Delete-Ereignis der betroffenen Entität synchronisiert wie bisher. Bewusste
|
||||
Vereinfachung, um keine neue Sync-Infrastruktur (Ereignistyp, Konfliktbehandlung für
|
||||
Papierkorb-Einträge) einzuführen. Tests: 10 in
|
||||
[TrashTests.cs](LehrerApp.Data.Tests/TrashTests.cs) (Löschen/Wiederherstellen je
|
||||
Repository, Sortierung, Bereinigung), 6 in
|
||||
[TrashViewModelTests.cs](LehrerApp.Desktop.Tests/TrashViewModelTests.cs).
|
||||
- [ ] **14.4** Ladeanzeigen bei längeren Operationen (Import, Sync, Export).
|
||||
- [ ] **14.5** Leere Zustände mit Handlungsaufforderung statt leerer Tabellen.
|
||||
- [ ] **14.6** Fenstergröße und Spaltenbreiten über Sitzungen hinweg merken.
|
||||
- [~] **14.6** Fenstergröße und Spaltenbreiten über Sitzungen hinweg merken.
|
||||
|
||||
**Umsetzung (Fenstergröße):**
|
||||
[WindowSettingsService.cs](LehrerApp.Desktop/Services/WindowSettingsService.cs)
|
||||
(gleiches Muster wie `DashboardSettingsService`) speichert Breite/Höhe/Maximiert-Status
|
||||
beim Schließen und stellt sie beim Start wieder her
|
||||
(`MainWindow.EnableWindowSizePersistence`). **Bewusst nicht gespeichert: die
|
||||
Fensterposition** — bei wechselnder Monitor-Konfiguration (Laptop im Unterricht, externer
|
||||
Monitor zuhause) könnte das Fenster sonst außerhalb des sichtbaren Bereichs landen. War die
|
||||
App beim Schließen maximiert, bleibt die zuletzt bekannte "normale" Größe erhalten statt
|
||||
der (dann bedeutungslosen) Bildschirmgröße. Tests in
|
||||
[WindowSettingsServiceTests.cs](LehrerApp.Desktop.Tests/WindowSettingsServiceTests.cs).
|
||||
|
||||
**Spaltenbreiten: zurückgestellt.** Bestandsaufnahme aller 9 `DataGrid`s ergab: 5 haben
|
||||
eine feste, in XAML deklarierte Spaltenliste (dort wäre Persistierung tatsächlich billig),
|
||||
aber 4 bauen ihre Spalten im Code-behind dynamisch aus den Daten neu auf — je eine Spalte
|
||||
pro Klausur bzw. Mitarbeits-Aspekt (`GradeOverviewTabView`, `ParticipationTabView`,
|
||||
`ExamGradingDialog`, `AttendanceHomeworkQuickInputDialog`). Für diese vier gibt es keine
|
||||
stabile Spalten-Identität, an der eine gespeicherte Breite verlässlich hängen könnte —
|
||||
eine generische Lösung ist damit kein "billiger" Zusatz, sondern ein eigener, größerer
|
||||
Umbau. Nicht in diesem Durchgang umgesetzt.
|
||||
- [ ] **14.7** Bedienung auf Touch-Geräten prüfen (Tablet im Unterricht).
|
||||
- [ ] **14.8** Responsive Layout und Windows-DPI prüfen (kleine Notebook-Auflösungen sowie
|
||||
125/150/200 % Skalierung; starre Master-Detail-Spalten bei Bedarf stapeln). Der kompakte
|
||||
|
||||
Reference in New Issue
Block a user