CI / build-and-test (push) Canceled after 0s
Dashboard warnings (attendance, and now homework backlog and lateness) apply across all classes, but the existing Vorgänge case-file tab only works for the teacher's own class since it resolves the WebUntis classbook roster. Instead of loosening that coupling, students get their own narrower Vorgänge tab, and all three cross-class warnings now jump straight there, offering to open a matching case or create a new one. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
117 lines
6.4 KiB
C#
117 lines
6.4 KiB
C#
using CommunityToolkit.Mvvm.ComponentModel;
|
||
using System.Windows.Input;
|
||
|
||
namespace LehrerApp.Desktop.ViewModels;
|
||
|
||
/// <summary>Die neun Dashboard-Kacheln, die tatsächlich "wo muss ich reagieren" beantworten
|
||
/// (siehe <see cref="DashboardViewModel.AttentionCount"/>), zusammengefasst zu einer Karte mit
|
||
/// Filter-Chips statt eigenen Sichtbarkeits-Schaltern je Art. Reihenfolge hier ist die Reihenfolge
|
||
/// der Gruppen in der zusammengefassten Ansicht (siehe DashboardViewModel.RebuildAttention) — sie
|
||
/// übernimmt bewusst die frühere Sortierung aus DashboardSettingsService.DefaultCardOrder, damit
|
||
/// sich nichts, was Nutzer bereits kennen, unvorhersehbar umsortiert.
|
||
/// "Klausurwochen" (examload) bleibt bewusst außen vor: die Einträge dort sind Wochen-Aggregate,
|
||
/// keine Einzelvorgänge, die sich abhaken oder anklicken lassen — sie passen nicht ins Item-Modell.
|
||
/// MissingHomework/Lateness (Hausaufgaben-/Verspätungs-Warnung) ergänzen Attendance um dieselbe
|
||
/// klassenübergreifende Logik — alle drei navigieren über OnNavigateToStudentCase direkt auf den
|
||
/// "Vorgänge"-Tab des Schülerdetails statt auf dessen Standard-Tab (siehe DashboardViewModel).</summary>
|
||
public enum AttentionKind
|
||
{
|
||
MissingTeachingTime, Excuse, Correction, Unplanned, Alert, Attendance, MissingHomework, Lateness, SupportPlan,
|
||
}
|
||
|
||
/// <summary>Eine Inline-Aktion auf einem Handlungsbedarf-Eintrag (z.B. "Entschuldigt"/"Unentschuldigt",
|
||
/// "Erfassen"). Verwendet ICommand statt Action, damit die bereits vorhandenen generierten
|
||
/// RelayCommands (OpenExcuseItem.MarkExcusedCommand, DashboardViewModel.AddMissingTeachingTimeCommand)
|
||
/// direkt weiterverwendet werden können, statt sie in einen zweiten Delegate-Typ zu verpacken.</summary>
|
||
public sealed record AttentionAction(string Label, ICommand Command, object? Parameter = null);
|
||
|
||
/// <summary>Gemeinsame Projektion für die sieben Handlungsbedarf-Arten. Trägt nur Anzeigedaten —
|
||
/// die eigentliche Beschaffung bleibt unverändert in den jeweiligen DashboardViewModel.LoadXxx-
|
||
/// Methoden; sie erzeugen am Ende ein AttentionItem statt (wie vorher) ihre eigene Item-Klasse in
|
||
/// ihre eigene ObservableCollection einzutragen.</summary>
|
||
public sealed class AttentionItem
|
||
{
|
||
public AttentionKind Kind { get; }
|
||
public string Title { get; }
|
||
public string Subtitle { get; }
|
||
/// <summary>Kurztext rechts neben dem Titel — Fehlzeitenquote, Alert-Kind-Label. Meist leer.</summary>
|
||
public string TrailingText { get; }
|
||
public string DateDisplay { get; }
|
||
public bool IsOverdue { get; }
|
||
public AlertSeverity? Severity { get; }
|
||
/// <summary>Nur bei Korrekturen gesetzt (0–100); steuert die ProgressBar. Eigenes HasProgress statt
|
||
/// Nullability, damit die ProgressBar-Bindung ein einfaches int (kompatibel mit Value:double) bleibt.</summary>
|
||
public int ProgressPercent { get; }
|
||
public bool HasProgress { get; }
|
||
/// <summary>Text unter der ProgressBar, z.B. "3 von 5 Arbeiten bewertet". Nur bei Korrekturen gesetzt.</summary>
|
||
public string ProgressLabel { get; }
|
||
/// <summary>Färbt TrailingText wie die frühere Fehlzeiten-Warnung (fest "Foreground=Red") ein —
|
||
/// bei Attendance/MissingHomework/Lateness gesetzt, weil ein AttentionItem dieser Arten per
|
||
/// Konstruktion nur entsteht, wenn der jeweilige Schwellenwert bereits überschritten ist.</summary>
|
||
public bool IsWarningTrailing { get; }
|
||
public IReadOnlyList<AttentionAction> Actions { get; }
|
||
public Action? Navigate { get; }
|
||
|
||
public AttentionItem(AttentionKind kind, string title, string subtitle = "", string trailingText = "",
|
||
string dateDisplay = "", bool isOverdue = false, AlertSeverity? severity = null,
|
||
int progressPercent = 0, string progressLabel = "", bool hasProgress = false,
|
||
bool isWarningTrailing = false, IReadOnlyList<AttentionAction>? actions = null, Action? navigate = null)
|
||
{
|
||
Kind = kind;
|
||
Title = title;
|
||
Subtitle = subtitle;
|
||
TrailingText = trailingText;
|
||
DateDisplay = dateDisplay;
|
||
IsOverdue = isOverdue;
|
||
Severity = severity;
|
||
ProgressPercent = progressPercent;
|
||
ProgressLabel = progressLabel;
|
||
HasProgress = hasProgress;
|
||
IsWarningTrailing = isWarningTrailing;
|
||
Actions = actions ?? [];
|
||
Navigate = navigate;
|
||
}
|
||
|
||
public bool HasSubtitle => !string.IsNullOrWhiteSpace(Subtitle);
|
||
public bool HasTrailingText => !string.IsNullOrWhiteSpace(TrailingText);
|
||
public bool HasDate => !string.IsNullOrWhiteSpace(DateDisplay);
|
||
public bool HasActions => Actions.Count > 0;
|
||
public bool HasSeverity => Severity is not null;
|
||
public bool IsHighSeverity => Severity == AlertSeverity.High;
|
||
public bool IsMediumSeverity => Severity == AlertSeverity.Medium;
|
||
/// Färbt den linken Severity-Balken wie die frühere Fehlzeiten-Warnung ein — für alle drei
|
||
/// klassenübergreifenden Warnarten (Fehlzeiten/Hausaufgaben/Verspätungen), die per Konstruktion
|
||
/// nur entstehen, wenn ihr jeweiliger Schwellenwert bereits überschritten ist.
|
||
public bool IsCrossClassWarning => Kind is AttentionKind.Attendance or AttentionKind.MissingHomework or AttentionKind.Lateness;
|
||
}
|
||
|
||
/// <summary>Eine Kind-Gruppe innerhalb der zusammengefassten Handlungsbedarf-Liste. Items behalten
|
||
/// die Sortierung, die ihre jeweilige LoadXxx-Methode schon immer produziert hat (z.B. Fehlzeiten
|
||
/// nach Quote absteigend, Korrekturen nach Datum) — ein zweites, generisches "Importance"-Sortieren
|
||
/// quer über alle Arten würde diese bewusst gewählten Reihenfolgen wieder zerstören.</summary>
|
||
public sealed class AttentionGroup(AttentionKind kind, string header, IReadOnlyList<AttentionItem> items)
|
||
{
|
||
public AttentionKind Kind { get; } = kind;
|
||
public string Header { get; } = header;
|
||
public IReadOnlyList<AttentionItem> Items { get; } = items;
|
||
public int Count => Items.Count;
|
||
}
|
||
|
||
/// <summary>Ein Filter-Chip für eine Handlungsbedarf-Art in der zusammengefassten Karte — ersetzt
|
||
/// die frühere Sichtbarkeit je Einzelkachel (siehe DashboardViewModel.LoadAttentionFilters).</summary>
|
||
public partial class AttentionFilterOption : ObservableObject
|
||
{
|
||
[ObservableProperty] private bool _isActive = true;
|
||
public AttentionKind Kind { get; }
|
||
public string Label { get; }
|
||
public Action? OnChanged { get; set; }
|
||
|
||
public AttentionFilterOption(AttentionKind kind, string label)
|
||
{
|
||
Kind = kind;
|
||
Label = label;
|
||
}
|
||
|
||
partial void OnIsActiveChanged(bool value) => OnChanged?.Invoke();
|
||
}
|