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
@@ -337,6 +337,90 @@ public sealed class AddEditWorkTaskDialogViewModelTests
var vm = new AddEditWorkTaskDialogViewModel(source, []);
Assert.True(vm.IsReminder);
}
[Fact]
public void Save_MitPrioritaet_UebernimmtSieInsResult()
{
var vm = new AddEditWorkTaskDialogViewModel(null, [])
{
Title = "Wichtig",
SelectedPriority = TaskPriorityDisplay.Label(TaskPriority.High),
};
vm.SaveCommand.Execute(null);
Assert.Equal(TaskPriority.High, vm.Result!.Priority);
}
[Fact]
public void Konstruktor_OhneQuelle_StartetMitNormalerPrioritaet()
{
var vm = new AddEditWorkTaskDialogViewModel(null, []);
Assert.Equal(TaskPriorityDisplay.Label(TaskPriority.Normal), vm.SelectedPriority);
}
[Fact]
public void ChecklistItems_HinzufuegenUndEntfernen_WirdInResultUebernommen()
{
var vm = new AddEditWorkTaskDialogViewModel(null, []) { Title = "Material einsammeln" };
vm.NewChecklistItemText = "Erste Position";
vm.AddChecklistItemCommand.Execute(null);
vm.NewChecklistItemText = "Zweite Position";
vm.AddChecklistItemCommand.Execute(null);
Assert.Equal(["Erste Position", "Zweite Position"], vm.ChecklistItems.Select(c => c.Label));
Assert.Equal("", vm.NewChecklistItemText);
vm.RemoveChecklistItemCommand.Execute(vm.ChecklistItems[0]);
vm.SaveCommand.Execute(null);
var item = Assert.Single(vm.Result!.ChecklistItems);
Assert.Equal("Zweite Position", item.Label);
Assert.Null(item.StudentId);
}
[Fact]
public void Konstruktor_BestehendeAufgabeMitChecklist_UebernimmtEintraege()
{
var source = new WorkTask
{
Title = "Alt",
ChecklistItems = [new ChecklistItem { Label = "Schon da", IsDone = true }],
};
var vm = new AddEditWorkTaskDialogViewModel(source, []);
var row = Assert.Single(vm.ChecklistItems);
Assert.Equal("Schon da", row.Label);
Assert.True(row.IsDone);
}
[Fact]
public void FillFromRoster_OhneGruppeOderLader_MachtNichts()
{
var vm = new AddEditWorkTaskDialogViewModel(null, []);
vm.FillFromRosterCommand.Execute(null);
Assert.Empty(vm.ChecklistItems);
}
[Fact]
public void FillFromRoster_BefuelltAusRosterUndUeberspringtDoppelteSchueler()
{
var group = new LearningGroup { Name = "8a" };
var studentId = Guid.NewGuid();
var otherStudentId = Guid.NewGuid();
var vm = new AddEditWorkTaskDialogViewModel(null, [group], loadRoster: _ =>
[(studentId, "Anna Beispiel"), (otherStudentId, "Ben Muster")])
{
SelectedGroup = group,
};
Assert.True(vm.CanFillFromRoster);
vm.FillFromRosterCommand.Execute(null);
Assert.Equal(2, vm.ChecklistItems.Count);
vm.FillFromRosterCommand.Execute(null);
Assert.Equal(2, vm.ChecklistItems.Count); // kein Duplikat beim zweiten Aufruf
}
}
public sealed class TimeTrackingViewModelTests