Notenverwaltung (Kapitel 2) und Mitarbeits-Assistent

Notenübersicht der Gruppe (Matrix, Gesamt-Spalte, Sortierung, Halbjahresfilter),
Einzelnoten-Pflege samt Sammelerfassung, Gewichtungsschema mit Voreinstellung je
Gruppentyp, Zeugnisnoten-Berechnung mit Übersteuern/Festschreiben/Export und
Notenentwicklung im Schülerdetail.

Dazu Anwesenheits-/Hausaufgaben-Tracking je Mitarbeit-Sitzung, ein neuer
Mitarbeits-Assistent (Zeitleiste mit Abschnitten, automatischer Notenvorschlag,
Zusammenzug zur Halbjahresnote) und eine Dashboard-Kachel für offene
Entschuldigungen. Außerdem: verbliebene englische Begriffe in Auswahlfeldern
und Buttons auf Deutsch umgestellt.
This commit is contained in:
2026-08-12 19:19:22 +02:00
parent a1e722ace1
commit febcb0855a
34 changed files with 2503 additions and 39 deletions
@@ -0,0 +1,51 @@
<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.CollectiveGradeDialog"
x:DataType="vm:CollectiveGradeDialogViewModel"
Title="Sammelnote erfassen"
Width="480" Height="640" MinWidth="400" MinHeight="360"
CanResize="True" WindowStartupLocation="CenterOwner">
<Grid RowDefinitions="Auto,Auto,*,Auto" Margin="24">
<TextBlock Grid.Row="0" Text="Sammelnote erfassen" FontSize="18" FontWeight="SemiBold"/>
<StackPanel Grid.Row="1" Spacing="6" Margin="0,12,0,10">
<Grid ColumnDefinitions="120,*,90" ColumnSpacing="6">
<ComboBox Grid.Column="0" ItemsSource="{x:Static vm:GradeCategoryDisplay.Options}"
SelectedItem="{Binding CategoryName}"/>
<TextBox Grid.Column="1" Text="{Binding DateText}" PlaceholderText="TT.MM.JJJJ"/>
<NumericUpDown Grid.Column="2" Value="{Binding Weight}" Minimum="0" Maximum="10"
Increment="0.1" FormatString="0.#" ShowButtonSpinner="False"
ToolTip.Tip="Gewichtung"/>
</Grid>
<TextBox Text="{Binding Note}" PlaceholderText="Notiz (z.B. Hausaufgabenkontrolle)"/>
<TextBlock Text="{Binding ValidationMessage}" Foreground="Red" FontSize="12"
IsVisible="{Binding ValidationMessage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<TextBlock Text="Werte pro Schüler unten eintragen. Leere Felder werden nicht gespeichert."
FontSize="11" Opacity="0.5" TextWrapping="Wrap"/>
</StackPanel>
<ScrollViewer Grid.Row="2">
<ItemsControl ItemsSource="{Binding Rows}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="vm:CollectiveGradeStudentRow">
<Grid ColumnDefinitions="*,100" Margin="0,3">
<TextBlock Grid.Column="0" Text="{Binding Name}" VerticalAlignment="Center" FontSize="13"/>
<TextBox Grid.Column="1" Text="{Binding Value}" PlaceholderText="Wert"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<Grid Grid.Row="3" ColumnDefinitions="*,Auto,Auto" Margin="0,16,0,0">
<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 SaveAllCommand}" Margin="0,0,8,0"/>
<Button Grid.Column="2" Content="Fertig" Click="OnClose"/>
</Grid>
</Grid>
</Window>
@@ -0,0 +1,11 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
namespace LehrerApp.Desktop.Views.Groups;
public partial class CollectiveGradeDialog : Window
{
public CollectiveGradeDialog() => InitializeComponent();
private void OnClose(object? sender, RoutedEventArgs e) => Close();
}
@@ -0,0 +1,40 @@
<UserControl 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.GradeOverviewTabView"
x:DataType="vm:GradeOverviewTabViewModel">
<Grid RowDefinitions="Auto,*">
<StackPanel Grid.Row="0" Orientation="Horizontal" Spacing="8" Margin="0,0,0,8">
<TextBlock Text="Zeitraum:" VerticalAlignment="Center"/>
<ComboBox ItemsSource="{Binding PeriodOptions}" SelectedItem="{Binding SelectedPeriod}" MinWidth="170"/>
<ToggleButton Content="Punkte anzeigen" IsChecked="{Binding ShowAsPoints}"
IsVisible="{Binding CanTogglePointsView}" Margin="12,0,0,0"/>
<Button Content="Sortierung: Name" Command="{Binding SortByNameCommand}" Margin="12,0,0,0"/>
<Button Content="Sortierung: Gesamt" Command="{Binding SortByTotalCommand}"/>
<Button Content="Noten verwalten" Command="{Binding ManageStudentGradesCommand}" Margin="12,0,0,0"
IsVisible="{Binding SelectedRow, Converter={x:Static ObjectConverters.IsNotNull}}"/>
<Button Content=" Sammelnote" Command="{Binding CollectiveGradeCommand}"/>
<Button Content="Zeugnisnoten" Command="{Binding ReportGradesCommand}"/>
</StackPanel>
<DataGrid Grid.Row="1"
x:Name="GradesGrid"
ItemsSource="{Binding Rows}"
SelectedItem="{Binding SelectedRow}"
AutoGenerateColumns="False"
IsReadOnly="True"
GridLinesVisibility="All"
CanUserReorderColumns="False"
CanUserResizeColumns="True"/>
<TextBlock Grid.Row="1" Text="Keine Schüler in dieser Gruppe."
HorizontalAlignment="Center" VerticalAlignment="Center"
Opacity="0.35" FontSize="14"
IsVisible="{Binding !Rows.Count}"/>
</Grid>
</UserControl>
@@ -0,0 +1,107 @@
using Avalonia.Controls;
using Avalonia.Data;
using LehrerApp.Core.Interfaces;
using LehrerApp.Core.Services;
using LehrerApp.Desktop.ViewModels.Groups;
using Microsoft.Extensions.DependencyInjection;
namespace LehrerApp.Desktop.Views.Groups;
public partial class GradeOverviewTabView : UserControl
{
private GradeOverviewTabViewModel? _vm;
public GradeOverviewTabView() => InitializeComponent();
protected override void OnDataContextChanged(EventArgs e)
{
base.OnDataContextChanged(e);
if (DataContext is GradeOverviewTabViewModel vm)
{
_vm = vm;
vm.OnManageStudentGrades = ShowStudentGradesDialog;
vm.OnCollectiveGrade = ShowCollectiveGradeDialog;
vm.OnReportGrades = ShowReportGradesDialog;
vm.PropertyChanged += (_, pe) =>
{
if (pe.PropertyName == nameof(GradeOverviewTabViewModel.RebuildColumnsSignal))
BuildColumns();
};
BuildColumns();
}
}
private async Task ShowStudentGradesDialog(GradeOverviewRow row)
{
var dialogVm = new StudentGradesDialogViewModel(
App.Services.GetRequiredService<IGradeRepository>(),
row.StudentId, _vm!.GroupId, row.Name);
var dialog = new StudentGradesDialog { DataContext = dialogVm };
var owner = TopLevel.GetTopLevel(this) as Window;
if (owner is not null) await dialog.ShowDialog(owner);
}
private async Task ShowCollectiveGradeDialog()
{
var dialogVm = new CollectiveGradeDialogViewModel(
App.Services.GetRequiredService<IGradeRepository>(),
App.Services.GetRequiredService<IStudentRepository>(),
_vm!.GroupId);
var dialog = new CollectiveGradeDialog { DataContext = dialogVm };
var owner = TopLevel.GetTopLevel(this) as Window;
if (owner is not null) await dialog.ShowDialog(owner);
}
private async Task ShowReportGradesDialog()
{
var dialogVm = new ReportGradeDialogViewModel(
App.Services.GetRequiredService<IGradeRepository>(),
App.Services.GetRequiredService<IExamRepository>(),
App.Services.GetRequiredService<IExamResultRepository>(),
App.Services.GetRequiredService<IStudentRepository>(),
App.Services.GetRequiredService<IGroupMembershipRepository>(),
App.Services.GetRequiredService<IGradingSchemeRepository>(),
App.Services.GetRequiredService<IReportGradeRepository>(),
App.Services.GetRequiredService<GradingService>(),
_vm!.GroupId, _vm.GroupType, _vm.GradingSystem, _vm.GroupLabel);
var dialog = new ReportGradeDialog { DataContext = dialogVm };
var owner = TopLevel.GetTopLevel(this) as Window;
if (owner is not null) await dialog.ShowDialog(owner);
}
private void BuildColumns()
{
var grid = this.FindControl<DataGrid>("GradesGrid");
if (grid is null || _vm is null) return;
grid.Columns.Clear();
grid.Columns.Add(new DataGridTextColumn
{
Header = "Schüler",
Binding = new Binding("Name"),
Width = new DataGridLength(160, DataGridLengthUnitType.Pixel),
});
foreach (var (col, i) in _vm.Columns.Select((c, i) => (c, i)))
{
var idx = i;
grid.Columns.Add(new DataGridTextColumn
{
Header = col.Header,
Binding = new Binding($"Cells[{idx}]"),
Width = new DataGridLength(120, DataGridLengthUnitType.Pixel),
});
}
grid.Columns.Add(new DataGridTextColumn
{
Header = "Gesamt",
Binding = new Binding("TotalDisplay"),
Width = new DataGridLength(80, DataGridLengthUnitType.Pixel),
});
}
}
@@ -151,12 +151,7 @@
<!-- Tab: Noten -->
<ContentPage Header="Noten">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="Notenübersicht" FontSize="16" Opacity="0.4"
HorizontalAlignment="Center"/>
<TextBlock Text="Wird implementiert." FontSize="12" Opacity="0.3"
HorizontalAlignment="Center"/>
</StackPanel>
<views:GradeOverviewTabView DataContext="{Binding GradeOverviewTab}"/>
</ContentPage>
<!-- Tab: Planung -->
@@ -48,7 +48,7 @@
<DataGridTextColumn Header="Schüler" Binding="{Binding Name}" Width="2*"/>
<DataGridTextColumn Header="Sitzungen" Binding="{Binding SessionCount}" Width="Auto"/>
<DataGridTextColumn Header="Ø Bewertung" Binding="{Binding AverageDisplay}" Width="Auto"/>
<DataGridTextColumn Header="Trend" Binding="{Binding TrendSymbol}" Width="Auto"/>
<DataGridTextColumn Header="Entwicklung" Binding="{Binding TrendSymbol}" Width="Auto"/>
<DataGridTextColumn Header="Note" Binding="{Binding GradeDisplay}" Width="Auto"/>
</DataGrid.Columns>
</DataGrid>
@@ -10,7 +10,7 @@
<Border Grid.Column="0"
BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
BorderThickness="0,0,1,0">
<Grid RowDefinitions="Auto,Auto,*">
<Grid RowDefinitions="Auto,Auto,Auto,*">
<StackPanel Grid.Row="0" Orientation="Horizontal" Spacing="6" Margin="10,10,10,6">
<Button Content=" Sitzung" Command="{Binding AddSessionCommand}" HorizontalAlignment="Stretch"/>
@@ -20,7 +20,10 @@
<Button Grid.Row="1" Content="Ø Mitarbeitsnote" Command="{Binding ComputeGradeCommand}"
HorizontalAlignment="Stretch" Margin="10,0,10,6"/>
<ListBox Grid.Row="2"
<Button Grid.Row="2" Content="Mitarbeits-Assistent" Command="{Binding OpenWizardCommand}"
HorizontalAlignment="Stretch" Margin="10,0,10,6"/>
<ListBox Grid.Row="3"
ItemsSource="{Binding Sessions}"
SelectedItem="{Binding SelectedSession}"
BorderThickness="0">
@@ -48,7 +51,7 @@
<ToggleButton Content="◇ Kompetenzen"
IsChecked="{Binding CompetencyTagsVisible}"
FontSize="11" Padding="8,3"/>
<ToggleButton Content="◈ Schüler-Ratings"
<ToggleButton Content="◈ Schüler-Bewertungen"
IsChecked="{Binding StudentCompetencyRatingsVisible}"
FontSize="11" Padding="8,3"/>
</StackPanel>
@@ -24,6 +24,7 @@ public partial class ParticipationTabView : UserControl
vm.OnAddSession = ShowAddSessionDialog;
vm.OnQuickInput = ShowQuickInputDialog;
vm.OnComputeGrade = ShowComputeGradeDialog;
vm.OnOpenWizard = ShowWizardDialog;
vm.Aspects.CollectionChanged += (_, _) => BuildColumns();
vm.PropertyChanged += (_, pe) =>
{
@@ -60,6 +61,19 @@ public partial class ParticipationTabView : UserControl
});
}
grid.Columns.Add(new DataGridTemplateColumn
{
Header = "HA",
Width = new DataGridLength(50, DataGridLengthUnitType.Pixel),
CellTemplate = BuildHomeworkCellTemplate(),
});
grid.Columns.Add(new DataGridTemplateColumn
{
Header = "Anwesenheit",
Width = new DataGridLength(80, DataGridLengthUnitType.Pixel),
CellTemplate = BuildAttendanceCellTemplate(),
});
// Kompetenz-Spalten (opt-in)
if (_vm.StudentCompetencyRatingsVisible && _vm.ActiveCompetencyCodes.Count > 0)
{
@@ -119,6 +133,66 @@ public partial class ParticipationTabView : UserControl
});
}
private static IDataTemplate BuildHomeworkCellTemplate()
{
return new FuncDataTemplate<ParticipationStudentRow>((row, _) =>
{
if (row is null) return new TextBlock();
var btn = new Button
{
Content = "HA vergessen",
FontSize = 10,
Padding = new Avalonia.Thickness(5, 1),
Opacity = row.HomeworkMissing ? 1.0 : 0.25,
Command = row.ToggleHomeworkCommand,
};
ToolTip.SetTip(btn, "Hausaufgaben vergessen (an/aus)");
row.PropertyChanged += (_, pe) =>
{
if (pe.PropertyName == nameof(ParticipationStudentRow.HomeworkMissing))
btn.Opacity = row.HomeworkMissing ? 1.0 : 0.25;
};
return new StackPanel
{
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
Children = { btn },
};
});
}
private static IDataTemplate BuildAttendanceCellTemplate()
{
return new FuncDataTemplate<ParticipationStudentRow>((row, _) =>
{
if (row is null) return new TextBlock();
var btn = new Button
{
Content = string.IsNullOrEmpty(row.AttendanceLabel) ? "anwesend" : row.AttendanceLabel,
FontSize = 10,
Padding = new Avalonia.Thickness(5, 1),
Command = row.CycleAttendanceCommand,
};
ToolTip.SetTip(btn, row.AttendanceTooltip);
row.PropertyChanged += (_, pe) =>
{
if (pe.PropertyName == nameof(ParticipationStudentRow.AttendanceLabel))
{
btn.Content = string.IsNullOrEmpty(row.AttendanceLabel) ? "anwesend" : row.AttendanceLabel;
ToolTip.SetTip(btn, row.AttendanceTooltip);
}
};
return new StackPanel
{
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
Children = { btn },
};
});
}
private static string AspectShortcut(int i) => i switch
{
0 => "Q", 1 => "W", 2 => "E", 3 => "R", 4 => "T", _ => ""
@@ -163,4 +237,24 @@ public partial class ParticipationTabView : UserControl
if (owner is not null)
await dialog.ShowDialog(owner);
}
private async Task ShowWizardDialog(ParticipationTabViewModel tabVm)
{
var dialogVm = new ParticipationWizardDialogViewModel(
App.Services.GetRequiredService<IParticipationSessionRepository>(),
App.Services.GetRequiredService<IParticipationRepository>(),
App.Services.GetRequiredService<IParticipationAspectRepository>(),
App.Services.GetRequiredService<IParticipationSectionRepository>(),
App.Services.GetRequiredService<IStudentRepository>(),
App.Services.GetRequiredService<IExamRepository>(),
App.Services.GetRequiredService<IExamResultRepository>(),
App.Services.GetRequiredService<IGradeRepository>(),
App.Services.GetRequiredService<GradingService>(),
tabVm.GroupId, tabVm.SchoolYear, tabVm.GradingSystem, tabVm.GroupLabel);
var dialog = new ParticipationWizardDialog { DataContext = dialogVm };
var owner = TopLevel.GetTopLevel(this) as Window;
if (owner is not null)
await dialog.ShowDialog(owner);
}
}
@@ -0,0 +1,160 @@
<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.ParticipationWizardDialog"
x:DataType="vm:ParticipationWizardDialogViewModel"
Title="Mitarbeits-Assistent"
Width="920" Height="760" MinWidth="720" MinHeight="520"
CanResize="True" WindowStartupLocation="CenterOwner">
<Grid RowDefinitions="Auto,Auto,*,Auto,Auto,Auto" Margin="24">
<StackPanel Grid.Row="0" Spacing="2">
<TextBlock Text="{Binding GroupLabel}" FontSize="13" Opacity="0.5"/>
<Grid ColumnDefinitions="Auto,*,Auto">
<TextBlock Grid.Column="0" Text="{Binding StudentName}" FontSize="18" FontWeight="SemiBold"/>
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="8">
<Button Content="◂ Zurück" Command="{Binding PreviousStudentCommand}"/>
<TextBlock Text="{Binding ProgressText}" VerticalAlignment="Center" Opacity="0.6" FontSize="12"/>
<Button Content="Weiter ▸" Command="{Binding NextStudentCommand}"/>
</StackPanel>
</Grid>
</StackPanel>
<TextBlock Grid.Row="1" Text="Zeitleiste" FontSize="13" FontWeight="SemiBold" Margin="0,14,0,6"/>
<ScrollViewer Grid.Row="2" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">
<ItemsControl ItemsSource="{Binding Timeline}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Spacing="10"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Styles>
<Style Selector="Border.sectionband">
<Setter Property="Background" Value="#E1F5EE"/>
<Setter Property="BorderThickness" Value="0"/>
</Style>
<Style Selector="Border.sectionband.open">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1.5"/>
<Setter Property="BorderBrush" Value="{DynamicResource SystemControlForegroundBaseMediumBrush}"/>
</Style>
<Style Selector="Button.haicon">
<Setter Property="Opacity" Value="0.2"/>
</Style>
<Style Selector="Button.haicon.active">
<Setter Property="Opacity" Value="1"/>
<Setter Property="Background" Value="#E24B4A"/>
<Setter Property="Foreground" Value="White"/>
</Style>
<Style Selector="Button.attendanceicon">
<Setter Property="Opacity" Value="0.2"/>
</Style>
<Style Selector="Button.attendanceicon.active">
<Setter Property="Opacity" Value="1"/>
<Setter Property="Background" Value="#EF9F27"/>
<Setter Property="Foreground" Value="White"/>
</Style>
</ItemsControl.Styles>
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="vm:WizardSectionGroup">
<Border Classes="sectionband" Classes.open="{Binding IsOpen}" CornerRadius="6" Padding="8,8" VerticalAlignment="Top">
<StackPanel Spacing="6">
<TextBlock Text="{Binding BandLabel}" FontSize="10" Opacity="0.6" TextAlignment="Center"/>
<ItemsControl ItemsSource="{Binding Points}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Spacing="8"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="vm:WizardTimelinePoint">
<StackPanel Width="54" ToolTip.Tip="{Binding TooltipText}">
<TextBlock Text="{Binding DateDisplay}" FontSize="9" Opacity="0.5" HorizontalAlignment="Center"/>
<Border IsVisible="{Binding !IsExam}" Background="{DynamicResource SystemControlBackgroundAccentBrush}"
CornerRadius="3" Padding="4,1" HorizontalAlignment="Center" Margin="0,2">
<TextBlock Text="{Binding RatingLabel}" FontSize="11" Foreground="White" HorizontalAlignment="Center"/>
</Border>
<Border IsVisible="{Binding IsExam}" Background="#7F77DD"
CornerRadius="3" Padding="3,1" HorizontalAlignment="Center" Margin="0,2">
<TextBlock Text="{Binding ExamLabel}" FontSize="9" Foreground="White"
HorizontalAlignment="Center" TextWrapping="Wrap" TextAlignment="Center"/>
</Border>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="2"
IsVisible="{Binding !IsExam}">
<Button Classes="haicon" Classes.active="{Binding HasHomework}"
Content="HA" FontSize="8" Padding="3,0" Command="{Binding ToggleHomeworkCommand}"
ToolTip.Tip="Hausaufgaben vergessen (an/aus)"/>
<Button Classes="attendanceicon" Classes.active="{Binding IsAbsent}"
Content="{Binding AttendanceButtonLabel}" FontSize="8" Padding="3,0"
Command="{Binding CycleAttendanceCommand}"
ToolTip.Tip="{Binding AttendanceTooltip}"/>
<TextBlock Text="💬" FontSize="9" IsVisible="{Binding HasNote}" ToolTip.Tip="Bemerkung"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<StackPanel Grid.Row="3" Spacing="4" Margin="0,14,0,0">
<TextBlock Text="Abschnittsnoten dieses Schülers" FontSize="13" FontWeight="SemiBold"/>
<ItemsControl ItemsSource="{Binding Sections}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="vm:WizardSectionRow">
<Grid ColumnDefinitions="140,170,100,Auto,*" Margin="0,3">
<TextBlock Grid.Column="0" Text="{Binding Label}" VerticalAlignment="Center" FontSize="12"/>
<TextBlock Grid.Column="1" Text="{Binding RangeDisplay}" VerticalAlignment="Center" FontSize="11" Opacity="0.6"/>
<TextBox Grid.Column="2" Text="{Binding Value}" IsEnabled="{Binding !IsOpen}"
PlaceholderText="Note" FontSize="12"/>
<Button Grid.Column="3" Content="Speichern" Command="{Binding SaveCommand}"
IsVisible="{Binding !IsOpen}" Margin="6,0,0,0" FontSize="11" Padding="8,3"/>
<TextBlock Grid.Column="4" Text="{Binding StatusMessage}" Foreground="Green" FontSize="11"
VerticalAlignment="Center" Margin="8,0,0,0"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<Grid Grid.Row="4" ColumnDefinitions="*,*" Margin="0,14,0,0">
<Border Grid.Column="0" BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
BorderThickness="1" CornerRadius="6" Padding="10" Margin="0,0,6,0">
<StackPanel Spacing="6">
<TextBlock Text="Abschnitt abschließen" FontSize="12" FontWeight="SemiBold"/>
<Grid ColumnDefinitions="*,100" ColumnSpacing="6">
<TextBox Grid.Column="0" Text="{Binding NewSectionLabel}" PlaceholderText="Bezeichnung"/>
<TextBox Grid.Column="1" Text="{Binding NewSectionEndDateText}" PlaceholderText="TT.MM.JJJJ"/>
</Grid>
<TextBlock Text="{Binding SectionValidationMessage}" Foreground="Red" FontSize="11"
IsVisible="{Binding SectionValidationMessage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<Button Content="Abschnitt für alle Schüler abschließen" Command="{Binding CloseSectionCommand}"
HorizontalAlignment="Stretch"/>
</StackPanel>
</Border>
<Border Grid.Column="1" BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
BorderThickness="1" CornerRadius="6" Padding="10" Margin="6,0,0,0">
<StackPanel Spacing="6">
<TextBlock Text="Halbjahresnote aus Abschnitten" FontSize="12" FontWeight="SemiBold"/>
<ComboBox ItemsSource="{Binding RollupPeriodOptions}" SelectedItem="{Binding RollupPeriod}"
HorizontalAlignment="Stretch"/>
<TextBlock Text="{Binding RollupStatusMessage}" Foreground="Green" FontSize="11"
IsVisible="{Binding RollupStatusMessage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<Button Content="Für alle Schüler übernehmen" Command="{Binding ApplyRollupCommand}"
HorizontalAlignment="Stretch"/>
</StackPanel>
</Border>
</Grid>
<Button Grid.Row="5" Content="Schließen" HorizontalAlignment="Right" Margin="0,14,0,0" Click="OnClose"/>
</Grid>
</Window>
@@ -0,0 +1,11 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
namespace LehrerApp.Desktop.Views.Groups;
public partial class ParticipationWizardDialog : Window
{
public ParticipationWizardDialog() => InitializeComponent();
private void OnClose(object? sender, RoutedEventArgs e) => Close();
}
@@ -0,0 +1,64 @@
<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.ReportGradeDialog"
x:DataType="vm:ReportGradeDialogViewModel"
Title="Zeugnisnoten"
Width="820" Height="680" MinWidth="640" MinHeight="440"
CanResize="True" WindowStartupLocation="CenterOwner">
<Grid RowDefinitions="Auto,Auto,*,Auto" Margin="24">
<StackPanel Grid.Row="0" Spacing="2">
<TextBlock Text="{Binding GroupLabel}" FontSize="18" FontWeight="SemiBold"/>
<TextBlock Text="{Binding SchemeSummary}" FontSize="12" Opacity="0.6"/>
</StackPanel>
<StackPanel Grid.Row="1" Orientation="Horizontal" Spacing="12" Margin="0,12,0,10">
<StackPanel Orientation="Horizontal" Spacing="6">
<TextBlock Text="Zeitraum:" VerticalAlignment="Center"/>
<ComboBox ItemsSource="{Binding PeriodOptions}" SelectedItem="{Binding SelectedPeriod}" MinWidth="170"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="6">
<TextBlock Text="Rundung:" VerticalAlignment="Center"/>
<ComboBox ItemsSource="{Binding RoundingOptions}" SelectedItem="{Binding RoundingRuleName}" MinWidth="140"/>
</StackPanel>
</StackPanel>
<ScrollViewer Grid.Row="2">
<ItemsControl ItemsSource="{Binding Rows}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="vm:ReportGradeRow">
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
BorderThickness="0,0,0,1" Padding="0,8">
<StackPanel Spacing="6">
<Grid ColumnDefinitions="180,90,*,120,Auto,Auto">
<TextBlock Grid.Column="0" Text="{Binding Name}" VerticalAlignment="Center" FontSize="13"/>
<TextBlock Grid.Column="1" Text="{Binding CalculatedDisplay}" VerticalAlignment="Center"
FontSize="13" Opacity="0.7" ToolTip.Tip="Berechnet"/>
<TextBox Grid.Column="2" Text="{Binding OverrideValue}" PlaceholderText="Übersteuern (optional)"
IsEnabled="{Binding !IsLocked}"/>
<TextBox Grid.Column="3" Text="{Binding OverrideReason}" PlaceholderText="Begründung"
IsEnabled="{Binding !IsLocked}" Margin="6,0,0,0"/>
<TextBlock Grid.Column="4" Text="{Binding FinalDisplay}" FontWeight="SemiBold"
VerticalAlignment="Center" Margin="10,0" FontSize="14"/>
<StackPanel Grid.Column="5" Orientation="Horizontal" Spacing="6">
<Button Content="Speichern" Command="{Binding SaveCommand}" IsEnabled="{Binding !IsLocked}"/>
<Button Content="{Binding LockLabel}" Command="{Binding ToggleLockCommand}"/>
</StackPanel>
</Grid>
<TextBlock Text="{Binding ValidationMessage}" Foreground="Red" FontSize="11"
IsVisible="{Binding ValidationMessage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<Grid Grid.Row="3" ColumnDefinitions="Auto,*,Auto" Margin="0,16,0,0">
<Button Grid.Column="0" Content="Als CSV exportieren" Click="OnExportClick"/>
<Button Grid.Column="2" Content="Schließen" Click="OnClose"/>
</Grid>
</Grid>
</Window>
@@ -0,0 +1,30 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Platform.Storage;
using LehrerApp.Desktop.ViewModels.Groups;
namespace LehrerApp.Desktop.Views.Groups;
public partial class ReportGradeDialog : Window
{
public ReportGradeDialog() => InitializeComponent();
private async void OnExportClick(object? sender, RoutedEventArgs e)
{
if (DataContext is not ReportGradeDialogViewModel vm) return;
var topLevel = TopLevel.GetTopLevel(this);
if (topLevel is null) return;
var file = await topLevel.StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions
{
Title = "Zeugnisnoten exportieren",
SuggestedFileName = $"Zeugnisnoten_{vm.GroupLabel}.csv",
FileTypeChoices = [new FilePickerFileType("CSV-Dateien") { Patterns = ["*.csv"] }],
});
if (file is null) return;
await File.WriteAllTextAsync(file.Path.LocalPath, vm.ExportCsv());
}
private void OnClose(object? sender, RoutedEventArgs e) => Close();
}
@@ -0,0 +1,55 @@
<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.StudentGradesDialog"
x:DataType="vm:StudentGradesDialogViewModel"
Title="{Binding StudentName}"
Width="560" Height="600" MinWidth="480" MinHeight="360"
CanResize="True" WindowStartupLocation="CenterOwner">
<Grid RowDefinitions="Auto,Auto,*,Auto" Margin="24">
<TextBlock Grid.Row="0" Text="{Binding StudentName}" FontSize="18" FontWeight="SemiBold"/>
<Button Grid.Row="1" Content=" Note hinzufügen" Command="{Binding AddEntryCommand}"
HorizontalAlignment="Left" Margin="0,10,0,10"/>
<ScrollViewer Grid.Row="2">
<ItemsControl ItemsSource="{Binding Entries}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="vm:GradeEditItem">
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
BorderThickness="0,0,0,1" Padding="0,8">
<StackPanel Spacing="6">
<Grid ColumnDefinitions="120,*,90,90" ColumnSpacing="6">
<ComboBox Grid.Column="0" ItemsSource="{x:Static vm:GradeCategoryDisplay.Options}"
SelectedItem="{Binding CategoryName}"/>
<TextBox Grid.Column="1" Text="{Binding Value}" PlaceholderText="Wert (z.B. 2 oder 11)"/>
<TextBox Grid.Column="2" Text="{Binding DateText}" PlaceholderText="TT.MM.JJJJ"/>
<NumericUpDown Grid.Column="3" Value="{Binding Weight}" Minimum="0" Maximum="10"
Increment="0.1" FormatString="0.#" ShowButtonSpinner="False"
ToolTip.Tip="Gewichtung"/>
</Grid>
<TextBox Text="{Binding Note}" PlaceholderText="Notiz (optional)" FontSize="12"/>
<TextBlock Text="{Binding ValidationMessage}" Foreground="Red" FontSize="11"
IsVisible="{Binding ValidationMessage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<Grid ColumnDefinitions="*,Auto,Auto">
<TextBlock Grid.Column="0" Text="{Binding CreatedAtDisplay}" FontSize="11" Opacity="0.5"
VerticalAlignment="Center"/>
<Button Grid.Column="1" Content="Speichern" Command="{Binding SaveCommand}" Margin="0,0,6,0"/>
<Button Grid.Column="2" Content="Löschen" Command="{Binding DeleteCommand}"/>
</Grid>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<TextBlock Grid.Row="2" Text="Noch keine Noten erfasst." Opacity="0.4" FontSize="13"
HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,20,0,0"
IsVisible="{Binding !Entries.Count}"/>
<Button Grid.Row="3" Content="Fertig" HorizontalAlignment="Stretch" Margin="0,16,0,0" Click="OnClose"/>
</Grid>
</Window>
@@ -0,0 +1,11 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
namespace LehrerApp.Desktop.Views.Groups;
public partial class StudentGradesDialog : Window
{
public StudentGradesDialog() => InitializeComponent();
private void OnClose(object? sender, RoutedEventArgs e) => Close();
}