feat: Abwesenheits-Hinweise, Fehlquote bei Zeugnisnoten, Gruppen-Dokumentation
- Schnellbewerten-Dialog: abwesende Schüler werden gedimmt und mit ihrem Anwesenheitsstatus statt der Aspektbeschriftung angezeigt, damit keine Mitarbeitsnote für nicht anwesende Schüler vergeben wird. - Zeugnisnoten-Dialog: zeigt je Schüler die Fehlquote im gewählten Zeitraum, ab 50 % hervorgehoben (informativ, keine automatische Notenänderung). - Gruppen-Tab "Dokumentation" (bisher Platzhalter) implementiert: listet alle Dokumentationseinträge der Gruppen-Schüler, mit Schüler-Filter und optionalem "Nur dieser Unterricht"-Schalter. Einträge aus anderen Lerngruppen werden standardmäßig mitangezeigt, aber gedimmt. Der Dokumentationsdialog bekommt dafür einen optionalen Schüler-Picker. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,9 +4,14 @@ using LehrerApp.Core.Interfaces;
|
||||
using LehrerApp.Core.Models;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace LehrerApp.Desktop.ViewModels.Students;
|
||||
|
||||
/// Für die Schüler-Auswahl im Dokumentationsdialog, wenn er ohne festen Schüler geöffnet wird
|
||||
/// (Gruppen-Tab, siehe GroupDocumentationTabViewModel).
|
||||
public record StudentOption(Guid Id, string Name);
|
||||
|
||||
// ── Dokumentation: deutsche Anzeige für Typ/Status (5.1) ─────────────────────
|
||||
|
||||
public static class DocumentationTypeDisplay
|
||||
@@ -86,8 +91,17 @@ public partial class DocumentationDialogViewModel : ObservableObject
|
||||
private readonly IAttachmentStorage _attachmentStorage;
|
||||
private readonly Documentation? _editing;
|
||||
private readonly Guid _studentId;
|
||||
private readonly Guid? _contextGroupId;
|
||||
private readonly List<string> _newlyUploadedStorageIds = [];
|
||||
|
||||
// Nur gesetzt, wenn der Dialog aus einem Gruppen-Kontext (5.1, Dokumentation-Tab der
|
||||
// Lerngruppe) ohne festen Schüler geöffnet wird — vom Schüler-Tab aus (fester _studentId)
|
||||
// bleibt die Liste leer und die Auswahl unsichtbar.
|
||||
public List<StudentOption> StudentOptions { get; }
|
||||
public bool CanPickStudent => StudentOptions.Count > 0;
|
||||
[ObservableProperty] private StudentOption? _selectedStudent;
|
||||
[ObservableProperty] private string _studentError = "";
|
||||
|
||||
[ObservableProperty] private string _typeName = DocumentationTypeDisplay.Options[0];
|
||||
[ObservableProperty] private string _dateText = DateOnly.FromDateTime(DateTime.Today).ToString("dd.MM.yyyy");
|
||||
[ObservableProperty] private string _title = "";
|
||||
@@ -145,11 +159,16 @@ public partial class DocumentationDialogViewModel : ObservableObject
|
||||
|
||||
public Documentation? Result { get; private set; }
|
||||
|
||||
public DocumentationDialogViewModel(Guid studentId, Documentation? editing, IAttachmentStorage attachmentStorage)
|
||||
public DocumentationDialogViewModel(Guid studentId, Documentation? editing, IAttachmentStorage attachmentStorage,
|
||||
List<StudentOption>? studentOptions = null, Guid? contextGroupId = null)
|
||||
{
|
||||
_studentId = studentId;
|
||||
_editing = editing;
|
||||
_attachmentStorage = attachmentStorage;
|
||||
_contextGroupId = contextGroupId;
|
||||
StudentOptions = studentOptions ?? [];
|
||||
if (CanPickStudent)
|
||||
SelectedStudent = StudentOptions.FirstOrDefault(s => s.Id == studentId);
|
||||
if (editing is null) return;
|
||||
|
||||
TypeName = DocumentationTypeDisplay.Label(editing.Type);
|
||||
@@ -276,10 +295,12 @@ public partial class DocumentationDialogViewModel : ObservableObject
|
||||
[RelayCommand]
|
||||
private void Save()
|
||||
{
|
||||
TitleError = ""; DateTextError = ""; ReviewDateTextError = "";
|
||||
TitleError = ""; DateTextError = ""; ReviewDateTextError = ""; StudentError = "";
|
||||
LetterSentDateError = ""; LetterResponseDateError = "";
|
||||
var valid = true;
|
||||
|
||||
if (CanPickStudent && SelectedStudent is null) { StudentError = "Schüler auswählen."; valid = false; }
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Title)) { TitleError = "Titel erforderlich."; valid = false; }
|
||||
|
||||
if (!DateOnly.TryParseExact(DateText, "dd.MM.yyyy", null, DateTimeStyles.None, out var date))
|
||||
@@ -312,7 +333,8 @@ public partial class DocumentationDialogViewModel : ObservableObject
|
||||
if (!valid) return;
|
||||
|
||||
var type = DocumentationTypeDisplay.FromLabel(TypeName);
|
||||
Result = _editing ?? new Documentation { StudentId = _studentId };
|
||||
var effectiveStudentId = CanPickStudent ? SelectedStudent!.Id : _studentId;
|
||||
Result = _editing ?? new Documentation { StudentId = effectiveStudentId, GroupId = _contextGroupId };
|
||||
Result.Type = type;
|
||||
Result.Date = date;
|
||||
Result.Title = Title.Trim();
|
||||
@@ -401,10 +423,21 @@ public partial class DocumentationItem : ObservableObject
|
||||
public bool HasAttachments { get; }
|
||||
public string StatusLabel { get; }
|
||||
public List<TagChip> TagChips { get; }
|
||||
/// Nur im Gruppen-Tab (5.1, GroupDocumentationTabViewModel) gefüllt — die Schüler-Detailansicht
|
||||
/// zeigt ohnehin nur Einträge eines einzelnen Schülers und braucht den Namen nicht.
|
||||
public string StudentName { get; }
|
||||
/// True, wenn der Eintrag keiner Gruppe zugeordnet ist oder der aktuell angezeigten Gruppe
|
||||
/// entspricht — false für Einträge aus einem anderen Unterricht desselben Schülers (werden im
|
||||
/// Gruppen-Tab optisch abgesetzt statt komplett ausgeblendet, siehe Nutzer-Feedback).
|
||||
public bool IsOwnGroup { get; }
|
||||
/// Name der Gruppe, aus der ein nicht-eigener Eintrag stammt (nur gesetzt, wenn !IsOwnGroup).
|
||||
public string OtherGroupLabel { get; }
|
||||
/// Dimmt Einträge aus anderen Lerngruppen im Gruppen-Tab, ohne sie zu verstecken.
|
||||
public double ContentOpacity => IsOwnGroup ? 1.0 : 0.55;
|
||||
|
||||
[ObservableProperty] private bool _isRevealed;
|
||||
|
||||
public DocumentationItem(Documentation d)
|
||||
public DocumentationItem(Documentation d, string studentName = "", bool isOwnGroup = true, string otherGroupLabel = "")
|
||||
{
|
||||
Model = d;
|
||||
DateDisplay = d.Date.ToString("dd.MM.yyyy");
|
||||
@@ -415,6 +448,9 @@ public partial class DocumentationItem : ObservableObject
|
||||
HasAttachments = d.Attachments.Count > 0;
|
||||
StatusLabel = BuildStatusLabel(d);
|
||||
TagChips = d.Tags.Select(t => new TagChip(t)).ToList();
|
||||
StudentName = studentName;
|
||||
IsOwnGroup = isOwnGroup;
|
||||
OtherGroupLabel = otherGroupLabel;
|
||||
}
|
||||
|
||||
private static string BuildStatusLabel(Documentation d) => d.Type switch
|
||||
|
||||
Reference in New Issue
Block a user