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
@@ -15,7 +15,7 @@ public partial class GroupListViewModel : ObservableObject
private readonly SchoolYearService _sy;
public Action<Guid, int>? OnNavigateToDetail { get; set; }
public Action? OnAddGroup { get; set; }
public Func<Task>? OnAddGroup { get; set; }
[ObservableProperty] private string _selectedSchoolYear = "";
[ObservableProperty] private string _searchText = "";
@@ -54,7 +54,13 @@ public partial class GroupListViewModel : ObservableObject
Groups.Add(new GroupListItem(g));
}
[RelayCommand] private void AddGroup() => OnAddGroup?.Invoke();
[RelayCommand]
private async Task AddGroup()
{
if (OnAddGroup is null) return;
await OnAddGroup();
LoadGroups();
}
[RelayCommand] private void Refresh() => LoadGroups();
[RelayCommand(CanExecute = nameof(HasSelectedGroup))]
@@ -139,9 +145,15 @@ public partial class GroupDetailViewModel : ObservableObject
{
if (Group is null) return;
Students.Clear();
var enrolled = _students.GetByGroup(Group.Id, Group.SchoolYear);
var enrolled = _students.GetByGroup(Group.Id, Group.SchoolYear);
var enrollments = _enrollments.GetByGroupAndYear(Group.Id, Group.SchoolYear)
.ToDictionary(e => e.StudentId);
StudentCount = enrolled.Count;
foreach (var s in enrolled) Students.Add(new StudentSummary(s));
foreach (var s in enrolled)
{
enrollments.TryGetValue(s.Id, out var enr);
Students.Add(new StudentSummary(s, enr));
}
}
[RelayCommand]
@@ -149,7 +161,11 @@ public partial class GroupDetailViewModel : ObservableObject
{
if (OnAddStudent is null) return;
var confirmed = await OnAddStudent();
if (confirmed) LoadStudents();
if (confirmed)
{
LoadStudents();
ParticipationTab.RefreshCurrentGrid();
}
}
[RelayCommand(CanExecute = nameof(HasSelectedStudent))]
@@ -162,6 +178,7 @@ public partial class GroupDetailViewModel : ObservableObject
_enrollments.Delete(enrollment.Id);
LoadStudents();
SelectedStudent = null;
ParticipationTab.RefreshCurrentGrid();
}
partial void OnSelectedStudentChanged(StudentSummary? value) =>
@@ -175,9 +192,30 @@ public partial class GroupDetailViewModel : ObservableObject
public class StudentSummary
{
public Guid Id { get; }
public string FullName { get; }
public StudentSummary(Core.Models.Student s) { Id = s.Id; FullName = s.FullName; }
public Guid Id { get; }
public string FullName { get; }
public string PeriodLabel { get; }
public StudentSummary(Core.Models.Student s, Enrollment? e)
{
Id = s.Id;
FullName = s.FullName;
PeriodLabel = e?.Period switch
{
EnrollmentPeriod.H1Only => "H1",
EnrollmentPeriod.H2Only => "H2",
EnrollmentPeriod.Custom => BuildCustomLabel(e),
_ => "",
};
}
private static string BuildCustomLabel(Enrollment e)
{
if (e.JoinedAt.HasValue && e.LeftAt.HasValue) return $"{e.JoinedAt:dd.MM.}{e.LeftAt:dd.MM.}";
if (e.JoinedAt.HasValue) return $"ab {e.JoinedAt:dd.MM.}";
if (e.LeftAt.HasValue) return $"bis {e.LeftAt:dd.MM.}";
return "Datum";
}
}
public class ExamSummary
@@ -204,14 +242,23 @@ public class ExamSummary
public partial class AddStudentToGroupDialogViewModel : ObservableObject
{
private readonly IStudentRepository _students;
private readonly IStudentRepository _students;
private readonly IEnrollmentRepository _enrollments;
private readonly Guid _groupId;
private readonly Guid _groupId;
private readonly string _schoolYear;
[ObservableProperty] private string _searchText = "";
[ObservableProperty] private string _searchText = "";
[ObservableProperty] private StudentPickerItem? _selectedStudent;
[ObservableProperty] private string _validationMessage = "";
[ObservableProperty] private string _validationMessage = "";
[ObservableProperty] private EnrollmentPeriod _period = EnrollmentPeriod.FullYear;
[ObservableProperty] private string _joinedAtText = "";
[ObservableProperty] private string _leftAtText = "";
public bool IsFullYear { get => Period == EnrollmentPeriod.FullYear; set { if (value) Period = EnrollmentPeriod.FullYear; } }
public bool IsH1Only { get => Period == EnrollmentPeriod.H1Only; set { if (value) Period = EnrollmentPeriod.H1Only; } }
public bool IsH2Only { get => Period == EnrollmentPeriod.H2Only; set { if (value) Period = EnrollmentPeriod.H2Only; } }
public bool IsCustom { get => Period == EnrollmentPeriod.Custom; set { if (value) Period = EnrollmentPeriod.Custom; } }
public bool IsCustomPeriod => Period == EnrollmentPeriod.Custom;
public ObservableCollection<StudentPickerItem> AvailableStudents { get; } = [];
public Enrollment? Result { get; private set; }
@@ -224,6 +271,15 @@ public partial class AddStudentToGroupDialogViewModel : ObservableObject
LoadAvailableStudents();
}
partial void OnPeriodChanged(EnrollmentPeriod value)
{
OnPropertyChanged(nameof(IsFullYear));
OnPropertyChanged(nameof(IsH1Only));
OnPropertyChanged(nameof(IsH2Only));
OnPropertyChanged(nameof(IsCustom));
OnPropertyChanged(nameof(IsCustomPeriod));
}
partial void OnSearchTextChanged(string value) => LoadAvailableStudents();
private void LoadAvailableStudents()
@@ -243,11 +299,28 @@ public partial class AddStudentToGroupDialogViewModel : ObservableObject
private void Save()
{
if (SelectedStudent is null) { ValidationMessage = "Bitte einen Schüler auswählen."; return; }
DateOnly? joinedAt = null, leftAt = null;
if (Period == EnrollmentPeriod.Custom)
{
if (!string.IsNullOrWhiteSpace(JoinedAtText) &&
DateOnly.TryParseExact(JoinedAtText, "dd.MM.yyyy",
null, System.Globalization.DateTimeStyles.None, out var j))
joinedAt = j;
if (!string.IsNullOrWhiteSpace(LeftAtText) &&
DateOnly.TryParseExact(LeftAtText, "dd.MM.yyyy",
null, System.Globalization.DateTimeStyles.None, out var l))
leftAt = l;
}
Result = new Enrollment
{
StudentId = SelectedStudent.Id,
GroupId = _groupId,
SchoolYear = _schoolYear,
Period = Period,
JoinedAt = joinedAt,
LeftAt = leftAt,
};
_enrollments.Save(Result);
}
@@ -255,7 +328,7 @@ public partial class AddStudentToGroupDialogViewModel : ObservableObject
public class StudentPickerItem
{
public Guid Id { get; }
public Guid Id { get; }
public string FullName { get; }
public StudentPickerItem(Student s) { Id = s.Id; FullName = s.FullName; }
}
@@ -264,8 +337,10 @@ public class StudentPickerItem
public partial class AddGroupDialogViewModel : ObservableObject
{
private readonly IGroupRepository _groups;
private readonly SchoolYearService _sy;
private readonly IGroupRepository _groups;
private readonly ISubjectRepository _subjects;
private readonly SchoolYearService _sy;
private List<Subject> _allSubjects = [];
public List<string> TypeOptions { get; } = ["Klasse", "Kurs"];
[ObservableProperty] private string _selectedTypeName = "Kurs";
@@ -289,12 +364,15 @@ public partial class AddGroupDialogViewModel : ObservableObject
[ObservableProperty] private string _validationMessage = "";
public List<string> SchoolYears { get; }
public List<string> KnownSubjectNames { get; private set; } = [];
public LearningGroup? Result { get; private set; }
public AddGroupDialogViewModel(IGroupRepository groups, SchoolYearService sy)
public AddGroupDialogViewModel(IGroupRepository groups, ISubjectRepository subjects, SchoolYearService sy)
{
_groups = groups; _sy = sy;
SchoolYears = sy.RecentSchoolYears(3);
_groups = groups; _subjects = subjects; _sy = sy;
_allSubjects = subjects.GetAll();
KnownSubjectNames = _allSubjects.Select(s => s.Name).ToList();
SchoolYears = sy.RecentSchoolYears(3);
SelectedSchoolYear = sy.CurrentSchoolYear();
PropertyChanged += (_, e) =>
{
@@ -309,10 +387,31 @@ public partial class AddGroupDialogViewModel : ObservableObject
{
if (string.IsNullOrWhiteSpace(Name)) { ValidationMessage = "Bezeichnung erforderlich."; return; }
if (GradeLevel is < 1 or > 13) { ValidationMessage = "Klassenstufe 113."; return; }
string? subjectName = IsKurs && !string.IsNullOrWhiteSpace(Subject) ? Subject.Trim() : null;
Guid? subjectId = null;
if (subjectName is not null)
{
var existing = _allSubjects.FirstOrDefault(
s => s.Name.Equals(subjectName, StringComparison.OrdinalIgnoreCase));
if (existing is not null)
{
subjectId = existing.Id;
}
else
{
var newSubject = new Subject { Name = subjectName };
_subjects.Save(newSubject);
subjectId = newSubject.Id;
}
}
Result = new LearningGroup
{
Name = Name.Trim(),
Subject = IsKurs && !string.IsNullOrWhiteSpace(Subject) ? Subject.Trim() : null,
Subject = subjectName,
SubjectId = subjectId,
Type = IsKurs ? GroupType.Course : GroupType.Class,
GradeLevel = GradeLevel,
GradingSystem = GradingSystem,