Files
LehrerApp/LehrerApp.Desktop/ViewModels/AttentionItem.cs
T
admin 6af4bee1f0 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).
2026-09-11 00:40:58 +02:00

107 lines
5.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 (0100); 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();
}