feat: Geräte-Pairing-UI + Einstellungen-Neustrukturierung

- Sync-Settings-Tab: neuer Bereich "Gerät koppeln" (10.3.1), nutzt den
  bereits vorhandenen, bisher nirgends verdrahteten SnapshotService.
  Ein Gerät erzeugt per CreatePairingCode einen verschlüsselten
  DB-Snapshot + Einmal-Code, das zweite Gerät übernimmt per
  RedeemPairingCode Datenbank und Sync-Schlüssel. Bestätigungsdialog
  vor dem Einlösen, da die lokale Datenbank dabei vollständig ersetzt
  wird (alter Stand wird automatisch als Backup gesichert).
- Einstellungen: TabPlacement von Top auf Left umgestellt (vertikale
  Liste statt langem horizontalem Balken) und die 13 Tabs in drei
  logische Gruppen sortiert (Fachliches / Zeitplanung / System).
- Neuer SettingsTab-Enum ersetzt rohe int-Tab-Indizes bei
  MainWindowViewModel.NavigateToSettings/
  TimetableViewModel.OnNavigateToSettings. Dabei einen bestehenden Bug
  gefunden und mitbehoben: das Zahnrad im Stundenplan öffnete über den
  hartcodierten Index 7 tatsächlich "Datenschutz" statt des laut
  Kommentar/Tooltip beabsichtigten "Ferien & Feiertage".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 01:09:25 +02:00
co-authored by Claude Sonnet 5
parent 65c2211dab
commit f8058b537b
8 changed files with 411 additions and 234 deletions
@@ -20,6 +20,7 @@ public partial class SettingsView : UserControl
vm.OnConfirmRestore = ShowRestoreConfirmDialog;
vm.OnAppLockChanged = () => App.Services.GetRequiredService<AppLockViewModel>().ApplyConfig();
vm.OnConfirmHardDelete = ShowHardDeleteConfirmDialog;
vm.OnConfirmPairingRestore = ShowPairingRestoreConfirmDialog;
}
}
@@ -50,6 +51,21 @@ public partial class SettingsView : UserControl
return owner is not null && await dialog.ShowDialog<bool>(owner);
}
private async Task<bool> ShowPairingRestoreConfirmDialog()
{
var info = new ConfirmDialogInfo
{
Title = "Gerät koppeln?",
Message = "Die lokale Datenbank auf diesem Gerät wird vollständig durch den Stand des anderen " +
"Geräts ersetzt. Der bisherige Stand wird automatisch als Backup gesichert. " +
"Die App wird danach automatisch neu gestartet.",
ConfirmText = "Ersetzen",
};
var dialog = new ConfirmDialog { DataContext = info };
var owner = TopLevel.GetTopLevel(this) as Window;
return owner is not null && await dialog.ShowDialog<bool>(owner);
}
private async void OnImportClick(object? sender, RoutedEventArgs e)
{
var topLevel = TopLevel.GetTopLevel(this);