QuestPDF als PDF-Bibliothek eingeführt (11.3), bewusst nur in LehrerApp.Desktop referenziert - zieht SkiaSharp-Native-Binaries mit, die im Server-Docker-Image nichts verloren haben. Neuer PdfExportService mit gemeinsamem Kopf-/Fußzeilen-Layout (Titel, Stand-Datum, Seitenzahlen) und schlanken Druck-DTOs statt ViewModels, damit die Erzeugung ohne Avalonia-Bezug testbar bleibt. Speicherdialog läuft über den bestehenden ExportService (neues PDF-Format). Fünf Druckvorlagen (11.4) als "Als PDF"-Button direkt in der jeweiligen Ansicht: - Sitzplan: Raster mit Tafel-Banner, Tischabständen als echte Lücken, ausgeblendeten Plätzen als leere Rasterposition. - Notenliste: druckt exakt die aktuell angezeigte Matrix (Zeitraum/Darstellung/Sortierung). - Klausur-Notenspiegel: Kennzahlen, Notenverteilung und Aufgabenanalyse mit Balkendiagrammen, auffällig schwache Aufgaben rot markiert. - Kompetenzbericht: Analyse-Tabelle für den ausgewählten Schüler (erledigt zugleich 8.3.3). - Schülerdokumentation: alle Einträge chronologisch, vertrauliche/Entwurfs-Einträge markiert. Layout an gerenderten Muster-PDFs visuell geprüft, nicht nur per Unit-Test. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
328 lines
20 KiB
XML
328 lines
20 KiB
XML
<UserControl xmlns="https://github.com/avaloniaui"
|
||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||
xmlns:vm="clr-namespace:LehrerApp.Desktop.ViewModels.Students"
|
||
xmlns:shared="clr-namespace:LehrerApp.Desktop.Views.Shared"
|
||
x:Class="LehrerApp.Desktop.Views.Students.StudentDetailView"
|
||
x:DataType="vm:StudentDetailViewModel">
|
||
<Grid RowDefinitions="Auto,*">
|
||
|
||
<!-- Header -->
|
||
<Border Grid.Row="0" Padding="20,16"
|
||
BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
|
||
BorderThickness="0,0,0,1">
|
||
<Grid ColumnDefinitions="*,Auto">
|
||
<Border Grid.Column="0" Width="38" Height="38" CornerRadius="19"
|
||
Background="{Binding StudentAvatarColor}" HorizontalAlignment="Left"
|
||
ToolTip.Tip="{Binding StudentAvatarTooltip}" IsVisible="{Binding !IsEditing}">
|
||
<TextBlock Text="{Binding StudentAvatarLabel}" Foreground="White" FontWeight="Bold" FontSize="15"
|
||
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||
</Border>
|
||
<shared:PageHeader Grid.Column="0" Margin="50,0,0,0" IsVisible="{Binding !IsEditing}" Title="{Binding StudentTitle}"/>
|
||
<StackPanel Grid.Column="0" Spacing="6" IsVisible="{Binding IsEditing}">
|
||
<Grid ColumnDefinitions="*,8,*,8,170">
|
||
<TextBox Grid.Column="0" Text="{Binding EditFirstName}" PlaceholderText="Vorname"/>
|
||
<TextBox Grid.Column="2" Text="{Binding EditLastName}" PlaceholderText="Nachname"/>
|
||
<ComboBox Grid.Column="4" ItemsSource="{Binding GenderOptions}"
|
||
SelectedItem="{Binding EditGender}" PlaceholderText="Geschlecht"/>
|
||
</Grid>
|
||
</StackPanel>
|
||
<StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="8" VerticalAlignment="Top">
|
||
<TextBlock Text="{Binding StudentStatus}" VerticalAlignment="Center" Opacity="0.65"/>
|
||
<Button Content="Word-Brief" Command="{Binding CreateLetterCommand}"
|
||
IsVisible="{Binding !IsEditing}"/>
|
||
<Button Content="Bearbeiten" Command="{Binding StartEditCommand}"
|
||
IsVisible="{Binding !IsEditing}"/>
|
||
<Button Content="Schüler verwalten" Command="{Binding ManageStudentCommand}"
|
||
IsVisible="{Binding !IsEditing}"/>
|
||
<Button Content="Speichern" Command="{Binding SaveEditCommand}"
|
||
IsVisible="{Binding IsEditing}"/>
|
||
<Button Content="Abbrechen" Command="{Binding CancelEditCommand}"
|
||
IsVisible="{Binding IsEditing}"/>
|
||
</StackPanel>
|
||
</Grid>
|
||
</Border>
|
||
|
||
<!--
|
||
Avalonia 12: TabbedPage für Schüler-Tabs.
|
||
Kein manuelles Tab-Management nötig.
|
||
-->
|
||
<TabbedPage Grid.Row="1" TabPlacement="Top">
|
||
|
||
<ContentPage Header="Übersicht">
|
||
<ScrollViewer Padding="20">
|
||
<StackPanel Spacing="12">
|
||
<TextBlock Text="Lerngruppen" FontSize="15" FontWeight="SemiBold"/>
|
||
<ItemsControl ItemsSource="{Binding GroupMemberships}">
|
||
<ItemsControl.ItemTemplate>
|
||
<DataTemplate DataType="vm:GroupMembershipEntry">
|
||
<Border Background="{DynamicResource SystemControlBackgroundAltHighBrush}"
|
||
CornerRadius="6" Padding="12,8" Margin="0,0,0,6">
|
||
<Grid ColumnDefinitions="70,*,Auto">
|
||
<TextBlock Grid.Column="0" Text="{Binding SchoolYear}" FontWeight="SemiBold"/>
|
||
<TextBlock Grid.Column="1" Text="{Binding GroupName}" Margin="8,0"/>
|
||
<TextBlock Grid.Column="2" Text="{Binding Subject}" Opacity="0.5" FontSize="12"/>
|
||
</Grid>
|
||
</Border>
|
||
</DataTemplate>
|
||
</ItemsControl.ItemTemplate>
|
||
</ItemsControl>
|
||
<TextBlock Text="Keine Lerngruppenzuordnungen vorhanden." Opacity="0.4"
|
||
IsVisible="{Binding !GroupMemberships.Count}"/>
|
||
</StackPanel>
|
||
</ScrollViewer>
|
||
</ContentPage>
|
||
|
||
<ContentPage Header="Kontakte">
|
||
<Grid RowDefinitions="Auto,*" Margin="20">
|
||
<Grid Grid.Row="0" ColumnDefinitions="*,Auto,Auto,Auto" Margin="0,0,0,10">
|
||
<TextBlock Grid.Column="0" Text="Kontaktdaten" FontSize="15"
|
||
FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||
<Button Grid.Column="1" Content="+ Neu" Command="{Binding AddContactCommand}"
|
||
Margin="0,0,8,0"/>
|
||
<Button Grid.Column="2" Content="Bearbeiten" Command="{Binding EditContactCommand}"
|
||
Margin="0,0,8,0"/>
|
||
<Button Grid.Column="3" Content="Adresse anzeigen"
|
||
Command="{Binding ViewSelectedAddressCommand}"/>
|
||
</Grid>
|
||
|
||
<ListBox Grid.Row="1" x:Name="ContactList"
|
||
ItemsSource="{Binding Contacts}"
|
||
SelectedItem="{Binding SelectedContact}"
|
||
DoubleTapped="OnContactDoubleTapped">
|
||
<ListBox.ItemTemplate>
|
||
<DataTemplate x:DataType="vm:ContactItem">
|
||
<Border Background="{DynamicResource SystemControlBackgroundAltHighBrush}"
|
||
CornerRadius="8" Padding="14,10" Margin="2,3">
|
||
<Grid ColumnDefinitions="220,*,Auto" RowDefinitions="Auto,Auto">
|
||
<StackPanel Grid.Column="0" Grid.RowSpan="2" Spacing="4">
|
||
<TextBlock Text="{Binding Name}" FontWeight="SemiBold" FontSize="14"
|
||
TextWrapping="Wrap"/>
|
||
<Border Background="{DynamicResource SystemControlForegroundBaseLowBrush}"
|
||
CornerRadius="4" Padding="6,2" HorizontalAlignment="Left">
|
||
<TextBlock Text="{Binding Relation}" FontSize="11" Opacity="0.7"/>
|
||
</Border>
|
||
<TextBlock Text="{Binding LetterSalutation}" FontSize="11" Opacity="0.6"
|
||
TextWrapping="Wrap"
|
||
IsVisible="{Binding LetterSalutation, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
|
||
</StackPanel>
|
||
|
||
<StackPanel Grid.Column="1" Grid.Row="0" Orientation="Horizontal" Spacing="8">
|
||
<Button Content="{Binding PhoneDisplay}"
|
||
Command="{Binding CallCommand}"
|
||
IsVisible="{Binding HasPhone}"
|
||
FontSize="12" Padding="8,4"/>
|
||
<Button Content="{Binding EmailDisplay}"
|
||
Command="{Binding MailCommand}"
|
||
IsVisible="{Binding HasEmail}"
|
||
FontSize="12" Padding="8,4"/>
|
||
</StackPanel>
|
||
|
||
<TextBlock Grid.Column="1" Grid.Row="1" Text="{Binding Address}"
|
||
IsVisible="{Binding HasAddress}" FontSize="12" Opacity="0.65"
|
||
TextWrapping="Wrap" Margin="4,4,12,0"/>
|
||
|
||
<Border Grid.Column="2" Grid.RowSpan="2" CornerRadius="4" Padding="7,3"
|
||
VerticalAlignment="Top"
|
||
Background="{DynamicResource SystemControlBackgroundAltHighBrush}">
|
||
<StackPanel>
|
||
<TextBlock Text="{Binding StatusText}" FontSize="11"
|
||
IsVisible="{Binding IsInvalid}" Foreground="IndianRed"/>
|
||
<TextBlock Text="Aktuell" FontSize="11" Opacity="0.5"
|
||
IsVisible="{Binding IsValid}"/>
|
||
</StackPanel>
|
||
</Border>
|
||
</Grid>
|
||
</Border>
|
||
</DataTemplate>
|
||
</ListBox.ItemTemplate>
|
||
</ListBox>
|
||
|
||
<TextBlock Text="Keine Kontakte erfasst." Opacity="0.4"
|
||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||
IsVisible="{Binding HasNoContacts}"/>
|
||
</Grid>
|
||
</ContentPage>
|
||
|
||
<ContentPage Header="Noten">
|
||
<ScrollViewer Padding="20">
|
||
<StackPanel Spacing="20">
|
||
<TextBlock Text="Notenentwicklung" FontSize="15" FontWeight="SemiBold"/>
|
||
<StackPanel Orientation="Horizontal" Spacing="16">
|
||
<StackPanel Orientation="Horizontal" Spacing="5">
|
||
<Border Width="10" Height="10" CornerRadius="2"
|
||
Background="{DynamicResource SystemControlBackgroundAccentBrush}"/>
|
||
<TextBlock Text="normal" FontSize="11" Opacity="0.6"/>
|
||
</StackPanel>
|
||
<StackPanel Orientation="Horizontal" Spacing="5">
|
||
<Border Width="10" Height="10" CornerRadius="2" Background="#E53935"/>
|
||
<TextBlock Text="Auffälligkeit (Notenabfall ≥ 1 Note oder mangelhaft/ungenügend)"
|
||
FontSize="11" Opacity="0.6"/>
|
||
</StackPanel>
|
||
</StackPanel>
|
||
|
||
<ItemsControl ItemsSource="{Binding GradeHistory}">
|
||
<ItemsControl.ItemTemplate>
|
||
<DataTemplate DataType="vm:StudentGradeHistoryGroup">
|
||
<Border Background="{DynamicResource SystemControlBackgroundAltHighBrush}"
|
||
CornerRadius="8" Padding="14,12" Margin="0,0,0,12">
|
||
<StackPanel Spacing="8">
|
||
<Grid ColumnDefinitions="*,Auto">
|
||
<TextBlock Grid.Column="0" Text="{Binding Label}" FontWeight="SemiBold" FontSize="13"/>
|
||
<Border Grid.Column="1" Background="#E53935" CornerRadius="4" Padding="6,1"
|
||
IsVisible="{Binding HasWarnings}">
|
||
<TextBlock Text="Auffälligkeiten" FontSize="10" Foreground="White"/>
|
||
</Border>
|
||
</Grid>
|
||
|
||
<ItemsControl ItemsSource="{Binding Points}">
|
||
<ItemsControl.ItemsPanel>
|
||
<ItemsPanelTemplate>
|
||
<StackPanel Orientation="Horizontal" Spacing="10"/>
|
||
</ItemsPanelTemplate>
|
||
</ItemsControl.ItemsPanel>
|
||
<ItemsControl.ItemTemplate>
|
||
<DataTemplate DataType="vm:GradeHistoryPoint">
|
||
<StackPanel Width="46" VerticalAlignment="Bottom" ToolTip.Tip="{Binding TooltipText}">
|
||
<Border Classes="historybar" Height="{Binding BarHeight}" Width="16" CornerRadius="2"
|
||
HorizontalAlignment="Center" VerticalAlignment="Bottom"
|
||
Classes.warning="{Binding IsWarning}"/>
|
||
<TextBlock Text="{Binding Value}" FontSize="11" FontWeight="SemiBold"
|
||
HorizontalAlignment="Center" Margin="0,3,0,0"/>
|
||
<TextBlock Text="{Binding Label}" FontSize="9" Opacity="0.6"
|
||
HorizontalAlignment="Center" TextWrapping="Wrap" TextAlignment="Center"/>
|
||
<TextBlock Text="{Binding DateDisplay}" FontSize="9" Opacity="0.5"
|
||
HorizontalAlignment="Center"/>
|
||
</StackPanel>
|
||
</DataTemplate>
|
||
</ItemsControl.ItemTemplate>
|
||
<ItemsControl.Styles>
|
||
<Style Selector="Border.historybar">
|
||
<Setter Property="Background" Value="{DynamicResource SystemControlBackgroundAccentBrush}"/>
|
||
</Style>
|
||
<Style Selector="Border.historybar.warning">
|
||
<Setter Property="Background" Value="#E53935"/>
|
||
</Style>
|
||
</ItemsControl.Styles>
|
||
</ItemsControl>
|
||
</StackPanel>
|
||
</Border>
|
||
</DataTemplate>
|
||
</ItemsControl.ItemTemplate>
|
||
</ItemsControl>
|
||
|
||
<TextBlock Text="Noch keine Noten für diesen Schüler erfasst." Classes="emptyhint"
|
||
IsVisible="{Binding !GradeHistory.Count}"/>
|
||
</StackPanel>
|
||
</ScrollViewer>
|
||
</ContentPage>
|
||
|
||
<ContentPage Header="Dokumentation">
|
||
<ScrollViewer Padding="20">
|
||
<StackPanel Spacing="16">
|
||
|
||
<!-- Fehlzeitenbilanz (5.2.2/5.2.3) — Auswertung der Anwesenheitsdaten aus der
|
||
Mitarbeit-Erfassung, keine eigene Erfassung. -->
|
||
<Border Background="{DynamicResource SystemControlBackgroundAltHighBrush}"
|
||
CornerRadius="6" Padding="14,12" IsVisible="{Binding Attendance, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||
<StackPanel Spacing="6">
|
||
<TextBlock Text="Fehlzeitenbilanz (laufendes Schuljahr)" FontSize="13" FontWeight="SemiBold"/>
|
||
<TextBlock FontSize="12" Opacity="0.75">
|
||
<Run Text="{Binding Attendance.TotalChecked}"/>
|
||
<Run Text=" kontrollierte Stunden · "/>
|
||
<Run Text="{Binding Attendance.Excused}"/>
|
||
<Run Text=" entschuldigt · "/>
|
||
<Run Text="{Binding Attendance.Unexcused}"/>
|
||
<Run Text=" unentschuldigt · "/>
|
||
<Run Text="{Binding Attendance.ExcusePending}"/>
|
||
<Run Text=" Entschuldigung offen"/>
|
||
</TextBlock>
|
||
<TextBlock FontSize="12" Foreground="Red" FontWeight="SemiBold"
|
||
IsVisible="{Binding Attendance.ExceedsThreshold}">
|
||
<Run Text="Fehlquote "/>
|
||
<Run Text="{Binding Attendance.AbsenceRatePercent}"/>
|
||
<Run Text=" % — über dem Schwellenwert von 20 %."/>
|
||
</TextBlock>
|
||
</StackPanel>
|
||
</Border>
|
||
|
||
<Grid ColumnDefinitions="*,Auto,Auto,Auto">
|
||
<TextBlock Grid.Column="0" Text="Einträge" FontSize="14" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||
<Button Grid.Column="1" Content="Als PDF" FontSize="12" Padding="10,4"
|
||
Margin="0,0,8,0" Click="OnExportDocumentationPdfClick"
|
||
IsEnabled="{Binding !!Documentation.Count}"
|
||
ToolTip.Tip="Alle sichtbaren Dokumentationseinträge dieses Schülers als PDF drucken"/>
|
||
<Button Grid.Column="2" Content="Datenauskunft exportieren" FontSize="12" Padding="10,4"
|
||
Margin="0,0,8,0" Command="{Binding ExportPersonalDataCommand}"
|
||
ToolTip.Tip="Alle gespeicherten Daten dieses Schülers als JSON-Datei exportieren (Art. 15 DSGVO)"/>
|
||
<Button Grid.Column="3" Content="+ Eintrag" Command="{Binding AddDocumentationCommand}"/>
|
||
</Grid>
|
||
<TextBlock Text="{Binding ExportStatus}" Foreground="Green" FontSize="12"
|
||
IsVisible="{Binding ExportStatus, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
|
||
|
||
<ItemsControl ItemsSource="{Binding Documentation}">
|
||
<ItemsControl.ItemTemplate>
|
||
<DataTemplate DataType="vm:DocumentationItem">
|
||
<Border Background="{DynamicResource SystemControlBackgroundAltHighBrush}"
|
||
CornerRadius="6" Padding="12,10" Margin="0,0,0,8">
|
||
<StackPanel Spacing="4">
|
||
<Grid ColumnDefinitions="80,*,Auto,Auto,Auto">
|
||
<TextBlock Grid.Column="0" Text="{Binding DateDisplay}" Opacity="0.5" FontSize="12"/>
|
||
<StackPanel Grid.Column="1" Margin="8,0">
|
||
<TextBlock Text="{Binding Model.Title}" FontWeight="SemiBold" FontSize="13"
|
||
IsVisible="{Binding IsRevealed}"/>
|
||
<TextBlock Text="Vertraulich" FontWeight="SemiBold" FontSize="13" Opacity="0.6"
|
||
IsVisible="{Binding !IsRevealed}"/>
|
||
<TextBlock Text="{Binding TypeLabel}" FontSize="11" Opacity="0.5"/>
|
||
</StackPanel>
|
||
<TextBlock Grid.Column="2" Text="🔒" FontSize="14" VerticalAlignment="Center"
|
||
IsVisible="{Binding IsConfidential}" ToolTip.Tip="Vertraulich"/>
|
||
<Button Grid.Column="3" Content="Anzeigen" FontSize="11" Padding="8,3" Margin="6,0,0,0"
|
||
Command="{Binding RevealCommand}" IsVisible="{Binding !IsRevealed}"/>
|
||
<StackPanel Grid.Column="4" Orientation="Horizontal" Spacing="4" Margin="6,0,0,0"
|
||
IsVisible="{Binding IsRevealed}">
|
||
<Button Content="Gespräch begleiten" FontSize="11" Padding="8,3"
|
||
IsVisible="{Binding IsParentCall}"
|
||
Command="{Binding $parent[ItemsControl].((vm:StudentDetailViewModel)DataContext).ConductParentCallCommand}"
|
||
CommandParameter="{Binding}"/>
|
||
<Button Content="Bearbeiten" FontSize="11" Padding="8,3"
|
||
Command="{Binding $parent[ItemsControl].((vm:StudentDetailViewModel)DataContext).EditDocumentationCommand}"
|
||
CommandParameter="{Binding}"/>
|
||
<Button Content="Löschen" FontSize="11" Padding="8,3"
|
||
Command="{Binding $parent[ItemsControl].((vm:StudentDetailViewModel)DataContext).DeleteDocumentationCommand}"
|
||
CommandParameter="{Binding}"/>
|
||
</StackPanel>
|
||
</Grid>
|
||
<TextBlock Text="{Binding Model.Content}" FontSize="12" TextWrapping="Wrap" Opacity="0.8"
|
||
IsVisible="{Binding IsRevealed}"/>
|
||
<StackPanel Orientation="Horizontal" Spacing="8" IsVisible="{Binding IsRevealed}">
|
||
<TextBlock Text="{Binding StatusLabel}" FontSize="11" Opacity="0.55"
|
||
IsVisible="{Binding StatusLabel, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
|
||
<TextBlock Text="📎 Anhang" FontSize="11" Opacity="0.55" IsVisible="{Binding HasAttachments}"/>
|
||
</StackPanel>
|
||
<ItemsControl ItemsSource="{Binding TagChips}" IsVisible="{Binding IsRevealed}">
|
||
<ItemsControl.ItemsPanel>
|
||
<ItemsPanelTemplate><WrapPanel ItemSpacing="6" LineSpacing="4"/></ItemsPanelTemplate>
|
||
</ItemsControl.ItemsPanel>
|
||
<ItemsControl.ItemTemplate>
|
||
<DataTemplate DataType="vm:TagChip">
|
||
<Border Background="{Binding ColorHex}" CornerRadius="10" Padding="8,2">
|
||
<TextBlock Text="{Binding Text}" FontSize="10" Foreground="White"/>
|
||
</Border>
|
||
</DataTemplate>
|
||
</ItemsControl.ItemTemplate>
|
||
</ItemsControl>
|
||
</StackPanel>
|
||
</Border>
|
||
</DataTemplate>
|
||
</ItemsControl.ItemTemplate>
|
||
</ItemsControl>
|
||
<TextBlock Text="Keine Dokumentation vorhanden." Classes="emptyhint"
|
||
IsVisible="{Binding !Documentation.Count}"/>
|
||
</StackPanel>
|
||
</ScrollViewer>
|
||
</ContentPage>
|
||
|
||
</TabbedPage>
|
||
</Grid>
|
||
</UserControl>
|