feat: Pädagogische Klassen-Aufgaben, Prioritäten/Checklisten, Dashboard-Schnellzugriffe

Neue Aufgabenart über bestehende Felder (WorkTask.Kind=Reminder + GroupId) statt Vererbung,
mit Priorität (TaskPriority) und optionaler Abhak-Liste (ChecklistItems, wahlweise Kurs-
Roster oder Freitext). Kurs-Dashboard zeigt jetzt eine "Anstehende Aufgaben"-Karte samt
direktem Anlege-Button; derselbe Anlege-Einstieg wurde auch ins Hauptdashboard und (als
Schnellüberblick samt fehlender Übersicht/Mitarbeit-Buttons) in die Gruppenliste gezogen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-21 22:24:38 +02:00
co-authored by Claude Sonnet 5
parent e7c3fdd4ec
commit e65a729f97
25 changed files with 878 additions and 39 deletions
@@ -12,10 +12,10 @@ public sealed class GroupOverviewViewModelTests
{
private static GroupOverviewViewModel NewVm(FakeLessons? lessons = null, FakeExams? exams = null,
FakeSessions? sessions = null, FakeEntries? entries = null, FakeStudents? students = null,
FakeDocumentation? documentation = null) =>
FakeDocumentation? documentation = null, FakeWorkTasks? tasks = null) =>
new(lessons ?? new FakeLessons(), exams ?? new FakeExams([]), sessions ?? new FakeSessions([]),
entries ?? new FakeEntries(), students ?? new FakeStudents([]), documentation ?? new FakeDocumentation(),
new AttendanceBalanceService(), new SchoolYearService());
tasks ?? new FakeWorkTasks(), new AttendanceBalanceService(), new SchoolYearService());
private static (GroupOverviewViewModel Vm, FakeLessons Lessons, FakeExams Exams,
FakeSessions Sessions, FakeEntries Entries, FakeStudents Students, Guid GroupId) BuildScenario(
@@ -301,4 +301,68 @@ public sealed class GroupOverviewViewModelTests
var (vm, _, _, _, _, _, _) = BuildScenario();
Assert.False(vm.HasMissingHomework);
}
[Fact]
public void AnstehendeAufgaben_ZeigtNurOffeneAufgabenDieserGruppeSortiertNachFaelligkeit()
{
var today = DateOnly.FromDateTime(DateTime.Today);
var groupId = Guid.NewGuid();
var otherGroupId = Guid.NewGuid();
var tasks = new FakeWorkTasks();
tasks.Add(new WorkTask { GroupId = groupId, Title = "Später fällig", DueDate = today.AddDays(10), Status = WorkTaskStatus.Open });
tasks.Add(new WorkTask { GroupId = groupId, Title = "Bald fällig", DueDate = today.AddDays(1), Status = WorkTaskStatus.Open });
tasks.Add(new WorkTask { GroupId = groupId, Title = "Erledigt", DueDate = today, Status = WorkTaskStatus.Done });
tasks.Add(new WorkTask { GroupId = otherGroupId, Title = "Anderer Kurs", DueDate = today, Status = WorkTaskStatus.Open });
var vm = NewVm(tasks: tasks);
vm.Initialize(groupId, "Testkurs");
Assert.True(vm.HasGroupTasks);
Assert.Equal(["Bald fällig", "Später fällig"], vm.GroupTasks.Select(t => t.Title));
}
[Fact]
public void AnstehendeAufgaben_OhneAufgabenIstLeer()
{
var (vm, _, _, _, _, _, _) = BuildScenario();
Assert.False(vm.HasGroupTasks);
}
[Fact]
public void NavigateToWorkload_RuftDelegatAuf()
{
var (vm, _, _, _, _, _, _) = BuildScenario();
var called = false;
vm.OnNavigateToWorkload = () => called = true;
vm.NavigateToWorkloadCommand.Execute(null);
Assert.True(called);
}
[Fact]
public async Task AddGroupTask_SpeichertErgebnisMitGruppeUndAktualisiertListe()
{
var tasks = new FakeWorkTasks();
var (vm, _, _, _, _, _, groupId) = BuildScenario();
var vmWithTasks = NewVm(tasks: tasks);
vmWithTasks.Initialize(groupId, "Testkurs");
vmWithTasks.OnAddGroupTask = requestedGroupId =>
Task.FromResult<WorkTask?>(new WorkTask { Title = "Material einsammeln", GroupId = requestedGroupId });
await vmWithTasks.AddGroupTaskCommand.ExecuteAsync(null);
var saved = Assert.Single(tasks.GetByGroup(groupId));
Assert.Equal("Material einsammeln", saved.Title);
Assert.True(vmWithTasks.HasGroupTasks);
Assert.Equal("Material einsammeln", Assert.Single(vmWithTasks.GroupTasks).Title);
}
[Fact]
public async Task AddGroupTask_OhneDelegatMachtNichts()
{
var (vm, _, _, _, _, _, _) = BuildScenario();
await vm.AddGroupTaskCommand.ExecuteAsync(null);
Assert.False(vm.HasGroupTasks);
}
}