feat: Abwesenheits-Hinweise, Fehlquote bei Zeugnisnoten, Gruppen-Dokumentation
- Schnellbewerten-Dialog: abwesende Schüler werden gedimmt und mit ihrem Anwesenheitsstatus statt der Aspektbeschriftung angezeigt, damit keine Mitarbeitsnote für nicht anwesende Schüler vergeben wird. - Zeugnisnoten-Dialog: zeigt je Schüler die Fehlquote im gewählten Zeitraum, ab 50 % hervorgehoben (informativ, keine automatische Notenänderung). - Gruppen-Tab "Dokumentation" (bisher Platzhalter) implementiert: listet alle Dokumentationseinträge der Gruppen-Schüler, mit Schüler-Filter und optionalem "Nur dieser Unterricht"-Schalter. Einträge aus anderen Lerngruppen werden standardmäßig mitangezeigt, aber gedimmt. Der Dokumentationsdialog bekommt dafür einen optionalen Schüler-Picker. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using LehrerApp.Core.Interfaces;
|
||||
using LehrerApp.Core.Models;
|
||||
using LehrerApp.Desktop.ViewModels.Students;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace LehrerApp.Desktop.ViewModels.Groups;
|
||||
|
||||
/// <summary>
|
||||
/// Gruppen-Tab "Dokumentation" (bisher Platzhalter): zeigt die Dokumentationseinträge aller
|
||||
/// aktuellen/ehemaligen Schüler dieser Gruppe an einem Ort, statt sie einzeln im Schüler-Tab
|
||||
/// aufsuchen zu müssen. Nutzer-Feedback: die Liste soll standardmäßig auch Einträge aus anderen
|
||||
/// Lerngruppen desselben Schülers mit anzeigen (optisch abgesetzt statt ausgeblendet), damit
|
||||
/// Muster aus anderen Fächern/Kursen nicht verborgen bleiben — ein Schalter blendet sie bei
|
||||
/// Bedarf ganz aus.
|
||||
/// </summary>
|
||||
public partial class GroupDocumentationTabViewModel : ObservableObject
|
||||
{
|
||||
private readonly IDocumentationRepository _docs;
|
||||
private readonly IStudentRepository _students;
|
||||
private readonly IGroupRepository _groups;
|
||||
|
||||
private Guid _groupId;
|
||||
private List<StudentOption> _groupStudents = [];
|
||||
|
||||
public static readonly StudentOption AllStudentsOption = new(Guid.Empty, "Alle Schüler");
|
||||
|
||||
public ObservableCollection<DocumentationItem> Entries { get; } = [];
|
||||
public ObservableCollection<StudentOption> StudentFilterOptions { get; } = [AllStudentsOption];
|
||||
|
||||
[ObservableProperty] private StudentOption _selectedStudentFilter = AllStudentsOption;
|
||||
[ObservableProperty] private bool _onlyThisGroup;
|
||||
|
||||
public Func<Guid, List<StudentOption>, Documentation?, Task<Documentation?>>? OnEditDocumentation { get; set; }
|
||||
public Func<DocumentationItem, Task<bool>>? OnConfirmDeleteDocumentation { get; set; }
|
||||
public Func<Documentation, string, Task<Documentation?>>? OnConductParentCall { get; set; }
|
||||
|
||||
public GroupDocumentationTabViewModel(IDocumentationRepository docs, IStudentRepository students,
|
||||
IGroupRepository groups)
|
||||
{
|
||||
_docs = docs; _students = students; _groups = groups;
|
||||
}
|
||||
|
||||
partial void OnSelectedStudentFilterChanged(StudentOption value) => Load();
|
||||
partial void OnOnlyThisGroupChanged(bool value) => Load();
|
||||
|
||||
public void Initialize(Guid groupId)
|
||||
{
|
||||
_groupId = groupId;
|
||||
_groupStudents = _students.GetByGroup(groupId)
|
||||
.Select(s => new StudentOption(s.Id, s.FullName)).ToList();
|
||||
|
||||
StudentFilterOptions.Clear();
|
||||
StudentFilterOptions.Add(AllStudentsOption);
|
||||
foreach (var s in _groupStudents) StudentFilterOptions.Add(s);
|
||||
SelectedStudentFilter = AllStudentsOption;
|
||||
Load();
|
||||
}
|
||||
|
||||
private void Load()
|
||||
{
|
||||
Entries.Clear();
|
||||
if (_groupStudents.Count == 0) return;
|
||||
|
||||
var studentNameById = _groupStudents.ToDictionary(s => s.Id, s => s.Name);
|
||||
var groupNameCache = new Dictionary<Guid, string>();
|
||||
string GroupLabel(Guid id)
|
||||
{
|
||||
if (groupNameCache.TryGetValue(id, out var cached)) return cached;
|
||||
var label = _groups.GetById(id)?.Name ?? "";
|
||||
groupNameCache[id] = label;
|
||||
return label;
|
||||
}
|
||||
|
||||
var relevantStudentIds = SelectedStudentFilter.Id == Guid.Empty
|
||||
? _groupStudents.Select(s => s.Id)
|
||||
: [SelectedStudentFilter.Id];
|
||||
|
||||
var all = relevantStudentIds
|
||||
.SelectMany(id => _docs.GetByStudent(id))
|
||||
.Where(d => !OnlyThisGroup || d.GroupId == _groupId)
|
||||
.OrderByDescending(d => d.Date);
|
||||
|
||||
foreach (var d in all)
|
||||
{
|
||||
var isOwnGroup = d.GroupId is null || d.GroupId == _groupId;
|
||||
var otherGroupLabel = isOwnGroup ? "" : GroupLabel(d.GroupId!.Value);
|
||||
Entries.Add(new DocumentationItem(d, studentNameById.GetValueOrDefault(d.StudentId, ""),
|
||||
isOwnGroup, otherGroupLabel));
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task AddDocumentation()
|
||||
{
|
||||
if (OnEditDocumentation is null || _groupStudents.Count == 0) return;
|
||||
var result = await OnEditDocumentation(_groupId, _groupStudents, null);
|
||||
if (result is null) return;
|
||||
_docs.Save(result);
|
||||
Load();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task EditDocumentation(DocumentationItem? item)
|
||||
{
|
||||
if (item is null || OnEditDocumentation is null) return;
|
||||
var result = await OnEditDocumentation(_groupId, _groupStudents, item.Model);
|
||||
if (result is null) return;
|
||||
_docs.Save(result);
|
||||
Load();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task DeleteDocumentation(DocumentationItem? item)
|
||||
{
|
||||
if (item is null) return;
|
||||
if (OnConfirmDeleteDocumentation is not null && !await OnConfirmDeleteDocumentation(item)) return;
|
||||
_docs.Delete(item.Model.Id);
|
||||
Load();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task ConductParentCall(DocumentationItem? item)
|
||||
{
|
||||
if (item is null || OnConductParentCall is null) return;
|
||||
var result = await OnConductParentCall(item.Model, item.StudentName);
|
||||
if (result is null) return;
|
||||
_docs.Save(result);
|
||||
Load();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user