refactor: Dashboard-Handlungsbedarf zu einer Karte zusammenfassen
Fasst die sieben Kacheln, die tatsaechlich "wo muss ich reagieren" beantworten (Entschuldigungen, Fehlzeiten-Warnung, Foerderplan-Wiedervorlage, Korrekturen, ungeplante Stunden, Auffaelligkeiten, Unterrichtszeit-Nacherfassung), zu einer Karte "Handlungsbedarf" mit Filter-Chips zusammen statt sieben eigener Sichtbarkeits-Schalter. Neues gemeinsames Modell AttentionItem/AttentionGroup/AttentionAction traegt nur Anzeigedaten - die Datenbeschaffung bleibt unveraendert in den jeweiligen DashboardViewModel.LoadXxx-Methoden, die am Ende ein AttentionItem statt ihrer eigenen Item-Klasse erzeugen. RebuildAttention() gruppiert nach Art (feste Reihenfolge) und wendet Filter an, ohne die Repos erneut abzufragen. SupportPlanDueItem, CorrectionProgressItem, UnplannedLessonItem und DashboardAlertItem entfallen (nur dashboard-intern verwendet); OpenExcuseItem und AttendanceWarningItem bleiben bestehen, da GroupOverviewViewModel sie weiterhin nutzt. Fuenf Navigations-Commands wurden durch ein einziges OpenAttentionItemCommand ersetzt. Bewusste Verhaltensaenderung: LoadAlerts dupliziert Fehlzeiten-Ueberschreitungen nicht mehr in die Auffaelligkeiten-Gruppe, da dieselbe Zahl sonst zweimal in derselben Karte erschiene (vorher durch zwei getrennte Kacheln nicht sichtbar). Details und Testanpassungen siehe TODO.md, Abschnitt 9 (Dashboard).
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace LehrerApp.Desktop.ViewModels;
|
||||
|
||||
/// <summary>Die sieben Dashboard-Kacheln, die tatsächlich "wo muss ich reagieren" beantworten
|
||||
/// (siehe <see cref="DashboardViewModel.AttentionCount"/>), zusammengefasst zu einer Karte mit
|
||||
/// Filter-Chips statt sieben eigenen Sichtbarkeits-Schaltern. 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.</summary>
|
||||
public enum AttentionKind { MissingTeachingTime, Excuse, Correction, Unplanned, Alert, Attendance, 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 —
|
||||
/// nur bei Attendance gesetzt, weil ein AttentionItem dieser Art per Konstruktion nur entsteht,
|
||||
/// wenn die Fehlzeitenquote den Schwellenwert bereits überschreitet.</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;
|
||||
}
|
||||
|
||||
/// <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();
|
||||
}
|
||||
Reference in New Issue
Block a user