Files
adminandClaude Sonnet 5 7365947edc feat: PDF-Druck für Sitzplan, Notenliste, Klausur-Notenspiegel, Kompetenzbericht, Dokumentation
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>
2026-08-20 14:27:05 +02:00

157 lines
8.5 KiB
XML
Raw Permalink 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.
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:LehrerApp.Desktop.ViewModels.Groups"
x:Class="LehrerApp.Desktop.Views.Groups.ExamEvaluationDialog"
x:DataType="vm:ExamEvaluationDialogViewModel"
Title="{Binding ExamTitle}"
Width="760" Height="820" MinWidth="600" MinHeight="480"
CanResize="True" WindowStartupLocation="CenterOwner">
<Grid RowDefinitions="*,Auto" Margin="24">
<ScrollViewer Grid.Row="0">
<StackPanel Spacing="16" Margin="0,0,12,0">
<StackPanel Spacing="2">
<StackPanel Orientation="Horizontal" Spacing="8">
<TextBlock Text="{Binding ExamTitle}" Classes="dialogtitle" VerticalAlignment="Center"/>
<Border Background="{DynamicResource SystemControlBackgroundAccentBrush}" CornerRadius="4" Padding="8,2"
IsVisible="{Binding NiveauLabel, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
<TextBlock Text="{Binding NiveauLabel}" FontSize="12" Foreground="White"/>
</Border>
</StackPanel>
<TextBlock Text="{Binding ExamDateLabel}" FontSize="12" Opacity="0.6"/>
</StackPanel>
<!-- Kennzahlen -->
<UniformGrid Columns="3" Rows="2">
<StackPanel Margin="0,0,12,10">
<TextBlock Text="DURCHSCHNITT" FontSize="10" FontWeight="Bold" Opacity="0.5"/>
<TextBlock Text="{Binding AverageDisplay}" FontSize="20" FontWeight="SemiBold"/>
</StackPanel>
<StackPanel Margin="0,0,12,10">
<TextBlock Text="MEDIAN" FontSize="10" FontWeight="Bold" Opacity="0.5"/>
<TextBlock Text="{Binding MedianDisplay}" FontSize="20" FontWeight="SemiBold"/>
</StackPanel>
<StackPanel Margin="0,0,12,10">
<TextBlock Text="BEWERTET / ABWESEND" FontSize="10" FontWeight="Bold" Opacity="0.5"/>
<TextBlock FontSize="20" FontWeight="SemiBold">
<Run Text="{Binding GradedCount}"/>
<Run Text=" / "/>
<Run Text="{Binding AbsentCount}"/>
</TextBlock>
</StackPanel>
<StackPanel Margin="0,0,12,0" IsVisible="{Binding BelowThresholdLabel1, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
<TextBlock Text="{Binding BelowThresholdLabel1}" FontSize="10" FontWeight="Bold" Opacity="0.5"
TextWrapping="Wrap"/>
<TextBlock Text="{Binding BelowThresholdDisplay1}" FontSize="20" FontWeight="SemiBold"/>
</StackPanel>
<StackPanel Margin="0,0,12,0" IsVisible="{Binding BelowThresholdLabel2, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
<TextBlock Text="{Binding BelowThresholdLabel2}" FontSize="10" FontWeight="Bold" Opacity="0.5"
TextWrapping="Wrap"/>
<TextBlock Text="{Binding BelowThresholdDisplay2}" FontSize="20" FontWeight="SemiBold"/>
</StackPanel>
</UniformGrid>
<Separator/>
<!-- Notenspiegel -->
<TextBlock Text="Notenspiegel" FontSize="14" FontWeight="SemiBold"/>
<ItemsControl ItemsSource="{Binding GradeDistribution}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="vm:GradeBarItem">
<Grid ColumnDefinitions="40,210,*" Margin="0,3">
<TextBlock Grid.Column="0" Text="{Binding Grade}" VerticalAlignment="Center" FontWeight="SemiBold"/>
<Border Grid.Column="1" HorizontalAlignment="Left" Height="16" CornerRadius="3"
Width="{Binding BarWidth}"
Background="{DynamicResource SystemControlBackgroundAccentBrush}"/>
<TextBlock Grid.Column="2" Text="{Binding CountDisplay}" FontSize="12" Opacity="0.7"
VerticalAlignment="Center" Margin="8,0,0,0"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBlock Text="Noch keine bewerteten Ergebnisse." Classes="emptyhint"
IsVisible="{Binding !GradedCount}"/>
<Separator/>
<!-- Aufgabenanalyse -->
<TextBlock Text="Aufgabenanalyse" FontSize="14" FontWeight="SemiBold"/>
<ItemsControl ItemsSource="{Binding TaskAnalysis}">
<ItemsControl.Styles>
<Style Selector="Border.taskbar">
<Setter Property="Background" Value="{DynamicResource SystemControlBackgroundAccentBrush}"/>
</Style>
<Style Selector="Border.taskbar.weak">
<Setter Property="Background" Value="#E53935"/>
</Style>
<Style Selector="TextBlock.tasklabel">
<Setter Property="Foreground" Value="{DynamicResource SystemControlForegroundBaseHighBrush}"/>
</Style>
<Style Selector="TextBlock.tasklabel.weak">
<Setter Property="Foreground" Value="#E53935"/>
<Setter Property="FontWeight" Value="SemiBold"/>
</Style>
</ItemsControl.Styles>
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="vm:TaskAnalysisItem">
<Grid ColumnDefinitions="180,210,*" Margin="0,3">
<TextBlock Grid.Column="0" Classes="tasklabel" Classes.weak="{Binding IsWeak}"
Text="{Binding Label}" VerticalAlignment="Center" TextTrimming="CharacterEllipsis"/>
<Border Grid.Column="1" Classes="taskbar" Classes.weak="{Binding IsWeak}"
HorizontalAlignment="Left" Height="16" CornerRadius="3" Width="{Binding BarWidth}"/>
<TextBlock Grid.Column="2" Text="{Binding AvgPercentDisplay}" FontSize="12" Opacity="0.7"
VerticalAlignment="Center" Margin="8,0,0,0"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBlock Text="Auffällig schwache Aufgaben (Ø unter 50 %) sind rot markiert." Opacity="0.4" FontSize="11"
IsVisible="{Binding TaskAnalysis.Count}"/>
<Separator/>
<!-- Notenschlüssel verschieben -->
<Grid ColumnDefinitions="*,Auto">
<TextBlock Grid.Column="0" Text="Notenschlüssel verschieben" FontSize="14" FontWeight="SemiBold" VerticalAlignment="Center"/>
<Button Grid.Column="1" Content=" Stufe" Command="{Binding AddGradingKeyRowCommand}"/>
</Grid>
<TextBlock Text="Änderungen wirken sich sofort auf den Notenspiegel oben aus. Erst mit &quot;Übernehmen&quot; werden sie an der Klausur gespeichert."
FontSize="11" Opacity="0.5" TextWrapping="Wrap"/>
<ItemsControl ItemsSource="{Binding GradingKeyEntries}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="vm:GradingKeyEntryEditItem">
<Grid ColumnDefinitions="90,90,*,Auto" Margin="0,0,0,4">
<TextBox Grid.Column="0" Text="{Binding Grade}" PlaceholderText="Note" Margin="0,0,6,0"/>
<NumericUpDown Grid.Column="1" Value="{Binding MinPercent}" Minimum="0" Maximum="100"
FormatString="0.##" Width="90" ShowButtonSpinner="False" Margin="0,0,6,0"/>
<TextBlock Grid.Column="2" Text="{Binding AbsolutePointsDisplay}" FontSize="12" Opacity="0.65"
VerticalAlignment="Center"/>
<Button Grid.Column="3" Content="✕" Command="{Binding RemoveCommand}" Padding="6,2"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBlock Text="{Binding GradingKeyValidation}" Foreground="Red" FontSize="12"
IsVisible="{Binding GradingKeyValidation, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<Grid ColumnDefinitions="*,Auto">
<TextBlock Grid.Column="0" Text="{Binding GradingKeyStatus}" Foreground="Green" FontSize="12"
VerticalAlignment="Center"
IsVisible="{Binding GradingKeyStatus, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<Button Grid.Column="1" Content="Übernehmen" Command="{Binding ApplyGradingKeyCommand}"/>
</Grid>
</StackPanel>
</ScrollViewer>
<Grid Grid.Row="1" ColumnDefinitions="Auto,8,Auto,*,Auto" Margin="0,16,0,0">
<Button Grid.Column="0" Content="Als CSV exportieren" Click="OnExportClick"/>
<Button Grid.Column="2" Content="Als PDF" Click="OnExportPdfClick"/>
<Button Grid.Column="4" Content="Fertig" Click="OnClose"/>
</Grid>
</Grid>
</Window>