This commit is contained in:
2026-03-29 23:47:31 +02:00
commit 216d5d2280
75 changed files with 5702 additions and 0 deletions

View File

@@ -0,0 +1,90 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using LehrerApp.Core.Interfaces;
using LehrerApp.Core.Models;
using LehrerApp.Core.Services;
namespace LehrerApp.Desktop.ViewModels.Groups;
public partial class AddGroupDialogViewModel : ObservableObject
{
private readonly IGroupRepository _groups;
private readonly SchoolYearService _schoolYearService;
[ObservableProperty] private string _name = "";
[ObservableProperty] private string _subject = "";
[ObservableProperty] private GroupType _type = GroupType.Course;
[ObservableProperty] private int _gradeLevel = 10;
[ObservableProperty] private GradingSystem _gradingSystem = GradingSystem.Grades1To6;
[ObservableProperty] private int? _hoursPerWeek;
[ObservableProperty] private string _selectedSchoolYear = "";
[ObservableProperty] private string _validationMessage = "";
public List<string> SchoolYears { get; }
public List<GroupTypeItem> GroupTypes { get; } =
[
new(GroupType.Course, "Fachkurs"),
new(GroupType.Class, "Klassenverband"),
];
public List<GradingSystemItem> GradingSystems { get; } =
[
new(GradingSystem.Grades1To6, "Noten 16 (Sek I)"),
new(GradingSystem.Points0To15, "Punkte 015 (Oberstufe)"),
];
// Wird vom Dialog aufgerufen wenn OK geklickt wurde
public LearningGroup? Result { get; private set; }
public AddGroupDialogViewModel(
IGroupRepository groups,
SchoolYearService schoolYearService)
{
_groups = groups;
_schoolYearService = schoolYearService;
SchoolYears = schoolYearService.RecentSchoolYears(3);
SelectedSchoolYear = schoolYearService.CurrentSchoolYear();
// Automatisch Notensystem vorschlagen wenn Klassenstufe sich ändert
PropertyChanged += (_, e) =>
{
if (e.PropertyName == nameof(GradeLevel))
GradingSystem = GradeLevel >= 11
? GradingSystem.Points0To15
: GradingSystem.Grades1To6;
};
}
[RelayCommand]
private bool Save()
{
// Validierung
if (string.IsNullOrWhiteSpace(Name))
{
ValidationMessage = "Bitte einen Namen eingeben.";
return false;
}
if (GradeLevel is < 1 or > 13)
{
ValidationMessage = "Klassenstufe muss zwischen 1 und 13 liegen.";
return false;
}
Result = new LearningGroup
{
Name = Name.Trim(),
Subject = string.IsNullOrWhiteSpace(Subject) ? null : Subject.Trim(),
Type = Type,
GradeLevel = GradeLevel,
GradingSystem = GradingSystem,
SchoolYear = SelectedSchoolYear,
HoursPerWeek = HoursPerWeek,
};
_groups.Save(Result);
ValidationMessage = "";
return true;
}
}
public record GroupTypeItem(GroupType Value, string Label);
public record GradingSystemItem(GradingSystem Value, string Label);

View File

@@ -0,0 +1,208 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using LehrerApp.Core.Interfaces;
using LehrerApp.Core.Models;
using LehrerApp.Core.Services;
using System.Collections.ObjectModel;
namespace LehrerApp.Desktop.ViewModels.Groups;
// ── Gruppenliste ──────────────────────────────────────────────────────────────
public partial class GroupListViewModel : ObservableObject
{
private readonly IGroupRepository _groups;
private readonly SchoolYearService _schoolYearService;
[ObservableProperty] private string _selectedSchoolYear = "";
[ObservableProperty] private string _searchText = "";
[ObservableProperty] private GroupListItem? _selectedGroup;
public ObservableCollection<string> SchoolYears { get; } = [];
public ObservableCollection<GroupListItem> Groups { get; } = [];
public GroupListViewModel(
IGroupRepository groups,
SchoolYearService schoolYearService)
{
_groups = groups;
_schoolYearService = schoolYearService;
foreach (var y in schoolYearService.RecentSchoolYears())
SchoolYears.Add(y);
SelectedSchoolYear = schoolYearService.CurrentSchoolYear();
LoadGroups();
}
partial void OnSelectedSchoolYearChanged(string value) => LoadGroups();
partial void OnSearchTextChanged(string value) => LoadGroups();
private void LoadGroups()
{
Groups.Clear();
var all = _groups.GetBySchoolYear(SelectedSchoolYear);
var filtered = string.IsNullOrWhiteSpace(SearchText)
? all
: all.Where(g =>
g.Name.Contains(SearchText, StringComparison.OrdinalIgnoreCase) ||
(g.Subject?.Contains(SearchText, StringComparison.OrdinalIgnoreCase) ?? false));
foreach (var g in filtered)
Groups.Add(new GroupListItem(g));
}
[RelayCommand]
private void AddGroup()
{
// TODO: Dialog öffnen
}
[RelayCommand]
private void Refresh() => LoadGroups();
}
public class GroupListItem
{
public Guid Id { get; }
public string Name { get; }
public string Subject { get; }
public string SchoolYear { get; }
public int GradeLevel { get; }
public string TypeLabel { get; }
public string GradingLabel { get; }
public GroupListItem(LearningGroup g)
{
Id = g.Id;
Name = g.Name;
Subject = g.Subject ?? "";
SchoolYear = g.SchoolYear;
GradeLevel = g.GradeLevel;
TypeLabel = g.Type == GroupType.Class ? "Klasse" : "Kurs";
GradingLabel = g.GradingSystem == GradingSystem.Grades1To6
? "Noten 16"
: "Punkte 015";
}
}
// ── Gruppendetail (Tab-Navigation) ────────────────────────────────────────────
public partial class GroupDetailViewModel : ObservableObject
{
private readonly IGroupRepository _groups;
private readonly IStudentRepository _students;
private readonly IEnrollmentRepository _enrollments;
private readonly IExamRepository _exams;
private readonly SchoolYearService _schoolYearService;
[ObservableProperty] private LearningGroup? _group;
[ObservableProperty] private string _groupTitle = "";
[ObservableProperty] private int _studentCount;
[ObservableProperty] private GroupTab _activeTab = GroupTab.Overview;
// Sub-ViewModels für Tabs lazy geladen
public ObservableCollection<StudentSummary> Students { get; } = [];
public ObservableCollection<ExamSummary> Exams { get; } = [];
public GroupDetailViewModel(
IGroupRepository groups,
IStudentRepository students,
IEnrollmentRepository enrollments,
IExamRepository exams,
SchoolYearService schoolYearService)
{
_groups = groups;
_students = students;
_enrollments = enrollments;
_exams = exams;
_schoolYearService = schoolYearService;
}
public void LoadGroup(Guid groupId)
{
Group = _groups.GetById(groupId);
if (Group is null) return;
GroupTitle = $"{Group.Name} · {Group.SchoolYear}";
LoadStudents();
LoadExams();
}
[RelayCommand]
private void SwitchTab(GroupTab tab)
{
ActiveTab = tab;
}
private void LoadStudents()
{
if (Group is null) return;
Students.Clear();
var enrolled = _students.GetByGroup(Group.Id, Group.SchoolYear);
StudentCount = enrolled.Count;
foreach (var s in enrolled)
Students.Add(new StudentSummary(s));
}
private void LoadExams()
{
if (Group is null) return;
Exams.Clear();
foreach (var e in _exams.GetByGroup(Group.Id))
Exams.Add(new ExamSummary(e));
}
[RelayCommand]
private void AddStudent()
{
// TODO: Schüler-Auswahl-Dialog
}
[RelayCommand]
private void AddExam()
{
// TODO: Klausur-Erstellen-Dialog
}
}
public enum GroupTab { Overview, Students, Exams, Grades, Planner, Documentation }
public class StudentSummary
{
public Guid Id { get; }
public string FullName { get; }
public StudentSummary(Core.Models.Student s)
{
Id = s.Id;
FullName = s.FullName;
}
}
public class ExamSummary
{
public Guid Id { get; }
public string Title { get; }
public string Date { get; }
public string Status { get; }
public ExamSummary(Core.Models.Exam e)
{
Id = e.Id;
Title = e.Title;
Date = e.Date.ToString("dd.MM.yyyy");
Status = e.Status switch
{
ExamStatus.Planned => "Geplant",
ExamStatus.Conducted => "Durchgeführt",
ExamStatus.Graded => "Korrigiert",
ExamStatus.Returned => "Zurückgegeben",
_ => "",
};
}
}