Schüler hinzufügen, Kompetenzen

This commit is contained in:
2026-06-23 13:46:39 +02:00
parent b2211270b4
commit 220969fdfa
23 changed files with 1230 additions and 88 deletions
@@ -25,10 +25,15 @@
<TextBox Text="{Binding Name}" PlaceholderText="{Binding NameHint}"/>
</StackPanel>
<!-- Fach nur bei Kurs relevant -->
<!-- Fach nur bei Kurs, AutoComplete aus bekannten Fächern -->
<StackPanel Spacing="4" IsVisible="{Binding IsKurs}">
<TextBlock Text="Fach (optional)" FontSize="12" Opacity="0.7"/>
<TextBox Text="{Binding Subject}" PlaceholderText="z.B. Chemie, Mathematik"/>
<AutoCompleteBox Text="{Binding Subject}"
ItemsSource="{Binding KnownSubjectNames}"
FilterMode="Contains"
MinimumPrefixLength="0"
PlaceholderText="z.B. Chemie, Mathematik"
HorizontalAlignment="Stretch"/>
</StackPanel>
<Grid ColumnDefinitions="*,12,*">
@@ -4,10 +4,10 @@
x:Class="LehrerApp.Desktop.Views.Groups.AddStudentToGroupDialog"
x:DataType="vm:AddStudentToGroupDialogViewModel"
Title="Schüler hinzufügen"
Width="420" Height="500"
Width="420" Height="560"
CanResize="False" WindowStartupLocation="CenterOwner">
<Grid RowDefinitions="Auto,*,Auto" Margin="24">
<Grid RowDefinitions="Auto,*,Auto,Auto" Margin="24">
<!-- Überschrift + Suche -->
<StackPanel Grid.Row="0" Spacing="12" Margin="0,0,0,12">
@@ -29,8 +29,32 @@
</ListBox.ItemTemplate>
</ListBox>
<!-- Zeitraum-Auswahl -->
<Border Grid.Row="2"
BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
BorderThickness="1" CornerRadius="6" Padding="14,10" Margin="0,10,0,0">
<StackPanel Spacing="8">
<TextBlock Text="Einschreibungszeitraum" FontSize="12" FontWeight="SemiBold" Opacity="0.7"/>
<StackPanel Orientation="Horizontal" Spacing="8">
<RadioButton Content="Ganzes Jahr" IsChecked="{Binding IsFullYear}"/>
<RadioButton Content="Nur H1" IsChecked="{Binding IsH1Only}"/>
<RadioButton Content="Nur H2" IsChecked="{Binding IsH2Only}"/>
<RadioButton Content="Datum" IsChecked="{Binding IsCustom}"/>
</StackPanel>
<Grid ColumnDefinitions="*,12,*"
IsVisible="{Binding IsCustomPeriod}">
<TextBox Grid.Column="0" Text="{Binding JoinedAtText}"
PlaceholderText="Eintr. TT.MM.JJJJ" FontSize="12"/>
<TextBox Grid.Column="2" Text="{Binding LeftAtText}"
PlaceholderText="Austr. TT.MM.JJJJ" FontSize="12"/>
</Grid>
</StackPanel>
</Border>
<!-- Validation + Buttons -->
<StackPanel Grid.Row="2" Spacing="12" Margin="0,16,0,0">
<StackPanel Grid.Row="3" Spacing="12" Margin="0,16,0,0">
<TextBlock Text="{Binding ValidationMessage}" Foreground="Red" FontSize="12"
IsVisible="{Binding ValidationMessage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<Grid ColumnDefinitions="*,8,*">
@@ -53,9 +53,8 @@
CanUserResizeColumns="True"
Margin="0">
<DataGrid.Columns>
<DataGridTextColumn Header="Name"
Binding="{Binding FullName}"
Width="*"/>
<DataGridTextColumn Header="Name" Binding="{Binding FullName}" Width="*"/>
<DataGridTextColumn Header="Zeitraum" Binding="{Binding PeriodLabel}" Width="90"/>
</DataGrid.Columns>
</DataGrid>
</ContentPage>
@@ -1,3 +1,26 @@
using Avalonia.Controls;
using LehrerApp.Desktop.ViewModels.Groups;
using Microsoft.Extensions.DependencyInjection;
namespace LehrerApp.Desktop.Views.Groups;
public partial class GroupListView : UserControl { public GroupListView() => InitializeComponent(); }
public partial class GroupListView : UserControl
{
public GroupListView() => InitializeComponent();
protected override void OnDataContextChanged(EventArgs e)
{
base.OnDataContextChanged(e);
if (DataContext is GroupListViewModel vm)
vm.OnAddGroup = ShowAddGroupDialog;
}
private async Task ShowAddGroupDialog()
{
var dialogVm = App.Services.GetRequiredService<AddGroupDialogViewModel>();
var dialog = new AddGroupDialog { DataContext = dialogVm };
var owner = TopLevel.GetTopLevel(this) as Window;
if (owner is not null)
await dialog.ShowDialog(owner);
}
}
@@ -30,15 +30,65 @@
</Grid>
</Border>
<!-- Rechte Seite: Bewertungsraster (wird per Code-Behind aufgebaut) -->
<Grid Grid.Column="1" RowDefinitions="Auto,*">
<TextBlock Grid.Row="0"
Text="{Binding SelectedSessionDisplay}"
FontSize="13" FontWeight="SemiBold" Margin="12,10,12,4"
IsVisible="{Binding SelectedSession, Converter={x:Static ObjectConverters.IsNotNull}}"/>
<!-- Rechte Seite -->
<Grid Grid.Column="1" RowDefinitions="Auto,Auto,*">
<!-- Header: Session-Titel + Toggle-Buttons -->
<Grid Grid.Row="0" ColumnDefinitions="*,Auto" Margin="12,8,12,4"
IsVisible="{Binding SelectedSession, Converter={x:Static ObjectConverters.IsNotNull}}">
<TextBlock Grid.Column="0"
Text="{Binding SelectedSessionDisplay}"
FontSize="13" FontWeight="SemiBold"
VerticalAlignment="Center"/>
<StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="6"
IsVisible="{Binding HasCompetencyCatalog}">
<ToggleButton Content="◇ Kompetenzen"
IsChecked="{Binding CompetencyTagsVisible}"
FontSize="11" Padding="8,3"/>
<ToggleButton Content="◈ Schüler-Ratings"
IsChecked="{Binding StudentCompetencyRatingsVisible}"
FontSize="11" Padding="8,3"/>
</StackPanel>
</Grid>
<!-- Kompetenz-Tag-Panel -->
<Border Grid.Row="1"
IsVisible="{Binding CompetencyTagsVisible}"
BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
BorderThickness="0,0,0,1"
Padding="12,8">
<ScrollViewer HorizontalScrollBarVisibility="Disabled" MaxHeight="200">
<ItemsControl ItemsSource="{Binding CompetencyTagGroups}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="vm:CompetencyTagGroup">
<StackPanel Spacing="4" Margin="0,0,0,10">
<TextBlock Text="{Binding DisplayName}"
FontSize="11" FontWeight="SemiBold" Opacity="0.55"/>
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="vm:CompetencyTag">
<ToggleButton IsChecked="{Binding IsSelected}"
Content="{Binding Display}"
FontSize="11"
Padding="6,3"
Margin="0,0,4,4"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Border>
<!-- DataGrid: Spalten werden in ParticipationTabView.axaml.cs dynamisch erzeugt -->
<DataGrid Grid.Row="1"
<DataGrid Grid.Row="2"
x:Name="RatingGrid"
ItemsSource="{Binding StudentRows}"
AutoGenerateColumns="False"
@@ -48,7 +98,7 @@
CanUserResizeColumns="True"
IsVisible="{Binding SelectedSession, Converter={x:Static ObjectConverters.IsNotNull}}"/>
<TextBlock Grid.Row="1"
<TextBlock Grid.Row="2"
Text="{Binding NoDataText}"
HorizontalAlignment="Center" VerticalAlignment="Center"
Opacity="0.35" FontSize="14"
@@ -21,6 +21,11 @@ public partial class ParticipationTabView : UserControl
vm.OnAddSession = ShowAddSessionDialog;
vm.OnQuickInput = ShowQuickInputDialog;
vm.Aspects.CollectionChanged += (_, _) => BuildColumns();
vm.PropertyChanged += (_, pe) =>
{
if (pe.PropertyName == nameof(ParticipationTabViewModel.RebuildColumnsSignal))
BuildColumns();
};
BuildColumns();
}
}
@@ -39,25 +44,43 @@ public partial class ParticipationTabView : UserControl
Width = new DataGridLength(160, DataGridLengthUnitType.Pixel),
});
// Aspekt-Spalten
foreach (var (aspect, i) in _vm.Aspects.Select((a, i) => (a, i)))
{
var capturedIndex = i;
var idx = i;
grid.Columns.Add(new DataGridTemplateColumn
{
Header = $"{aspect.Label} [{AspectShortcut(i)}]",
Width = new DataGridLength(1, DataGridLengthUnitType.Star),
CellTemplate = BuildCellTemplate(capturedIndex),
Header = $"{aspect.Label} [{AspectShortcut(i)}]",
Width = new DataGridLength(1, DataGridLengthUnitType.Star),
CellTemplate = BuildCellTemplate(idx, forCompetency: false),
});
}
// Kompetenz-Spalten (opt-in)
if (_vm.StudentCompetencyRatingsVisible && _vm.ActiveCompetencyCodes.Count > 0)
{
foreach (var (code, i) in _vm.ActiveCompetencyCodes.Select((c, i) => (c, i)))
{
var idx = i;
grid.Columns.Add(new DataGridTemplateColumn
{
Header = code,
Width = new DataGridLength(80, DataGridLengthUnitType.Pixel),
CellTemplate = BuildCellTemplate(idx, forCompetency: true),
});
}
}
}
private static IDataTemplate BuildCellTemplate(int aspectIndex)
private static IDataTemplate BuildCellTemplate(int cellIndex, bool forCompetency)
{
return new FuncDataTemplate<ParticipationStudentRow>((row, _) =>
{
if (row is null) return new TextBlock();
var cell = row.Cells.ElementAtOrDefault(aspectIndex);
var cell = forCompetency
? row.CompetencyCells.ElementAtOrDefault(cellIndex)
: row.Cells.ElementAtOrDefault(cellIndex);
if (cell is null) return new TextBlock();
var panel = new StackPanel
@@ -73,10 +96,10 @@ public partial class ParticipationTabView : UserControl
{
var btn = new Button
{
Content = label,
Padding = new Avalonia.Thickness(5, 1),
Content = label,
Padding = new Avalonia.Thickness(5, 1),
FontSize = 11,
Opacity = cell.Value == val ? 1.0 : 0.3,
Opacity = cell.Value == val ? 1.0 : 0.3,
};
var capturedVal = val;
btn.Click += (_, _) => cell.SetValue(capturedVal);