From 741586846adc2ade192a76a3bea6a6762eedeb69 Mon Sep 17 00:00:00 2001 From: Sebastian Hedtrich Date: Thu, 13 Aug 2026 01:49:40 +0200 Subject: [PATCH] =?UTF-8?q?Mitarbeits=20Wizzard=20und=20neue=20Statuslabel?= =?UTF-8?q?s=20f=C3=BCr=20Anwesenheit=20und=20Hausaufgaben?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LehrerApp.Core/Models/Participation.cs | 39 +- .../Groups/ParticipationViewModels.cs | 186 ++++++- .../Groups/ParticipationWizardViewModels.cs | 97 +++- .../AttendanceHomeworkQuickInputDialog.axaml | 41 ++ ...ttendanceHomeworkQuickInputDialog.axaml.cs | 225 +++++++++ .../Views/Groups/ParticipationTabView.axaml | 18 +- .../Groups/ParticipationTabView.axaml.cs | 74 ++- .../Groups/ParticipationTimelineChart.axaml | 6 + .../ParticipationTimelineChart.axaml.cs | 455 ++++++++++++++++++ .../Groups/ParticipationWizardDialog.axaml | 163 +++---- 10 files changed, 1160 insertions(+), 144 deletions(-) create mode 100644 LehrerApp.Desktop/Views/Groups/AttendanceHomeworkQuickInputDialog.axaml create mode 100644 LehrerApp.Desktop/Views/Groups/AttendanceHomeworkQuickInputDialog.axaml.cs create mode 100644 LehrerApp.Desktop/Views/Groups/ParticipationTimelineChart.axaml create mode 100644 LehrerApp.Desktop/Views/Groups/ParticipationTimelineChart.axaml.cs diff --git a/LehrerApp.Core/Models/Participation.cs b/LehrerApp.Core/Models/Participation.cs index 9a8363c..765ed94 100644 --- a/LehrerApp.Core/Models/Participation.cs +++ b/LehrerApp.Core/Models/Participation.cs @@ -20,18 +20,47 @@ public class ParticipationEntry public List Ratings { get; set; } = []; public List CompetencyRatings { get; set; } = []; public string? Note { get; set; } + public HomeworkStatus? Homework { get; set; } + // Bleibt für bereits gespeicherte Daten erhalten. Neue Schreibvorgänge setzen beide Felder. public bool HomeworkMissing { get; set; } public AttendanceStatus? Attendance { get; set; } public DateTime UpdatedAt { get; set; } = DateTime.UtcNow; } /// -/// Anwesenheitsstatus einer Sitzung; null (Standard) bedeutet anwesend. -/// ist ein bewusster Zwischenzustand, da die Entschuldigung meist -/// erst später eintrifft — er wird beim Erfassen des Fehltags gesetzt und danach manuell auf -/// oder nachgetragen. +/// Hausaufgabenstatus einer Sitzung; null bedeutet, dass für diesen Termin keine +/// Hausaufgabe erfasst wurde. MissingOpen kann später in MissingOverdue oder SubmittedLate +/// überführt werden. /// -public enum AttendanceStatus { ExcusePending, Excused, Unexcused } +public enum HomeworkStatus +{ + Completed, + MissingOpen, + MissingOverdue, + SubmittedLate, + // Hinten angefügt, damit bestehende LiteDB-Werte numerisch stabil bleiben. + PartiallyCompleted, + PartialSubmittedLate, + PartialMissingOverdue, +} + +/// +/// Anwesenheitsstatus einer Sitzung; null bedeutet, dass die Anwesenheit für diesen Termin +/// noch nicht kontrolliert wurde. bestätigt die Anwesenheit ausdrücklich. +/// ist ein bewusster Zwischenzustand, da die Entschuldigung meist +/// erst später eintrifft. bezeichnet gesichertes Schwänzen; +/// eine schulisch veranlasste Abwesenheit. +/// +public enum AttendanceStatus +{ + ExcusePending, + Excused, + Unexcused, + Truant, + OtherSchoolEvent, + // Hinten angefügt, damit die numerischen Werte bereits gespeicherter LiteDB-Einträge stabil bleiben. + Present, +} /// /// Ein Bewertungsabschnitt einer Lerngruppe (z.B. alle 4–7 Wochen), an dessen Ende eine diff --git a/LehrerApp.Desktop/ViewModels/Groups/ParticipationViewModels.cs b/LehrerApp.Desktop/ViewModels/Groups/ParticipationViewModels.cs index 29167d4..5777db6 100644 --- a/LehrerApp.Desktop/ViewModels/Groups/ParticipationViewModels.cs +++ b/LehrerApp.Desktop/ViewModels/Groups/ParticipationViewModels.cs @@ -47,6 +47,7 @@ public partial class ParticipationTabViewModel : ObservableObject public Func>? OnAddSession { get; set; } public Func? OnQuickInput { get; set; } + public Func? OnStatusQuickInput { get; set; } public Func? OnComputeGrade { get; set; } public Func? 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 Cells { get; } = []; public ObservableCollection 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? OnRatingChanged { get; set; } public Action? OnCompetencyRatingChanged { get; set; } - public Action? HomeworkChangedCallback { get; set; } + public Action? HomeworkChangedCallback { get; set; } public Action? 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 Rows { get; } + public string SessionLabel { get; } + + [ObservableProperty] private ParticipationStudentRow? _selectedRow; + [ObservableProperty] private bool _advanceAfterInput = true; + + public AttendanceHomeworkQuickInputViewModel( + IEnumerable 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); + } +} diff --git a/LehrerApp.Desktop/ViewModels/Groups/ParticipationWizardViewModels.cs b/LehrerApp.Desktop/ViewModels/Groups/ParticipationWizardViewModels.cs index 3840546..e439c99 100644 --- a/LehrerApp.Desktop/ViewModels/Groups/ParticipationWizardViewModels.cs +++ b/LehrerApp.Desktop/ViewModels/Groups/ParticipationWizardViewModels.cs @@ -40,6 +40,15 @@ public partial class ParticipationWizardDialogViewModel : ObservableObject [ObservableProperty] private string _sectionValidationMessage = ""; [ObservableProperty] private ParticipationPeriodOption _rollupPeriod; [ObservableProperty] private string _rollupStatusMessage = ""; + [ObservableProperty] private double _timelineZoom = 1.0; + [ObservableProperty] private bool _showQuality = true; + [ObservableProperty] private bool _showQuantity = true; + [ObservableProperty] private bool _showWorkphase = true; + [ObservableProperty] private bool _showDataPoints = true; + [ObservableProperty] private bool _showWeightedTrend = true; + [ObservableProperty] private bool _showQualityTrend; + [ObservableProperty] private bool _showQuantityTrend; + [ObservableProperty] private bool _showWorkphaseTrend; public List RollupPeriodOptions { get; } = [ @@ -68,8 +77,10 @@ public partial class ParticipationWizardDialogViewModel : ObservableObject _groupId = groupId; _schoolYear = schoolYear; _gradingSystem = gradingSystem; GroupLabel = groupLabel; - _aspectWeights = aspects.GetDefaults().Concat(aspects.GetByGroup(groupId)) - .ToDictionary(a => a.Key, a => a.Weight); + _aspectWeights = aspects.GetDefaults() + .Concat(aspects.GetByGroup(groupId)) + .GroupBy(a => a.Key) + .ToDictionary(g => g.Key, g => g.Last().Weight); _students = students.GetByGroup(groupId).OrderBy(s => s.LastName).ThenBy(s => s.FirstName).ToList(); _allSessions = sessions.GetByGroup(groupId).OrderBy(s => s.Date).ToList(); _sectionList = sectionRepo.GetByGroup(groupId).OrderBy(s => s.StartDate).ToList(); @@ -120,6 +131,11 @@ public partial class ParticipationWizardDialogViewModel : ObservableObject if (result is null) continue; points.Add((exam.Date, BuildExamPoint(exam, result))); } + foreach (var grade in _grades.GetByStudentAndGroup(studentId, _groupId) + .Where(g => g.Category != GradeCategory.Participation)) + { + points.Add((grade.Date, BuildOtherGradePoint(grade))); + } points = points.OrderBy(p => p.Date).ToList(); Timeline.Clear(); @@ -140,7 +156,8 @@ public partial class ParticipationWizardDialogViewModel : ObservableObject private WizardTimelinePoint BuildSessionPoint(ParticipationSession session, ParticipationEntry? entry, Guid studentId) { - var ratingLabel = entry is not null ? WeightedRatingLabel(entry) : ""; + var weightedValue = entry is not null ? WeightedRating(entry) : null; + var ratingLabel = weightedValue is not null ? RatingLabel((int)Math.Round(weightedValue.Value, MidpointRounding.AwayFromZero)) : ""; var note = entry?.Note; var tooltip = $"{session.Date:dd.MM.yyyy}" + (ratingLabel.Length > 0 ? $" · {ratingLabel}" : "") + @@ -149,9 +166,14 @@ public partial class ParticipationWizardDialogViewModel : ObservableObject var point = new WizardTimelinePoint(session.Date, isExam: false, ratingLabel, examLabel: "", hasNote: !string.IsNullOrWhiteSpace(note), tooltip: tooltip) { - HasHomework = entry?.HomeworkMissing ?? false, + Homework = entry is null ? null : HomeworkDisplay.Effective(entry), AttendanceIcon = AttendanceDisplay.ShortLabel(entry?.Attendance), AttendanceTooltip = AttendanceDisplay.Label(entry?.Attendance), + AttendanceColor = AttendanceDisplay.Color(entry?.Attendance), + AspectValues = entry?.Ratings.ToDictionary(r => r.Key, r => r.Value) + ?? new Dictionary(), + WeightedValue = weightedValue, + OverallGrade = weightedValue is null ? "" : _grading.ParticipationGrade(weightedValue.Value, _gradingSystem), }; point.ToggleHomeworkCommand = new RelayCommand(() => ToggleHomeworkAt(session.Id, studentId, point)); point.CycleAttendanceCommand = new RelayCommand(() => CycleAttendanceAt(session.Id, studentId, point)); @@ -165,9 +187,24 @@ public partial class ParticipationWizardDialogViewModel : ObservableObject hasNote: false, tooltip: $"{exam.Date:dd.MM.yyyy} · Klausur {examLabel}"); } - private string WeightedRatingLabel(ParticipationEntry entry) + private static WizardTimelinePoint BuildOtherGradePoint(Grade grade) { - if (entry.Ratings.Count == 0) return ""; + var category = grade.Category switch + { + GradeCategory.Oral => "Mündlich", + GradeCategory.Homework => "Hausaufgabe", + GradeCategory.Project => "Projekt", + _ => "Sonstige Leistung", + }; + var detail = string.IsNullOrWhiteSpace(grade.Note) ? category : grade.Note.Trim(); + var label = $"{detail}: {grade.Value}"; + return new WizardTimelinePoint(grade.Date, isExam: true, ratingLabel: "", examLabel: label, + hasNote: false, tooltip: $"{grade.Date:dd.MM.yyyy} · {category} · {label}"); + } + + private double? WeightedRating(ParticipationEntry entry) + { + if (entry.Ratings.Count == 0) return null; var weightSum = 0.0; var valueSum = 0.0; foreach (var r in entry.Ratings) { @@ -175,8 +212,7 @@ public partial class ParticipationWizardDialogViewModel : ObservableObject if (w <= 0) continue; valueSum += r.Value * w; weightSum += w; } - if (weightSum <= 0) return ""; - return RatingLabel((int)Math.Round(valueSum / weightSum, MidpointRounding.AwayFromZero)); + return weightSum <= 0 ? null : valueSum / weightSum; } private static string RatingLabel(int v) => v switch @@ -188,9 +224,10 @@ public partial class ParticipationWizardDialogViewModel : ObservableObject { var entry = _entries.GetBySessionAndStudent(sessionId, studentId) ?? new ParticipationEntry { SessionId = sessionId, StudentId = studentId }; - entry.HomeworkMissing = !entry.HomeworkMissing; + entry.Homework = HomeworkDisplay.Next(HomeworkDisplay.Effective(entry)); + entry.HomeworkMissing = HomeworkDisplay.CountsAsMissing(entry.Homework); _entries.Save(entry); - point.HasHomework = entry.HomeworkMissing; + point.Homework = entry.Homework; } private void CycleAttendanceAt(Guid sessionId, Guid studentId, WizardTimelinePoint point) @@ -199,15 +236,21 @@ public partial class ParticipationWizardDialogViewModel : ObservableObject ?? new ParticipationEntry { SessionId = sessionId, StudentId = studentId }; entry.Attendance = entry.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, }; _entries.Save(entry); - point.AttendanceIcon = AttendanceDisplay.ShortLabel(entry.Attendance); + // Das Setzen jeder ObservableProperty kann unmittelbar ein Neuzeichnen auslösen. + // Deshalb muss die zum aktiven Symbol gehörende Farbe zuerst bereitstehen. + point.AttendanceColor = AttendanceDisplay.Color(entry.Attendance); point.AttendanceTooltip = AttendanceDisplay.Label(entry.Attendance); + point.AttendanceIcon = AttendanceDisplay.ShortLabel(entry.Attendance); } // ── Abschnitte ──────────────────────────────────────────────────────────── @@ -364,19 +407,36 @@ public class WizardSectionGroup(string bandLabel, bool isOpen) public partial class WizardTimelinePoint : ObservableObject { + public DateOnly Date { get; } public string DateDisplay { get; } public bool IsExam { get; } public string RatingLabel { get; } public string ExamLabel { get; } public bool HasNote { get; } public string TooltipText { get; } + public IReadOnlyDictionary AspectValues { get; init; } = new Dictionary(); + public double? WeightedValue { get; init; } + public string OverallGrade { get; init; } = ""; - [ObservableProperty] private bool _hasHomework; + [ObservableProperty] private HomeworkStatus? _homework; [ObservableProperty] private string _attendanceIcon = ""; [ObservableProperty] private string _attendanceTooltip = "Anwesend"; + [ObservableProperty] private string _attendanceColor = ""; public bool IsAbsent => AttendanceIcon.Length > 0; - public string AttendanceButtonLabel => AttendanceIcon.Length > 0 ? AttendanceIcon : "Anw"; + public string AttendanceButtonLabel => AttendanceIcon.Length > 0 ? AttendanceIcon : "·"; + public bool HasHomeworkStatus => Homework is not null; + public string HomeworkSymbol => HomeworkDisplay.Symbol(Homework); + public string HomeworkTooltip => HomeworkDisplay.Label(Homework); + public string HomeworkColor => HomeworkDisplay.Color(Homework); + + partial void OnHomeworkChanged(HomeworkStatus? value) + { + OnPropertyChanged(nameof(HasHomeworkStatus)); + OnPropertyChanged(nameof(HomeworkSymbol)); + OnPropertyChanged(nameof(HomeworkTooltip)); + OnPropertyChanged(nameof(HomeworkColor)); + } partial void OnAttendanceIconChanged(string value) { @@ -390,6 +450,7 @@ public partial class WizardTimelinePoint : ObservableObject public WizardTimelinePoint(DateOnly date, bool isExam, string ratingLabel, string examLabel, bool hasNote, string tooltip) { + Date = date; DateDisplay = date.ToString("dd.MM.", CultureInfo.InvariantCulture); IsExam = isExam; RatingLabel = ratingLabel; diff --git a/LehrerApp.Desktop/Views/Groups/AttendanceHomeworkQuickInputDialog.axaml b/LehrerApp.Desktop/Views/Groups/AttendanceHomeworkQuickInputDialog.axaml new file mode 100644 index 0000000..22b244a --- /dev/null +++ b/LehrerApp.Desktop/Views/Groups/AttendanceHomeworkQuickInputDialog.axaml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +