Schülerdokumentation: Einträge, Fehlzeitenbilanz, Datenschutz (Kapitel 5)
Dokumentationseinträge mit Typwahl (Gespräch, Vorkommnis, Förderplan, Fehlzeit, Elternanruf, Elternbrief), vertrauliche Einträge nur nach Bestätigung sichtbar, weiche Löschung mit Nachvollziehbarkeit. Fehlzeiten als Auswertung des bestehenden Anwesenheits-Trackings statt zweiter Erfassung, mit Schwellenwert-Warnung im Schülerdetail und Dashboard. Förderplan-Wiedervorlage als Dashboard-Karte. Datenschutz: Löschfristen mit manueller Bereinigung und DSGVO-Art.-15-Datenauskunft als Export. Auf Nutzer-Feedback hin ergänzt: Elternanruf mit begleitendem Gesprächsprotokoll-Dialog (Punkte abhaken, Eindrücke festhalten), Elternbrief mit Versand-/Rückmeldungs-Tracking, Datei-Anhänge über LiteDBs Dateispeicher, frei vergebbare Labels zur Nachverfolgung mit Dringlichkeits-Farbcodierung, sowie eine sichtbare Farblegende für das bestehende Notenentwicklungs-Diagramm. Dabei einen Absturz behoben: leere Textfelder lieferten über das Binding null statt "", was beim Speichern eine NullReferenceException auslöste. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,9 +20,12 @@ public partial class DashboardViewModel : ObservableObject
|
||||
private readonly IParticipationSessionRepository _participationSessions;
|
||||
private readonly IParticipationRepository _participationEntries;
|
||||
private readonly IStudentRepository _students;
|
||||
private readonly IDocumentationRepository _documentation;
|
||||
private readonly AttendanceBalanceService _attendanceBalance;
|
||||
private readonly SchoolYearService _sy;
|
||||
|
||||
private const int OpenExcuseMaxAgeDays = 21;
|
||||
private const int SupportPlanDueWithinDays = 14;
|
||||
|
||||
[ObservableProperty] private string _greeting = "";
|
||||
[ObservableProperty] private string _currentDate = "";
|
||||
@@ -36,18 +39,22 @@ public partial class DashboardViewModel : ObservableObject
|
||||
public ObservableCollection<GroupChip> CurrentGroups { get; } = [];
|
||||
public ObservableCollection<CalendarDayCell> CalendarDays { get; } = [];
|
||||
public ObservableCollection<OpenExcuseItem> OpenExcuses { get; } = [];
|
||||
public ObservableCollection<AttendanceWarningItem> AttendanceWarnings { get; } = [];
|
||||
public ObservableCollection<SupportPlanDueItem> SupportPlanReviews { get; } = [];
|
||||
public static string[] CalendarWeekdayHeaders { get; } = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"];
|
||||
|
||||
// Navigation-Callback – wird von App.axaml.cs verdrahtet
|
||||
public Action<Guid>? OnNavigateToGroup { get; set; }
|
||||
public Action<Guid>? OnNavigateToStudent { get; set; }
|
||||
|
||||
public DashboardViewModel(IGroupRepository groups, ISubjectRepository subjects, ILessonRepository lessons,
|
||||
IExamRepository exams, IWorkTaskRepository tasks, IParticipationSessionRepository participationSessions,
|
||||
IParticipationRepository participationEntries, IStudentRepository students, SchoolYearService sy)
|
||||
IParticipationRepository participationEntries, IStudentRepository students,
|
||||
IDocumentationRepository documentation, AttendanceBalanceService attendanceBalance, SchoolYearService sy)
|
||||
{
|
||||
_groups = groups; _subjects = subjects; _lessons = lessons; _exams = exams; _tasks = tasks;
|
||||
_participationSessions = participationSessions; _participationEntries = participationEntries;
|
||||
_students = students; _sy = sy;
|
||||
_students = students; _documentation = documentation; _attendanceBalance = attendanceBalance; _sy = sy;
|
||||
Load();
|
||||
}
|
||||
|
||||
@@ -90,8 +97,64 @@ public partial class DashboardViewModel : ObservableObject
|
||||
CalendarMonth = FirstOfMonth(now);
|
||||
LoadCalendar();
|
||||
LoadOpenExcuses(groups.Values.ToList(), today);
|
||||
LoadAttendanceWarnings(today);
|
||||
LoadSupportPlanReviews(today);
|
||||
}
|
||||
|
||||
// ── Fehlzeiten-Warnung (5.2.3) ────────────────────────────────────────────
|
||||
|
||||
private void LoadAttendanceWarnings(DateOnly today)
|
||||
{
|
||||
AttendanceWarnings.Clear();
|
||||
var schoolYear = _sy.CurrentSchoolYear();
|
||||
var from = _sy.SchoolYearStart(schoolYear);
|
||||
var to = _sy.SchoolYearEnd(schoolYear);
|
||||
|
||||
var items = new List<AttendanceWarningItem>();
|
||||
foreach (var student in _students.GetAll())
|
||||
{
|
||||
var entries = _participationEntries.GetByStudent(student.Id)
|
||||
.Select(e => _participationSessions.GetById(e.SessionId) is { } session
|
||||
? ((DateOnly?)session.Date, e.Attendance) : (null, e.Attendance))
|
||||
.Where(t => t.Item1.HasValue)
|
||||
.Select(t => (t.Item1!.Value, t.Attendance));
|
||||
|
||||
var balance = _attendanceBalance.Calculate(entries, from, to);
|
||||
if (balance.ExceedsThreshold)
|
||||
items.Add(new AttendanceWarningItem(student.Id, student.FullName, balance.AbsenceRatePercent));
|
||||
}
|
||||
foreach (var item in items.OrderByDescending(i => i.AbsenceRatePercent))
|
||||
AttendanceWarnings.Add(item);
|
||||
}
|
||||
|
||||
// ── Förderplan-Wiedervorlage (5.3.2) ──────────────────────────────────────
|
||||
|
||||
private void LoadSupportPlanReviews(DateOnly today)
|
||||
{
|
||||
SupportPlanReviews.Clear();
|
||||
var dueBy = today.AddDays(SupportPlanDueWithinDays);
|
||||
|
||||
var due = _documentation.GetAll()
|
||||
.Where(d => d.Type == DocumentationType.SupportPlan
|
||||
&& d.SupportData is { Status: SupportStatus.Active, ReviewDate: not null }
|
||||
&& d.SupportData.ReviewDate!.Value <= dueBy)
|
||||
.OrderBy(d => d.SupportData!.ReviewDate);
|
||||
|
||||
foreach (var d in due)
|
||||
{
|
||||
var student = _students.GetById(d.StudentId);
|
||||
if (student is null) continue;
|
||||
SupportPlanReviews.Add(new SupportPlanDueItem(
|
||||
d.StudentId, student.FullName, d.Title, d.SupportData!.ReviewDate!.Value, today));
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand] private void OpenStudentAttendance(AttendanceWarningItem? item)
|
||||
{ if (item is not null) OnNavigateToStudent?.Invoke(item.StudentId); }
|
||||
|
||||
[RelayCommand] private void OpenStudentSupportPlan(SupportPlanDueItem? item)
|
||||
{ if (item is not null) OnNavigateToStudent?.Invoke(item.StudentId); }
|
||||
|
||||
private void LoadOpenExcuses(List<LearningGroup> groups, DateOnly today)
|
||||
{
|
||||
OpenExcuses.Clear();
|
||||
@@ -243,6 +306,35 @@ public partial class OpenExcuseItem : ObservableObject
|
||||
[RelayCommand] private void MarkUnexcused() => OnResolve?.Invoke(this, AttendanceStatus.Unexcused);
|
||||
}
|
||||
|
||||
// ── Fehlzeiten-Warnung (5.2.3) ────────────────────────────────────────────────
|
||||
|
||||
public class AttendanceWarningItem(Guid studentId, string studentName, double absenceRatePercent)
|
||||
{
|
||||
public Guid StudentId { get; } = studentId;
|
||||
public string StudentName { get; } = studentName;
|
||||
public double AbsenceRatePercent { get; } = absenceRatePercent;
|
||||
}
|
||||
|
||||
// ── Förderplan-Wiedervorlage (5.3.2) ──────────────────────────────────────────
|
||||
|
||||
public class SupportPlanDueItem
|
||||
{
|
||||
public Guid StudentId { get; }
|
||||
public string StudentName { get; }
|
||||
public string Title { get; }
|
||||
public string ReviewDateDisplay { get; }
|
||||
public bool IsOverdue { get; }
|
||||
|
||||
public SupportPlanDueItem(Guid studentId, string studentName, string title, DateOnly reviewDate, DateOnly today)
|
||||
{
|
||||
StudentId = studentId;
|
||||
StudentName = studentName;
|
||||
Title = title;
|
||||
ReviewDateDisplay = reviewDate.ToString("dd.MM.yyyy");
|
||||
IsOverdue = reviewDate < today;
|
||||
}
|
||||
}
|
||||
|
||||
public class CalendarDayCell
|
||||
{
|
||||
public int DayNumber { get; }
|
||||
|
||||
Reference in New Issue
Block a user