Files
LehrerApp/LehrerApp.Desktop/Views/Settings/SettingsView.axaml
T
adminandClaude Sonnet 5 4eb4d0a946 WIP (unstable): WebUntis-iCal-Abgleich für Vertretungen/Ausfälle
Erkennt Vertretungen, Ausfälle und Zusatzaufsichten aus dem persönlichen
WebUntis-iCal-Feed und schreibt sie automatisch als SubstitutionEntry.
Bekannter offener Bug: es tauchen weiterhin falsche Vertretungen für
Stunden auf, die real unverändert sind — wird in einem Folge-Commit
untersucht, deshalb vorerst auf diesem Branch statt main.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 13:30:15 +02:00

1023 lines
60 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:LehrerApp.Desktop.ViewModels.Settings"
xmlns:services="clr-namespace:LehrerApp.Core.Services;assembly=LehrerApp.Core"
xmlns:shared="clr-namespace:LehrerApp.Desktop.Views.Shared"
x:Class="LehrerApp.Desktop.Views.Settings.SettingsView"
x:DataType="vm:SettingsViewModel">
<Grid RowDefinitions="Auto,*">
<shared:PageHeader Grid.Row="0" Title="Einstellungen" Margin="32,28,32,0"/>
<TabbedPage Grid.Row="1" TabPlacement="Left" SelectedIndex="{Binding ActiveTabIndex}">
<!-- ═══ Fachliches ═══ -->
<!-- Tab: Fächer -->
<ContentPage Header="Fächer">
<ScrollViewer>
<StackPanel Margin="32,20,32,28" Spacing="14" MaxWidth="640">
<ItemsControl ItemsSource="{Binding Subjects}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="vm:SubjectListItem">
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
BorderThickness="0,0,0,1" Padding="0,7">
<Grid ColumnDefinitions="*,80,Auto">
<TextBlock Grid.Column="0" Text="{Binding Name}"
VerticalAlignment="Center" FontSize="14"/>
<TextBlock Grid.Column="1" Text="{Binding ShortName}"
VerticalAlignment="Center" Opacity="0.5" FontSize="13"/>
<Button Grid.Column="2" Content="Löschen" FontSize="12" Padding="10,4"
Command="{Binding $parent[ItemsControl].((vm:SettingsViewModel)DataContext).DeleteSubjectCommand}"
CommandParameter="{Binding}"/>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
BorderThickness="1" CornerRadius="6" Padding="14,12">
<StackPanel Spacing="10">
<TextBlock Text="Neues Fach" FontSize="12" FontWeight="SemiBold" Opacity="0.7"/>
<Grid ColumnDefinitions="*,12,100,12,Auto">
<TextBox Grid.Column="0" Text="{Binding NewName}"
PlaceholderText="Name (z.B. Chemie)"/>
<TextBox Grid.Column="2" Text="{Binding NewShort}"
PlaceholderText="Kürzel (z.B. Ch)"/>
<Button Grid.Column="4" Content="Hinzufügen"
Command="{Binding AddSubjectCommand}"/>
</Grid>
<TextBlock Text="{Binding NewNameError}" Foreground="Red" FontSize="12"
IsVisible="{Binding NewNameError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
</ContentPage>
<!-- Tab: Kürzel-Katalog -->
<ContentPage Header="Kürzel-Katalog">
<ScrollViewer>
<StackPanel Margin="32,20,32,28" Spacing="14" MaxWidth="640">
<TextBlock Text="Kürzel für den Stundenverlaufsplan (Kapitel 4.2), z.B. Tb = Tafelbild. Im Stundeneditor werden Von/Nach aus diesen Kürzeln zu einem Kurzsymbol wie &quot;Tb-&gt;SH&quot; kombiniert."
FontSize="12" Opacity="0.6" TextWrapping="Wrap"/>
<ItemsControl ItemsSource="{Binding ShorthandCodes}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="vm:ShorthandCodeListItem">
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
BorderThickness="0,0,0,1" Padding="0,7">
<Grid ColumnDefinitions="80,*,Auto">
<TextBlock Grid.Column="0" Text="{Binding Code}"
VerticalAlignment="Center" FontSize="14" FontWeight="SemiBold"/>
<TextBlock Grid.Column="1" Text="{Binding Label}"
VerticalAlignment="Center" Opacity="0.7" FontSize="13"/>
<Button Grid.Column="2" Content="Löschen" FontSize="12" Padding="10,4"
Command="{Binding $parent[ItemsControl].((vm:SettingsViewModel)DataContext).DeleteShorthandCodeCommand}"
CommandParameter="{Binding}"/>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
BorderThickness="1" CornerRadius="6" Padding="14,12">
<StackPanel Spacing="10">
<TextBlock Text="Neues Kürzel" FontSize="12" FontWeight="SemiBold" Opacity="0.7"/>
<Grid ColumnDefinitions="100,12,*,12,Auto">
<TextBox Grid.Column="0" Text="{Binding NewShorthandCode}"
PlaceholderText="Kürzel (z.B. Tb)"/>
<TextBox Grid.Column="2" Text="{Binding NewShorthandLabel}"
PlaceholderText="Bedeutung (z.B. Tafelbild)"/>
<Button Grid.Column="4" Content="Hinzufügen"
Command="{Binding AddShorthandCodeCommand}"/>
</Grid>
<TextBlock Text="{Binding NewShorthandCodeError}" Foreground="Red" FontSize="12"
IsVisible="{Binding NewShorthandCodeError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
</ContentPage>
<!-- Tab: Kompetenzen -->
<ContentPage Header="Kompetenzen">
<ScrollViewer>
<StackPanel Margin="32,20,32,28" Spacing="14" MaxWidth="640">
<!-- Fach + Klassenstufe Auswahl + Import/Export -->
<Grid ColumnDefinitions="*,12,100,12,Auto,8,Auto,8,Auto">
<ComboBox Grid.Column="0"
ItemsSource="{Binding Subjects}"
SelectedItem="{Binding CatalogSubject}"
PlaceholderText="Fach auswählen"
HorizontalAlignment="Stretch">
<ComboBox.ItemTemplate>
<DataTemplate DataType="vm:SubjectListItem">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<NumericUpDown Grid.Column="2"
Value="{Binding CatalogGradeLevel}"
Minimum="1" Maximum="13" FormatString="0"/>
<Button Grid.Column="4" Content="JSON importieren" Click="OnImportClick"
IsEnabled="{Binding CatalogSubject, Converter={x:Static ObjectConverters.IsNotNull}}"/>
<Button Grid.Column="6" Content="JSON exportieren" Click="OnExportClick"
IsEnabled="{Binding CatalogSubject, Converter={x:Static ObjectConverters.IsNotNull}}"/>
<Button Grid.Column="8" Content="Kopieren…" Click="OnCopyCatalogClick"
IsEnabled="{Binding CatalogSubject, Converter={x:Static ObjectConverters.IsNotNull}}"
ToolTip.Tip="Katalog in eine andere Klassenstufe kopieren"/>
</Grid>
<!-- Katalog-Validierungsmeldung -->
<TextBlock Text="{Binding CatalogValidation}" Foreground="Red" FontSize="12"
IsVisible="{Binding CatalogValidation, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<!-- Leerer Zustand -->
<TextBlock Text="Kein Fach ausgewählt." Classes="emptyhint"
IsVisible="{Binding CatalogSubject, Converter={x:Static ObjectConverters.IsNull}}"/>
<!-- Domain-Liste -->
<ItemsControl ItemsSource="{Binding Domains}"
IsVisible="{Binding CatalogSubject, Converter={x:Static ObjectConverters.IsNotNull}}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="vm:DomainEditItem">
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
BorderThickness="1" CornerRadius="6" Margin="0,0,0,8" Padding="14,12">
<StackPanel Spacing="8">
<!-- Bereichs-Header -->
<Grid ColumnDefinitions="*,Auto,4,Auto,8,Auto">
<TextBlock Grid.Column="0" Text="{Binding DisplayName}"
FontWeight="SemiBold" FontSize="14" VerticalAlignment="Center"/>
<Button Grid.Column="1" Content="↑" FontSize="13" Padding="8,3"
Command="{Binding MoveUpCommand}" ToolTip.Tip="Bereich nach oben"/>
<Button Grid.Column="3" Content="↓" FontSize="13" Padding="8,3"
Command="{Binding MoveDownCommand}" ToolTip.Tip="Bereich nach unten"/>
<Button Grid.Column="5" Content="Bereich löschen" FontSize="12" Padding="10,4"
Command="{Binding $parent[ItemsControl].((vm:SettingsViewModel)DataContext).DeleteDomainCommand}"
CommandParameter="{Binding}"/>
</Grid>
<!-- Kompetenz-Items -->
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="vm:CompetencyItemVm">
<Grid ColumnDefinitions="*,Auto,4,Auto,6,Auto" Margin="0,3">
<TextBlock Grid.Column="0" Text="{Binding Display}"
TextWrapping="Wrap" VerticalAlignment="Center"
FontSize="13"/>
<Button Grid.Column="1" Content="↑" Padding="7,2" FontSize="12"
Command="{Binding MoveUpCommand}" ToolTip.Tip="Kompetenz nach oben"/>
<Button Grid.Column="3" Content="↓" Padding="7,2" FontSize="12"
Command="{Binding MoveDownCommand}" ToolTip.Tip="Kompetenz nach unten"/>
<Button Grid.Column="5" Content="×" Padding="7,2" FontSize="13"
Command="{Binding DeleteCommand}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- Kompetenz hinzufügen -->
<Grid ColumnDefinitions="80,8,*,8,Auto">
<TextBox Grid.Column="0" Text="{Binding NewItemCode}"
PlaceholderText="EG1" FontSize="12"/>
<TextBox Grid.Column="2" Text="{Binding NewItemDesc}"
PlaceholderText="Kompetenz-Beschreibung" FontSize="12"/>
<Button Grid.Column="4" Content=""
Command="{Binding AddItemCommand}"/>
</Grid>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- Neuen Bereich hinzufügen -->
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
BorderThickness="1" CornerRadius="6" Padding="14,12"
IsVisible="{Binding CatalogSubject, Converter={x:Static ObjectConverters.IsNotNull}}">
<StackPanel Spacing="10">
<TextBlock Text="Neuer Bereich" FontSize="12" FontWeight="SemiBold" Opacity="0.7"/>
<Grid ColumnDefinitions="*,12,90,12,Auto">
<TextBox Grid.Column="0" Text="{Binding NewDomainName}"
PlaceholderText="Bereichsname (z.B. Erkenntnisgewinnung)"/>
<TextBox Grid.Column="2" Text="{Binding NewDomainCode}"
PlaceholderText="Kürzel (z.B. EG)"/>
<Button Grid.Column="4" Content="Hinzufügen"
Command="{Binding AddDomainCommand}"/>
</Grid>
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
</ContentPage>
<!-- Tab: Notenschlüssel-Vorlagen -->
<ContentPage Header="Notenschlüssel-Vorlagen">
<ScrollViewer>
<StackPanel Margin="32,20,32,28" Spacing="14" MaxWidth="640">
<TextBlock Text="Noch keine Vorlagen angelegt." Classes="emptyhint"
IsVisible="{Binding !GradingKeyTemplateList.Count}"/>
<ItemsControl ItemsSource="{Binding GradingKeyTemplateList}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="vm:GradingKeyTemplateEditItem">
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
BorderThickness="0,0,0,1" Padding="0,9">
<Grid ColumnDefinitions="*,Auto,Auto">
<StackPanel Grid.Column="0">
<TextBlock Text="{Binding Name}" FontWeight="SemiBold" FontSize="14"/>
<TextBlock FontSize="12" Opacity="0.5">
<Run Text="{Binding GradingSystemLabel}"/>
<Run Text=" · "/>
<Run Text="{Binding Entries.Count}"/>
<Run Text=" Stufen"/>
</TextBlock>
<TextBlock Text="{Binding CompletenessWarning}" Foreground="Red" FontSize="11"
TextWrapping="Wrap"
IsVisible="{Binding CompletenessWarning, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
<Button Grid.Column="1" Content="Bearbeiten" FontSize="12" Padding="10,4"
Margin="8,0,0,0" Tag="{Binding}" Click="OnEditGradingKeyTemplateClick"/>
<Button Grid.Column="2" Content="Löschen" FontSize="12" Padding="10,4"
Margin="8,0,0,0"
Command="{Binding $parent[ItemsControl].((vm:SettingsViewModel)DataContext).DeleteGradingKeyTemplateCommand}"
CommandParameter="{Binding}"/>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- Neue Vorlage anlegen -->
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
BorderThickness="1" CornerRadius="6" Padding="14,12">
<StackPanel Spacing="10">
<TextBlock Text="Neue Vorlage" FontSize="12" FontWeight="SemiBold" Opacity="0.7"/>
<Grid ColumnDefinitions="*,12,140,12,Auto">
<TextBox Grid.Column="0" Text="{Binding NewTemplateName}"
PlaceholderText="Vorlagenname (z.B. Standard Oberstufe)"/>
<ComboBox Grid.Column="2" ItemsSource="{Binding GradingSystemOptions}"
SelectedItem="{Binding NewTemplateGradingSystemName}"
HorizontalAlignment="Stretch"/>
<Button Grid.Column="4" Content="Anlegen"
Command="{Binding AddGradingKeyTemplateCommand}"/>
</Grid>
<TextBlock Text="{Binding NewTemplateNameError}" Foreground="Red" FontSize="12"
IsVisible="{Binding NewTemplateNameError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
</ContentPage>
<!-- Tab: Notenschema -->
<ContentPage Header="Notenschema">
<ContentPage.Resources>
<DataTemplate x:Key="GradingSchemeTemplate" DataType="vm:GradingSchemeEditItem">
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
BorderThickness="1" CornerRadius="6" Padding="14,12">
<StackPanel Spacing="8">
<TextBlock Text="{Binding Label}" FontWeight="SemiBold" FontSize="14"/>
<Grid ColumnDefinitions="Auto,90,Auto,90,Auto,90" ColumnSpacing="6">
<TextBlock Grid.Column="0" Text="Klausuren %" VerticalAlignment="Center" FontSize="12"/>
<NumericUpDown Grid.Column="1" Value="{Binding ExamsPercent}" Minimum="0" Maximum="100"
FormatString="0.#" ShowButtonSpinner="False"/>
<TextBlock Grid.Column="2" Text="Mitarbeit %" VerticalAlignment="Center" FontSize="12"/>
<NumericUpDown Grid.Column="3" Value="{Binding ParticipationPercent}" Minimum="0" Maximum="100"
FormatString="0.#" ShowButtonSpinner="False"/>
<TextBlock Grid.Column="4" Text="Sonstige %" VerticalAlignment="Center" FontSize="12"/>
<NumericUpDown Grid.Column="5" Value="{Binding OtherPercent}" Minimum="0" Maximum="100"
FormatString="0.#" ShowButtonSpinner="False"/>
</Grid>
<TextBlock Text="{Binding ValidationMessage}" Foreground="Red" FontSize="12"
IsVisible="{Binding ValidationMessage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<Grid ColumnDefinitions="*,Auto">
<TextBlock Grid.Column="0" Text="{Binding StatusMessage}" Foreground="Green" FontSize="12"
VerticalAlignment="Center"
IsVisible="{Binding StatusMessage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<Button Grid.Column="1" Content="Speichern" Command="{Binding SaveCommand}"/>
</Grid>
</StackPanel>
</Border>
</DataTemplate>
</ContentPage.Resources>
<ScrollViewer>
<StackPanel Margin="32,20,32,28" Spacing="14" MaxWidth="480">
<TextBlock Text="Voreinstellung der Gewichtung für die Zeugnisnote (Klausuren / Mitarbeit / Sonstige), je Gruppentyp. Kann pro Lerngruppe überschrieben werden."
FontSize="12" Opacity="0.6" TextWrapping="Wrap"/>
<ContentControl Content="{Binding ClassScheme}" ContentTemplate="{StaticResource GradingSchemeTemplate}"/>
<ContentControl Content="{Binding CourseScheme}" ContentTemplate="{StaticResource GradingSchemeTemplate}"/>
</StackPanel>
</ScrollViewer>
</ContentPage>
<!-- Tab: Word-Briefvorlagen (7.1.4 / 11.5) -->
<ContentPage Header="Briefvorlagen">
<ScrollViewer>
<StackPanel Margin="32,20,32,28" Spacing="16" MaxWidth="760">
<TextBlock Text="Word-Vorlagen bleiben vollständig in Word gestaltet. Die App befüllt Inhaltssteuerelemente anhand ihres Tags und prüft die Vorlage bereits beim Import."
FontSize="12" Opacity="0.65" TextWrapping="Wrap"/>
<Grid ColumnDefinitions="Auto,*" ColumnSpacing="12">
<Button Grid.Column="0" Content=" DOCX-Vorlage importieren" Click="OnImportLetterTemplateClick"/>
<TextBlock Grid.Column="1" Text="{Binding LetterTemplateStatus}" FontSize="12"
VerticalAlignment="Center" TextWrapping="Wrap"
IsVisible="{Binding LetterTemplateStatus, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</Grid>
<TextBlock Text="Noch keine Briefvorlage importiert." Classes="emptyhint"
IsVisible="{Binding !LetterTemplateList.Count}"/>
<ItemsControl ItemsSource="{Binding LetterTemplateList}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="vm:LetterTemplateListItem">
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
BorderThickness="1" CornerRadius="7" Padding="14,12" Margin="0,0,0,9">
<StackPanel Spacing="8">
<Grid ColumnDefinitions="*,Auto,Auto,Auto">
<StackPanel Grid.Column="0">
<TextBlock Text="{Binding Name}" FontWeight="SemiBold" FontSize="14"/>
<TextBlock FontSize="11" Opacity="0.55">
<Run Text="{Binding OriginalFileName}"/><Run Text=" · "/>
<Run Text="{Binding ValidationSummary}"/>
</TextBlock>
</StackPanel>
<Button Grid.Column="1" Content="Öffnen" FontSize="12" Padding="10,4"
Margin="8,0,0,0" Tag="{Binding}" Click="OnOpenLetterTemplateClick"/>
<Button Grid.Column="2" Content="Neu prüfen" FontSize="12" Padding="10,4"
Margin="8,0,0,0"
Command="{Binding $parent[ItemsControl].((vm:SettingsViewModel)DataContext).ValidateLetterTemplateCommand}"
CommandParameter="{Binding}"/>
<Button Grid.Column="3" Content="Löschen" FontSize="12" Padding="10,4"
Margin="8,0,0,0"
Command="{Binding $parent[ItemsControl].((vm:SettingsViewModel)DataContext).DeleteLetterTemplateCommand}"
CommandParameter="{Binding}"/>
</Grid>
<ItemsControl ItemsSource="{Binding Issues}" IsVisible="{Binding HasIssues}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="vm:LetterTemplateIssueItem">
<Grid ColumnDefinitions="24,*" Margin="0,2">
<TextBlock Grid.Column="0" Text="{Binding Icon}" Foreground="{Binding Color}"/>
<TextBlock Grid.Column="1" Text="{Binding Message}" Foreground="{Binding Color}"
FontSize="12" TextWrapping="Wrap"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBlock Text="✓ Vorlage ohne Auffälligkeiten" Foreground="SeaGreen" FontSize="12"
IsVisible="{Binding HasNoIssues}"/>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Separator/>
<TextBlock Text="Unterstützte Tags" FontSize="15" FontWeight="SemiBold"/>
<TextBlock Text="In Word: Entwicklertools → Rich-Text- oder Nur-Text-Inhaltssteuerelement → Eigenschaften → Tag. Der Titel ist frei wählbar; ausgewertet wird der Tag."
FontSize="12" Opacity="0.65" TextWrapping="Wrap"/>
<ItemsControl ItemsSource="{Binding SupportedLetterPlaceholders}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="services:LetterPlaceholder">
<Grid ColumnDefinitions="190,*" Margin="0,3">
<TextBlock Grid.Column="0" Text="{Binding Tag}" FontFamily="Monospace" FontSize="12"/>
<TextBlock Grid.Column="1" Text="{Binding Description}" FontSize="12" Opacity="0.7"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
</ContentPage>
<!-- ═══ Zeitplanung ═══ -->
<!-- Tab: Ferien & Feiertage (4.3.5) -->
<ContentPage Header="Ferien &amp; Feiertage">
<ScrollViewer>
<StackPanel Margin="32,20,32,28" Spacing="14" MaxWidth="480">
<StackPanel Spacing="4">
<TextBlock Text="Bundesland (für Feiertage)" FontSize="13" FontWeight="SemiBold"/>
<ComboBox ItemsSource="{Binding StateOptions}" SelectedItem="{Binding SelectedStateName}"
HorizontalAlignment="Stretch"/>
</StackPanel>
<Separator Margin="0,4"/>
<TextBlock Text="Schulferien" FontSize="16" FontWeight="SemiBold"/>
<TextBlock Text="Werden im Stundenplan als unterrichtsfreie Tage angezeigt."
FontSize="12" Opacity="0.6" TextWrapping="Wrap"/>
<ItemsControl ItemsSource="{Binding SchoolHolidayEntries}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="vm:SchoolHolidayItem">
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
BorderThickness="0,0,0,1" Padding="0,7">
<Grid ColumnDefinitions="*,Auto">
<StackPanel Grid.Column="0">
<TextBlock Text="{Binding Name}" FontSize="13" FontWeight="SemiBold"/>
<TextBlock Text="{Binding RangeDisplay}" FontSize="12" Opacity="0.6"/>
</StackPanel>
<Button Grid.Column="1" Content="×" FontSize="14" Padding="9,3"
Command="{Binding $parent[ItemsControl].((vm:SettingsViewModel)DataContext).RemoveSchoolHolidayCommand}"
CommandParameter="{Binding}"/>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBlock Text="Noch keine Schulferien hinterlegt." Classes="emptyhint"
IsVisible="{Binding !SchoolHolidayEntries.Count}"/>
<Separator Margin="0,4"/>
<StackPanel Spacing="4">
<TextBox Text="{Binding NewHolidayName}" PlaceholderText="Name (z.B. Sommerferien)"/>
<TextBlock Text="{Binding HolidayNameError}" Foreground="Red" FontSize="11"
IsVisible="{Binding HolidayNameError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<Grid ColumnDefinitions="*,8,*">
<TextBox Grid.Column="0" Text="{Binding NewHolidayStartText}" PlaceholderText="Beginn TT.MM.JJJJ"/>
<TextBox Grid.Column="2" Text="{Binding NewHolidayEndText}" PlaceholderText="Ende TT.MM.JJJJ"/>
</Grid>
<TextBlock Text="{Binding HolidayDateError}" Foreground="Red" FontSize="11"
IsVisible="{Binding HolidayDateError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<Button Content=" Schulferien hinzufügen" Command="{Binding AddSchoolHolidayCommand}"
HorizontalAlignment="Left"/>
</StackPanel>
</StackPanel>
</ScrollViewer>
</ContentPage>
<!-- Tab: Stundenraster (4.2.2 Nachtrag) -->
<ContentPage Header="Stundenraster">
<ScrollViewer>
<StackPanel Margin="32,20,32,28" Spacing="14" MaxWidth="420">
<TextBlock Text="Uhrzeiten der Einzelstunden" FontSize="16" FontWeight="SemiBold"/>
<TextBlock FontSize="12" Opacity="0.6" TextWrapping="Wrap"
Text="Grundlage für die Zeitbedarf-Rückmeldung im Verlaufsplan-Editor. Nicht alle Stunden müssen eingetragen sein — für unkonfigurierte Stunden bleibt die Rückmeldung dort einfach aus."/>
<Grid ColumnDefinitions="70,*,8,*" Margin="0,4,0,0">
<TextBlock Grid.Column="1" Text="Beginn" FontSize="11" FontWeight="Bold" Opacity="0.5"/>
<TextBlock Grid.Column="3" Text="Ende" FontSize="11" FontWeight="Bold" Opacity="0.5"/>
</Grid>
<ItemsControl ItemsSource="{Binding PeriodTimes}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="vm:PeriodTimeEditItem">
<Grid ColumnDefinitions="70,*,8,*" Margin="0,3">
<TextBlock Grid.Column="0" Text="{Binding PeriodLabel}" FontSize="13" VerticalAlignment="Center"/>
<TextBox Grid.Column="1" Text="{Binding StartText}" PlaceholderText="HH:MM"/>
<TextBox Grid.Column="3" Text="{Binding EndText}" PlaceholderText="HH:MM"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBlock Text="{Binding PeriodTimesError}" Foreground="Red" FontSize="12"
IsVisible="{Binding PeriodTimesError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<Button Content="Speichern" Command="{Binding SavePeriodTimesCommand}" HorizontalAlignment="Left"/>
<TextBlock Text="{Binding PeriodTimesStatus}" Foreground="Green" FontSize="12"
IsVisible="{Binding PeriodTimesStatus, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
</ScrollViewer>
</ContentPage>
<!-- Tab: Aufsichten (4.3 Nachtrag) -->
<ContentPage Header="Aufsichten">
<ScrollViewer>
<StackPanel Margin="32,20,32,28" Spacing="14" MaxWidth="480">
<TextBlock Text="Wiederkehrende Pausenaufsicht" FontSize="16" FontWeight="SemiBold"/>
<TextBlock FontSize="12" Opacity="0.6" TextWrapping="Wrap"
Text="Wird im Stundenplan zwischen den betroffenen Stunden angezeigt. Einmalige Vertretungsaufsichten trägst du direkt im Stundenplan (Heute-Ansicht) ein, nicht hier."/>
<ItemsControl ItemsSource="{Binding SupervisionDuties}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="vm:SupervisionDutyItem">
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
BorderThickness="0,0,0,1" Padding="0,7">
<Grid ColumnDefinitions="*,Auto">
<StackPanel Grid.Column="0">
<TextBlock FontSize="13" FontWeight="SemiBold">
<Run Text="{Binding WeekdayLabel}"/><Run Text=" — "/><Run Text="{Binding PeriodLabel}"/>
</TextBlock>
<TextBlock Text="{Binding Location}" FontSize="12" Opacity="0.6"/>
</StackPanel>
<Button Grid.Column="1" Content="×" FontSize="14" Padding="9,3"
Command="{Binding $parent[ItemsControl].((vm:SettingsViewModel)DataContext).RemoveSupervisionDutyCommand}"
CommandParameter="{Binding}"/>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBlock Text="Noch keine Aufsicht hinterlegt." Classes="emptyhint"
IsVisible="{Binding !SupervisionDuties.Count}"/>
<Separator Margin="0,4"/>
<Grid ColumnDefinitions="*,8,Auto">
<StackPanel Grid.Column="0" Spacing="4">
<TextBlock Text="Wochentag" FontSize="12" Opacity="0.7"/>
<ComboBox ItemsSource="{Binding WeekdayOptions}" SelectedItem="{Binding NewDutyWeekdayName}"
HorizontalAlignment="Stretch"/>
</StackPanel>
<StackPanel Grid.Column="2" Spacing="4">
<TextBlock Text="Nach Stunde (0 = davor)" FontSize="12" Opacity="0.7"/>
<NumericUpDown Value="{Binding NewDutyAfterPeriod}" Minimum="0" Maximum="10" FormatString="0"
Width="140" ShowButtonSpinner="True"/>
</StackPanel>
</Grid>
<StackPanel Spacing="4">
<TextBlock Text="Ort / Bezeichnung" FontSize="12" Opacity="0.7"/>
<TextBox Text="{Binding NewDutyLocation}" PlaceholderText="z.B. Pausenhof"/>
</StackPanel>
<TextBlock Text="{Binding NewDutyError}" Foreground="Red" FontSize="12"
IsVisible="{Binding NewDutyError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<Button Content=" Aufsicht hinzufügen" Command="{Binding AddSupervisionDutyCommand}"
HorizontalAlignment="Left"/>
</StackPanel>
</ScrollViewer>
</ContentPage>
<!-- ═══ System ═══ -->
<!-- Tab: Sicherheit (13.3) -->
<ContentPage Header="Sicherheit">
<ScrollViewer>
<StackPanel Margin="32,20,32,28" Spacing="24" MaxWidth="560">
<!-- Datensicherung -->
<StackPanel Spacing="10">
<TextBlock Text="Datensicherung" FontSize="16" FontWeight="SemiBold"/>
<TextBlock Text="Bei jedem Start wird automatisch ein Backup der Datenbank angelegt (die letzten 10 werden behalten)."
FontSize="12" Opacity="0.6" TextWrapping="Wrap"/>
<Button Content="Jetzt sichern" Command="{Binding CreateBackupNowCommand}"
HorizontalAlignment="Left"/>
<TextBlock Text="{Binding BackupStatus}" Foreground="Green" FontSize="12"
IsVisible="{Binding BackupStatus, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<ItemsControl ItemsSource="{Binding Backups}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="vm:BackupListItem">
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
BorderThickness="0,0,0,1" Padding="0,7">
<Grid ColumnDefinitions="*,Auto">
<TextBlock Grid.Column="0" Text="{Binding Display}"
VerticalAlignment="Center" FontSize="13"/>
<Button Grid.Column="1" Content="Wiederherstellen" FontSize="12" Padding="10,4"
Command="{Binding $parent[ItemsControl].((vm:SettingsViewModel)DataContext).RestoreBackupCommand}"
CommandParameter="{Binding}"/>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBlock Text="Noch keine Backups vorhanden." Classes="emptyhint"
IsVisible="{Binding !Backups.Count}"/>
</StackPanel>
<Separator/>
<!-- Datenbank-Verschlüsselung -->
<StackPanel Spacing="10">
<TextBlock Text="Datenbank-Verschlüsselung" FontSize="16" FontWeight="SemiBold"/>
<TextBlock Text="Schützt die Datenbankdatei auf der Festplatte mit einem Passwort. Ohne das Passwort kann die App die Datenbank beim Start nicht öffnen — bei Verlust sind die Daten nicht mehr zugänglich."
FontSize="12" Opacity="0.6" TextWrapping="Wrap"/>
<TextBlock FontSize="13" FontWeight="SemiBold">
<Run Text="Status: "/>
<Run Text="{Binding DbEncryptionStatusLabel}"/>
</TextBlock>
<StackPanel Spacing="4" IsVisible="{Binding IsDbEncrypted}">
<TextBlock Text="Aktuelles Passwort" FontSize="12" Opacity="0.7"/>
<TextBox Text="{Binding CurrentDbPassword}" PasswordChar="●"/>
</StackPanel>
<Grid ColumnDefinitions="*,12,*">
<StackPanel Grid.Column="0" Spacing="4">
<TextBlock Text="Neues Passwort" FontSize="12" Opacity="0.7"/>
<TextBox Text="{Binding NewDbPassword}" PasswordChar="●"/>
</StackPanel>
<StackPanel Grid.Column="2" Spacing="4">
<TextBlock Text="Wiederholen" FontSize="12" Opacity="0.7"/>
<TextBox Text="{Binding NewDbPasswordConfirm}" PasswordChar="●"/>
</StackPanel>
</Grid>
<TextBlock Text="{Binding DbPasswordError}" Foreground="Red" FontSize="12"
IsVisible="{Binding DbPasswordError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<StackPanel Orientation="Horizontal" Spacing="8">
<Button Content="Passwort speichern" Command="{Binding SaveDbPasswordCommand}"/>
<Button Content="Verschlüsselung entfernen" Command="{Binding RemoveDbPasswordCommand}"
IsVisible="{Binding IsDbEncrypted}"/>
</StackPanel>
<TextBlock Text="Nach dem Ändern wird die App automatisch neu gestartet."
FontSize="11" Opacity="0.5"/>
</StackPanel>
<Separator/>
<!-- App-Sperre nach Inaktivität -->
<StackPanel Spacing="10">
<TextBlock Text="App-Sperre nach Inaktivität" FontSize="16" FontWeight="SemiBold"/>
<TextBlock Text="Sperrt die Oberfläche nach einer Zeit ohne Maus-/Tastatureingabe. Eigenes Passwort, unabhängig von der Datenbank-Verschlüsselung."
FontSize="12" Opacity="0.6" TextWrapping="Wrap"/>
<CheckBox Content="App-Sperre aktivieren" IsChecked="{Binding AppLockEnabled}"/>
<StackPanel Spacing="4" MaxWidth="200">
<TextBlock Text="Zeit bis zur Sperre (Minuten)" FontSize="12" Opacity="0.7"/>
<NumericUpDown Value="{Binding AppLockTimeoutMinutes}" Minimum="1" Maximum="120" FormatString="0"/>
</StackPanel>
<Grid ColumnDefinitions="*,12,*">
<StackPanel Grid.Column="0" Spacing="4">
<TextBlock Text="Passwort (leer lassen, um es zu behalten)" FontSize="12" Opacity="0.7"/>
<TextBox Text="{Binding AppLockNewPassword}" PasswordChar="●"/>
</StackPanel>
<StackPanel Grid.Column="2" Spacing="4">
<TextBlock Text="Wiederholen" FontSize="12" Opacity="0.7"/>
<TextBox Text="{Binding AppLockNewPasswordConfirm}" PasswordChar="●"/>
</StackPanel>
</Grid>
<TextBlock Text="{Binding AppLockPasswordError}" Foreground="Red" FontSize="12"
IsVisible="{Binding AppLockPasswordError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<Button Content="Speichern" Command="{Binding SaveAppLockSettingsCommand}" HorizontalAlignment="Left"/>
<TextBlock Text="{Binding AppLockStatus}" Foreground="Green" FontSize="12"
IsVisible="{Binding AppLockStatus, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
</StackPanel>
</ScrollViewer>
</ContentPage>
<!-- Tab: Datenschutz (5.4.2) -->
<ContentPage Header="Datenschutz">
<ScrollViewer>
<StackPanel Margin="32,20,32,28" Spacing="14" MaxWidth="640">
<TextBlock Text="Löschfristen für Dokumentationseinträge" FontSize="16" FontWeight="SemiBold"/>
<TextBlock Text="Einträge werden nie automatisch gelöscht — hier abgelaufene Einträge nur zur manuellen Prüfung anzeigen."
FontSize="12" Opacity="0.6" TextWrapping="Wrap"/>
<Grid ColumnDefinitions="140,Auto">
<NumericUpDown Grid.Column="0" Value="{Binding RetentionYears}" Minimum="1" Maximum="20" FormatString="0"/>
<Button Grid.Column="1" Content="Speichern" Margin="8,0,0,0"
Command="{Binding SaveRetentionYearsCommand}"/>
</Grid>
<TextBlock Text="{Binding RetentionStatus}" Foreground="Green" FontSize="12"
IsVisible="{Binding RetentionStatus, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<Separator Margin="0,4"/>
<TextBlock Text="Abgelaufene Einträge" FontSize="14" FontWeight="SemiBold"/>
<ItemsControl ItemsSource="{Binding ExpiredDocuments}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="vm:ExpiredDocumentItem">
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
BorderThickness="0,0,0,1" Padding="0,7">
<Grid ColumnDefinitions="*,Auto">
<StackPanel Grid.Column="0">
<TextBlock FontSize="13">
<Run Text="{Binding StudentName}"/>
<Run Text=" — "/>
<Run Text="{Binding Title}"/>
</TextBlock>
<TextBlock FontSize="11" Opacity="0.5">
<Run Text="erfasst am "/>
<Run Text="{Binding CreatedAtDisplay}"/>
</TextBlock>
</StackPanel>
<Button Grid.Column="1" Content="Endgültig löschen" FontSize="12" Padding="10,4"
Command="{Binding $parent[ItemsControl].((vm:SettingsViewModel)DataContext).HardDeleteDocumentCommand}"
CommandParameter="{Binding}"/>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBlock Text="Keine abgelaufenen Einträge." Classes="emptyhint"
IsVisible="{Binding !ExpiredDocuments.Count}"/>
</StackPanel>
</ScrollViewer>
</ContentPage>
<!-- Tab: Synchronisation (Kapitel 10) -->
<ContentPage Header="Synchronisation">
<ScrollViewer>
<StackPanel Margin="32,20,32,28" Spacing="14" MaxWidth="420">
<TextBlock Text="Geräte-Synchronisation" FontSize="16" FontWeight="SemiBold"/>
<TextBlock FontSize="12" Opacity="0.6" TextWrapping="Wrap"
Text="Gleicht Daten zwischen mehreren eigenen Geräten über einen selbst betriebenen Server ab. Erfordert einen bereits eingerichteten Server und einen dort angelegten Nutzer."/>
<Border Background="#3DE53935" BorderBrush="#E53935" BorderThickness="1" CornerRadius="6"
Padding="12,10" IsVisible="{Binding SyncKeyWasRegenerated}">
<StackPanel Spacing="4">
<TextBlock Text="Neuer Sync-Schlüssel erzeugt" FontSize="13" FontWeight="Bold"/>
<TextBlock FontSize="12" TextWrapping="Wrap"
Text="Der bisherige Sync-Schlüssel dieses Geräts fehlte beim Start und wurde automatisch durch einen neuen ersetzt. Bereits synchronisierte Daten unter dem alten Schlüssel sind für dieses Gerät dadurch nicht mehr lesbar. Hast du noch ein anderes Gerät mit dem alten Schlüssel, koppele dieses Gerät DAMIT neu (unten „Gerät koppeln“), statt fortzufahren. Ansonsten hilft nur ein zuvor gesicherter Wiederherstellungscode (unten)."/>
</StackPanel>
</Border>
<StackPanel Spacing="4">
<TextBlock Text="Server-Adresse" FontSize="12" Opacity="0.7"/>
<TextBox Text="{Binding SyncServerUrl}" PlaceholderText="https://sync.example.com"/>
</StackPanel>
<StackPanel Spacing="8" IsVisible="{Binding !SyncIsLoggedIn}">
<StackPanel Spacing="4">
<TextBlock Text="Benutzername" FontSize="12" Opacity="0.7"/>
<TextBox Text="{Binding SyncUsername}"/>
</StackPanel>
<StackPanel Spacing="4">
<TextBlock Text="Passwort" FontSize="12" Opacity="0.7"/>
<TextBox Text="{Binding SyncPassword}" PasswordChar="●"/>
</StackPanel>
<TextBlock Text="{Binding SyncLoginError}" Foreground="Red" FontSize="12"
IsVisible="{Binding SyncLoginError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<StackPanel Orientation="Horizontal" Spacing="8">
<Button Content="Anmelden" Command="{Binding SyncLoginCommand}"/>
<Button Content="Verbindung testen" Command="{Binding SyncTestConnectionCommand}"/>
</StackPanel>
</StackPanel>
<StackPanel Spacing="8" IsVisible="{Binding SyncIsLoggedIn}">
<TextBlock FontSize="13" FontWeight="SemiBold">
<Run Text="Angemeldet als: "/><Run Text="{Binding SyncUsername}"/>
</TextBlock>
<StackPanel Orientation="Horizontal" Spacing="8">
<Button Content="Verbindung testen" Command="{Binding SyncTestConnectionCommand}"/>
<Button Content="Abmelden" Command="{Binding SyncLogoutCommand}"/>
</StackPanel>
</StackPanel>
<TextBlock Text="{Binding SyncConnectionStatus}" FontSize="12"
IsVisible="{Binding SyncConnectionStatus, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<TextBlock FontSize="11" Opacity="0.5" TextWrapping="Wrap"
Text="Speichern/Anmelden startet die App neu, damit die Änderung wirksam wird."/>
<StackPanel Spacing="6" Margin="0,10,0,0" IsVisible="{Binding SyncIsLoggedIn}">
<Separator/>
<TextBlock Text="Vollständigen Sync erzwingen" FontSize="14" FontWeight="SemiBold"/>
<TextBlock FontSize="12" Opacity="0.6" TextWrapping="Wrap"
Text="Falls dieses Gerät wiederholt keine Änderungen eines anderen Geräts erhält, kann hier der lokale Sync-Fortschritt zurückgesetzt werden — beim nächsten Sync werden alle Ereignisse vom Server erneut geladen."/>
<Button Content="Jetzt zurücksetzen und synchronisieren" Command="{Binding SyncForceFullResyncCommand}"
IsEnabled="{Binding !SyncForceResyncBusy}" HorizontalAlignment="Left"/>
<TextBlock Text="{Binding SyncForceResyncStatus}" FontSize="12" TextWrapping="Wrap"
IsVisible="{Binding SyncForceResyncStatus, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
<StackPanel Spacing="10" Margin="0,10,0,0" IsVisible="{Binding SyncIsLoggedIn}">
<Separator/>
<TextBlock Text="Wiederherstellungscode" FontSize="14" FontWeight="SemiBold"/>
<TextBlock FontSize="12" Opacity="0.6" TextWrapping="Wrap"
Text="Der Sync-Schlüssel verlässt nie den Server im Klartext — geht er auf allen Geräten verloren, sind bereits synchronisierte Daten ohne einen vorher gesicherten Code unwiederbringlich. Der Code läuft komplett offline, ohne Server."/>
<StackPanel Spacing="6">
<TextBlock Text="Code erzeugen und Datei sichern" FontSize="12" FontWeight="SemiBold" Opacity="0.7"/>
<TextBlock FontSize="11" Opacity="0.5" TextWrapping="Wrap"
Text="Speichert eine verschlüsselte Schlüsseldatei an einem Ort deiner Wahl (z. B. USB-Stick, eigene Cloud). Den anschließend angezeigten Code getrennt davon notieren und sicher aufbewahren — er wird nirgends gespeichert."/>
<Button Content="Code erzeugen" Command="{Binding CreateRecoveryCodeCommand}"
IsEnabled="{Binding !RecoveryBusy}" HorizontalAlignment="Left"/>
<TextBlock Text="{Binding RecoveryCode}" FontSize="18" FontWeight="Bold"
FontFamily="Monospace" TextWrapping="Wrap"
IsVisible="{Binding RecoveryCode, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
<StackPanel Spacing="6">
<TextBlock Text="Wiederherstellungscode einlösen" FontSize="12" FontWeight="SemiBold" Opacity="0.7"/>
<TextBlock FontSize="11" Opacity="0.5" TextWrapping="Wrap"
Text="Ersetzt den Sync-Schlüssel DIESES Geräts durch den aus der gesicherten Datei — nur nötig, wenn der eigene Schlüssel verloren ging."/>
<TextBox Text="{Binding RecoveryCodeInput}" PlaceholderText="Code eingeben"/>
<Button Content="Datei auswählen und wiederherstellen" Command="{Binding RedeemRecoveryCodeCommand}"
IsEnabled="{Binding !RecoveryBusy}" HorizontalAlignment="Left"/>
</StackPanel>
<TextBlock Text="{Binding RecoveryStatus}" FontSize="12" TextWrapping="Wrap"
IsVisible="{Binding RecoveryStatus, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
<StackPanel Spacing="10" Margin="0,10,0,0" IsVisible="{Binding SyncIsLoggedIn}">
<Separator/>
<TextBlock Text="Gerät koppeln" FontSize="14" FontWeight="SemiBold"/>
<TextBlock FontSize="12" Opacity="0.6" TextWrapping="Wrap"
Text="Login allein reicht nicht: Verschlüsselt wird lokal mit einem eigenen Schlüssel je Gerät, der nie über den Server läuft. Für ein zweites Gerät hier einen Code erzeugen — auf dem anderen Gerät eingeben, um Datenbank und Schlüssel zu übernehmen."/>
<StackPanel Spacing="6">
<TextBlock Text="Dieses Gerät als Quelle" FontSize="12" FontWeight="SemiBold" Opacity="0.7"/>
<Button Content="Code erzeugen" Command="{Binding CreatePairingCodeCommand}"
IsEnabled="{Binding !PairingBusy}" HorizontalAlignment="Left"/>
<TextBlock Text="{Binding PairingCode}" FontSize="20" FontWeight="Bold"
FontFamily="Monospace" IsVisible="{Binding PairingCode, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
<StackPanel Spacing="6">
<TextBlock Text="Code von einem anderen Gerät einlösen" FontSize="12" FontWeight="SemiBold" Opacity="0.7"/>
<TextBlock FontSize="11" Opacity="0.5" TextWrapping="Wrap"
Text="Ersetzt die Datenbank auf DIESEM Gerät vollständig — nur auf einem Gerät ohne eigene Daten verwenden."/>
<Grid ColumnDefinitions="*,8,Auto">
<TextBox Grid.Column="0" Text="{Binding PairingCodeInput}" PlaceholderText="TIGER-42-BLAU"/>
<Button Grid.Column="2" Content="Einlösen" Command="{Binding RedeemPairingCodeCommand}"
IsEnabled="{Binding !PairingBusy}"/>
</Grid>
</StackPanel>
<TextBlock Text="{Binding PairingStatus}" FontSize="12" TextWrapping="Wrap"
IsVisible="{Binding PairingStatus, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
<TextBlock Text="Konflikte" FontSize="14" FontWeight="SemiBold" Margin="0,10,0,0"
IsVisible="{Binding SyncConflicts.Count}"/>
<TextBlock FontSize="12" Opacity="0.6" TextWrapping="Wrap"
IsVisible="{Binding SyncConflicts.Count}"
Text="Ein anderes Gerät hat dieselbe Änderung gleichzeitig gemacht — hier steht, welche Seite jeweils übernommen wurde."/>
<ItemsControl ItemsSource="{Binding SyncConflicts}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="vm:SyncConflictListItem">
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
BorderThickness="0,0,0,1" Padding="0,7">
<Grid ColumnDefinitions="*,Auto">
<StackPanel Grid.Column="0" Spacing="2">
<TextBlock Text="{Binding EntityDisplay}" FontSize="13"/>
<TextBlock FontSize="12" Opacity="0.6">
<Run Text="{Binding DetectedAtDisplay}"/><Run Text=" · "/><Run Text="{Binding ResolutionDisplay}"/>
</TextBlock>
</StackPanel>
<Button Grid.Column="1" Content="Gesehen" FontSize="12" Padding="10,4"
Command="{Binding $parent[ItemsControl].((vm:SettingsViewModel)DataContext).MarkConflictReviewedCommand}"
CommandParameter="{Binding}"/>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
</ContentPage>
<!-- Tab: KI-Unterstützung (4.5.9) -->
<ContentPage Header="KI-Unterstützung">
<ScrollViewer>
<StackPanel Margin="32,20,32,28" Spacing="14" MaxWidth="420">
<TextBlock Text="KI-gestützte Planungsunterstützung" FontSize="16" FontWeight="SemiBold"/>
<TextBlock FontSize="12" Opacity="0.6" TextWrapping="Wrap"
Text="Ermöglicht KI-Vorschläge für Unterrichtseinheiten über einen Zwischendienst auf dem eigenen Server (kein API-Schlüssel im Client). Jede Anfrage verbraucht Guthaben."/>
<CheckBox Content="KI-Unterstützung aktivieren" IsChecked="{Binding AiEnabled}"/>
<StackPanel Spacing="8" IsVisible="{Binding !AiIsLoggedIn}">
<StackPanel Spacing="4">
<TextBlock Text="Benutzername" FontSize="12" Opacity="0.7"/>
<TextBox Text="{Binding AiUsername}"/>
</StackPanel>
<StackPanel Spacing="4">
<TextBlock Text="Passwort" FontSize="12" Opacity="0.7"/>
<TextBox Text="{Binding AiPassword}" PasswordChar="●"/>
</StackPanel>
<TextBlock Text="{Binding AiLoginError}" Foreground="Red" FontSize="12"
IsVisible="{Binding AiLoginError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<Button Content="Anmelden" Command="{Binding AiLoginCommand}" HorizontalAlignment="Left"/>
</StackPanel>
<StackPanel Spacing="8" IsVisible="{Binding AiIsLoggedIn}">
<TextBlock FontSize="13" FontWeight="SemiBold">
<Run Text="Angemeldet als: "/><Run Text="{Binding AiUsername}"/>
</TextBlock>
<TextBlock Text="{Binding AiBalanceDisplay}" FontSize="13"/>
<Button Content="Abmelden" Command="{Binding AiLogoutCommand}" HorizontalAlignment="Left"/>
</StackPanel>
</StackPanel>
</ScrollViewer>
</ContentPage>
<!-- Tab: WebUntis-iCal-Abgleich (Nutzer-Feedback) -->
<ContentPage Header="Stundenplan-Abgleich">
<ScrollViewer>
<StackPanel Margin="32,20,32,28" Spacing="14" MaxWidth="420">
<TextBlock Text="WebUntis-Stundenplan-Abgleich" FontSize="16" FontWeight="SemiBold"/>
<TextBlock FontSize="12" Opacity="0.6" TextWrapping="Wrap"
Text="Ruft periodisch den persönlichen iCal-Export von WebUntis ab, um Vertretungen, Ausfälle und Raumänderungen automatisch zu erkennen und in den Stundenplan zu übernehmen. Der Link enthält ein Zugangs-Token und wird verschlüsselt gespeichert."/>
<StackPanel Spacing="8" IsVisible="{Binding !UntisIsConfigured}">
<StackPanel Spacing="4">
<TextBlock Text="iCal-URL" FontSize="12" Opacity="0.7"/>
<TextBox Text="{Binding UntisIcalUrlInput}" PasswordChar="●"
PlaceholderText="https://…/WebUntis/ical_export?..."/>
</StackPanel>
<TextBlock Text="{Binding UntisUrlError}" Foreground="Red" FontSize="12"
IsVisible="{Binding UntisUrlError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<Button Content="Speichern und aktivieren" Command="{Binding UntisSaveUrlCommand}" HorizontalAlignment="Left"/>
</StackPanel>
<StackPanel Spacing="8" IsVisible="{Binding UntisIsConfigured}">
<TextBlock Text="iCal-URL hinterlegt (verschlüsselt gespeichert)." FontSize="13" FontWeight="SemiBold"/>
<TextBlock Text="{Binding UntisStatusDisplay}" FontSize="12" Opacity="0.7"/>
<StackPanel Orientation="Horizontal" Spacing="8">
<Button Content="Jetzt abrufen" Command="{Binding UntisFetchNowCommand}"
IsEnabled="{Binding !UntisFetchBusy}"/>
<Button Content="Zuordnung prüfen…" Command="{Binding UntisReviewMappingCommand}"/>
<Button Content="Entfernen" Command="{Binding UntisRemoveCommand}"/>
</StackPanel>
</StackPanel>
</StackPanel>
</ScrollViewer>
</ContentPage>
<!-- Tab: Darstellung (12.4) -->
<ContentPage Header="Darstellung">
<ScrollViewer>
<StackPanel Margin="32,20,32,28" Spacing="14" MaxWidth="420">
<TextBlock Text="Darstellung" FontSize="16" FontWeight="SemiBold"/>
<TextBlock FontSize="12" Opacity="0.6" TextWrapping="Wrap"
Text="Wechselt sofort, ohne Neustart."/>
<StackPanel Spacing="4">
<TextBlock Text="Design" FontSize="12" Opacity="0.7"/>
<ComboBox ItemsSource="{Binding ThemeOptions}" SelectedItem="{Binding SelectedThemeName}"
HorizontalAlignment="Left" MinWidth="180"/>
</StackPanel>
</StackPanel>
</ScrollViewer>
</ContentPage>
<!-- Tab: Papierkorb (14.3) -->
<ContentPage Header="Papierkorb">
<ScrollViewer>
<StackPanel Margin="32,20,32,28" Spacing="14" MaxWidth="560" DataContext="{Binding TrashTab}"
x:DataType="vm:TrashViewModel">
<TextBlock Text="Papierkorb" FontSize="16" FontWeight="SemiBold"/>
<TextBlock FontSize="12" Opacity="0.6" TextWrapping="Wrap"
Text="Gelöschte Noten, Aufgaben, Zeiteinträge, Sitzpläne und Notenschlüssel-Vorlagen bleiben 30 Tage wiederherstellbar, danach werden sie endgültig entfernt. Andere Löschvorgänge (z. B. eine ganze Lerngruppe) sind davon nicht erfasst."/>
<TextBlock Text="{Binding Status}" Foreground="Green" FontSize="12"
IsVisible="{Binding Status, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<TextBlock Text="Der Papierkorb ist leer." Classes="emptyhint"
IsVisible="{Binding !HasItems}"/>
<ItemsControl ItemsSource="{Binding Items}" IsVisible="{Binding HasItems}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="vm:TrashItemViewModel">
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
BorderThickness="0,0,0,1" Padding="0,8">
<Grid ColumnDefinitions="*,Auto">
<StackPanel Grid.Column="0" Spacing="2">
<TextBlock FontSize="13">
<Run Text="{Binding TypeLabel}" FontWeight="SemiBold"/><Run Text=": "/><Run Text="{Binding Summary}"/>
</TextBlock>
<TextBlock FontSize="11" Opacity="0.6">
<Run Text="Gelöscht am "/><Run Text="{Binding DeletedAtDisplay}"/><Run Text=" · "/><Run Text="{Binding ExpiresInDisplay}"/>
</TextBlock>
</StackPanel>
<Button Grid.Column="1" Content="Wiederherstellen" FontSize="12" Padding="10,4"
Command="{Binding $parent[ItemsControl].((vm:TrashViewModel)DataContext).RestoreCommand}"
CommandParameter="{Binding}"/>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
</ContentPage>
</TabbedPage>
</Grid>
</UserControl>