Mitarbeits Wizzard und neue Statuslabels für Anwesenheit und Hausaufgaben

This commit is contained in:
2026-08-13 01:49:40 +02:00
parent febcb0855a
commit 741586846a
10 changed files with 1160 additions and 144 deletions
@@ -47,6 +47,7 @@ public partial class ParticipationTabViewModel : ObservableObject
public Func<Task<ParticipationSession?>>? OnAddSession { get; set; }
public Func<ParticipationTabViewModel, Task>? OnQuickInput { get; set; }
public Func<ParticipationTabViewModel, Task>? OnStatusQuickInput { get; set; }
public Func<ParticipationTabViewModel, Task>? OnComputeGrade { get; set; }
public Func<ParticipationTabViewModel, Task>? OnOpenWizard { get; set; }
@@ -119,6 +120,7 @@ public partial class ParticipationTabViewModel : ObservableObject
CompetencyTagGroups.Clear();
ActiveCompetencyCodes = [];
QuickInputCommand.NotifyCanExecuteChanged();
StatusQuickInputCommand.NotifyCanExecuteChanged();
RebuildColumnsSignal++;
return;
}
@@ -151,6 +153,7 @@ public partial class ParticipationTabViewModel : ObservableObject
StudentRows.Add(row);
}
QuickInputCommand.NotifyCanExecuteChanged();
StatusQuickInputCommand.NotifyCanExecuteChanged();
RebuildColumnsSignal++;
}
@@ -190,11 +193,12 @@ public partial class ParticipationTabViewModel : ObservableObject
if (SelectedSession is not null) LoadGrid(SelectedSession.Id);
}
private void SaveHomework(Guid sessionId, Guid studentId, bool value)
private void SaveHomework(Guid sessionId, Guid studentId, HomeworkStatus? value)
{
var entry = _entries.GetBySessionAndStudent(sessionId, studentId)
?? new ParticipationEntry { SessionId = sessionId, StudentId = studentId };
entry.HomeworkMissing = value;
entry.Homework = value;
entry.HomeworkMissing = HomeworkDisplay.CountsAsMissing(value);
_entries.Save(entry);
}
@@ -273,6 +277,14 @@ public partial class ParticipationTabViewModel : ObservableObject
private bool CanQuickInput() => SelectedSession is not null && StudentRows.Count > 0;
[RelayCommand(CanExecute = nameof(CanQuickInput))]
private async Task StatusQuickInput()
{
if (OnStatusQuickInput is null) return;
await OnStatusQuickInput(this);
if (SelectedSession is not null) LoadGrid(SelectedSession.Id);
}
[RelayCommand]
private async Task ComputeGrade()
{
@@ -338,15 +350,17 @@ public partial class ParticipationStudentRow : ObservableObject
public ObservableCollection<RatingCell> Cells { get; } = [];
public ObservableCollection<RatingCell> CompetencyCells { get; } = [];
[ObservableProperty] private bool _homeworkMissing;
[ObservableProperty] private HomeworkStatus? _homework;
[ObservableProperty] private AttendanceStatus? _attendance;
public string AttendanceLabel => AttendanceDisplay.ShortLabel(Attendance);
public string AttendanceTooltip => AttendanceDisplay.Label(Attendance);
public string HomeworkSymbol => HomeworkDisplay.Symbol(Homework);
public string HomeworkTooltip => HomeworkDisplay.Label(Homework);
public Action<Guid, string, int?>? OnRatingChanged { get; set; }
public Action<Guid, string, int?>? OnCompetencyRatingChanged { get; set; }
public Action<Guid, bool>? HomeworkChangedCallback { get; set; }
public Action<Guid, HomeworkStatus?>? HomeworkChangedCallback { get; set; }
public Action<Guid, AttendanceStatus?>? AttendanceChangedCallback { get; set; }
public ParticipationStudentRow(Guid id, string name, ParticipationEntry entry,
@@ -356,7 +370,7 @@ public partial class ParticipationStudentRow : ObservableObject
Name = name;
_entry = entry;
_aspectDefs = aspects;
_homeworkMissing = entry.HomeworkMissing;
_homework = HomeworkDisplay.Effective(entry);
_attendance = entry.Attendance;
foreach (var a in aspects)
@@ -389,8 +403,18 @@ public partial class ParticipationStudentRow : ObservableObject
[RelayCommand]
private void ToggleHomework()
{
HomeworkMissing = !HomeworkMissing;
HomeworkChangedCallback?.Invoke(StudentId, HomeworkMissing);
Homework = HomeworkDisplay.Next(Homework);
OnPropertyChanged(nameof(HomeworkSymbol));
OnPropertyChanged(nameof(HomeworkTooltip));
HomeworkChangedCallback?.Invoke(StudentId, Homework);
}
public void SetHomework(HomeworkStatus? value)
{
Homework = value;
OnPropertyChanged(nameof(HomeworkSymbol));
OnPropertyChanged(nameof(HomeworkTooltip));
HomeworkChangedCallback?.Invoke(StudentId, value);
}
[RelayCommand]
@@ -398,11 +422,14 @@ public partial class ParticipationStudentRow : ObservableObject
{
Attendance = Attendance switch
{
null => AttendanceStatus.ExcusePending,
AttendanceStatus.ExcusePending => AttendanceStatus.Excused,
AttendanceStatus.Excused => AttendanceStatus.Unexcused,
AttendanceStatus.Unexcused => null,
_ => null,
null => AttendanceStatus.Present,
AttendanceStatus.Present => AttendanceStatus.ExcusePending,
AttendanceStatus.ExcusePending => AttendanceStatus.Excused,
AttendanceStatus.Excused => AttendanceStatus.Unexcused,
AttendanceStatus.Unexcused => AttendanceStatus.Truant,
AttendanceStatus.Truant => AttendanceStatus.OtherSchoolEvent,
AttendanceStatus.OtherSchoolEvent => null,
_ => null,
};
OnPropertyChanged(nameof(AttendanceLabel));
OnPropertyChanged(nameof(AttendanceTooltip));
@@ -419,27 +446,109 @@ public partial class ParticipationStudentRow : ObservableObject
}
}
// ── Hausaufgaben-Anzeige ─────────────────────────────────────────────────────
public static class HomeworkDisplay
{
public static HomeworkStatus? Effective(ParticipationEntry entry) =>
entry.Homework ?? (entry.HomeworkMissing ? HomeworkStatus.MissingOpen : null);
public static HomeworkStatus? Next(HomeworkStatus? status) => status switch
{
null => HomeworkStatus.Completed,
HomeworkStatus.Completed => HomeworkStatus.PartiallyCompleted,
HomeworkStatus.PartiallyCompleted => HomeworkStatus.PartialSubmittedLate,
HomeworkStatus.PartialSubmittedLate => HomeworkStatus.PartialMissingOverdue,
HomeworkStatus.PartialMissingOverdue => HomeworkStatus.MissingOpen,
HomeworkStatus.MissingOpen => HomeworkStatus.MissingOverdue,
HomeworkStatus.MissingOverdue => HomeworkStatus.SubmittedLate,
HomeworkStatus.SubmittedLate => null,
_ => null,
};
public static string Label(HomeworkStatus? status) => status switch
{
null => "Keine Hausaufgabe erfasst",
HomeworkStatus.Completed => "Hausaufgabe gemacht",
HomeworkStatus.PartiallyCompleted => "Teilweise angefertigt Rest offen",
HomeworkStatus.PartialSubmittedLate => "Teilweise angefertigt Rest nachgereicht",
HomeworkStatus.PartialMissingOverdue => "Teilweise angefertigt Rest nicht nachgereicht",
HomeworkStatus.MissingOpen => "Nicht gemacht Nachreichen offen",
HomeworkStatus.MissingOverdue => "Nicht gemacht nicht mehr nachgereicht",
HomeworkStatus.SubmittedLate => "Hausaufgabe nachgereicht",
_ => "Keine Hausaufgabe erfasst",
};
public static string Symbol(HomeworkStatus? status) => status switch
{
null => "·",
HomeworkStatus.Completed => "✓",
HomeworkStatus.PartiallyCompleted => "◐",
HomeworkStatus.PartialSubmittedLate => "◕",
HomeworkStatus.PartialMissingOverdue => "◒",
HomeworkStatus.MissingOpen => "!",
HomeworkStatus.MissingOverdue => "✕",
HomeworkStatus.SubmittedLate => "↺",
_ => "·",
};
public static string Color(HomeworkStatus? status) => status switch
{
HomeworkStatus.Completed => "#2E9D57",
HomeworkStatus.PartiallyCompleted => "#D98200",
HomeworkStatus.PartialSubmittedLate => "#7F77DD",
HomeworkStatus.PartialMissingOverdue => "#D64545",
HomeworkStatus.MissingOpen => "#D98200",
HomeworkStatus.MissingOverdue => "#D64545",
HomeworkStatus.SubmittedLate => "#7F77DD",
_ => "",
};
public static bool CountsAsMissing(HomeworkStatus? status) => status is
HomeworkStatus.MissingOpen or
HomeworkStatus.MissingOverdue or
HomeworkStatus.PartiallyCompleted or
HomeworkStatus.PartialMissingOverdue;
}
// ── Anwesenheits-Anzeige ──────────────────────────────────────────────────────
public static class AttendanceDisplay
{
public static string Label(AttendanceStatus? s) => s switch
{
null => "Anwesend",
null => "Noch nicht kontrolliert",
AttendanceStatus.Present => "Anwesend",
AttendanceStatus.ExcusePending => "Krank (Entschuldigung offen)",
AttendanceStatus.Excused => "Krank, entschuldigt",
AttendanceStatus.Unexcused => "Krank, unentschuldigt",
AttendanceStatus.Truant => "Geschwänzt",
AttendanceStatus.OtherSchoolEvent => "Andere Schulveranstaltung",
_ => "Anwesend",
};
public static string ShortLabel(AttendanceStatus? s) => s switch
{
null => "",
AttendanceStatus.ExcusePending => "K ?",
AttendanceStatus.Excused => "K ✓",
AttendanceStatus.Unexcused => "K ✗",
AttendanceStatus.Present => "",
AttendanceStatus.ExcusePending => "?",
AttendanceStatus.Excused => "",
AttendanceStatus.Unexcused => "!",
AttendanceStatus.Truant => "✕",
AttendanceStatus.OtherSchoolEvent => "◇",
_ => "",
};
public static string Color(AttendanceStatus? s) => s switch
{
AttendanceStatus.Present => "#2E9D57",
AttendanceStatus.ExcusePending => "#D98200",
AttendanceStatus.Excused => "#4C86A8",
AttendanceStatus.Unexcused => "#D96C00",
AttendanceStatus.Truant => "#D64545",
AttendanceStatus.OtherSchoolEvent => "#5277C3",
_ => "",
};
}
// ── Eine Bewertungszelle ──────────────────────────────────────────────────────
@@ -731,3 +840,48 @@ public partial class QuickAspectRow : ObservableObject
2 => "++", 1 => "+", 0 => "~", -1 => "", -2 => "−−", _ => "·",
};
}
// ── Schnelleingabe Anwesenheit / Hausaufgaben ────────────────────────────────
public partial class AttendanceHomeworkQuickInputViewModel : ObservableObject
{
public List<ParticipationStudentRow> Rows { get; }
public string SessionLabel { get; }
[ObservableProperty] private ParticipationStudentRow? _selectedRow;
[ObservableProperty] private bool _advanceAfterInput = true;
public AttendanceHomeworkQuickInputViewModel(
IEnumerable<ParticipationStudentRow> rows, string sessionLabel)
{
Rows = rows.ToList();
SessionLabel = sessionLabel;
SelectedRow = Rows.FirstOrDefault();
}
public void ApplyAttendance(ParticipationStudentRow row, AttendanceStatus? status)
{
SelectedRow = row;
row.SetAttendance(status);
AdvanceIfRequested();
}
public void ApplyHomework(ParticipationStudentRow row, HomeworkStatus? status)
{
SelectedRow = row;
row.SetHomework(status);
AdvanceIfRequested();
}
public void MoveSelection(int delta)
{
if (Rows.Count == 0) return;
var current = SelectedRow is null ? 0 : Rows.IndexOf(SelectedRow);
SelectedRow = Rows[Math.Clamp(current + delta, 0, Rows.Count - 1)];
}
private void AdvanceIfRequested()
{
if (AdvanceAfterInput) MoveSelection(1);
}
}