using System.Collections.ObjectModel; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using LehrerApp.Core.Models; using LehrerApp.Desktop.ViewModels.Students; namespace LehrerApp.Desktop.ViewModels.ClassTeacher; // ── Anzeige-Helfer ──────────────────────────────────────────────────────── public static class VorgangTagDisplay { // Vom Nutzer selbst genannte Schlagwörter — eigene Labels bleiben trotzdem frei möglich // (AutoCompleteBox, gleiches Muster wie DocumentationTagDisplay.Suggestions). public static string[] Suggestions { get; } = ["Absentismus", "Hausaufgaben", "Verspätungen", "Konflikte", "Mitarbeit", "Elternkontakt", "Eskalation"]; } public static class VorgangStatusDisplay { public static string Label(VorgangStatus status) => status == VorgangStatus.Closed ? "Geschlossen" : "Offen"; } /// Ergebnis des "→ Vorgang"-Auswahldialogs beim Anheften einer Klassenbuchzeile: entweder ein /// bestehender, offener Vorgang oder der Titel für einen neu anzulegenden. public sealed record PinToVorgangChoice(Guid? ExistingVorgangId, string? NewVorgangTitle); /// Eingefrorene WebUntis-Klassenbuchzeile mit deutscher Anzeige-Aufbereitung — das Core-Modell /// bleibt bewusst framework-frei ohne Formatierungslogik. public sealed record VorgangClassRegisterEntryRow(VorgangClassRegisterEntry Model) { public string DateLabel => Model.Date.ToString("dd.MM.yyyy"); public string StudentName => Model.StudentName; public string SummaryLabel => string.Join(" · ", new[] { Model.Subject, Model.CategoryName, Model.Text } .Where(v => !string.IsNullOrWhiteSpace(v))); } // ── Mehrfachauswahl Schüler*innen (Anlegen/Bearbeiten-Dialog) ──────────────── public partial class VorgangStudentOption(Guid studentId, string name) : ObservableObject { public Guid StudentId { get; } = studentId; public string Name { get; } = name; [ObservableProperty] private bool _isSelected; } // ── Listen-Eintrag mit aufklappbarer Detailansicht ─────────────────────────── /// /// Ein Vorgang in der Liste des "Vorgänge"-Tabs. Verknüpfte Dokumentation/Klassenbucheinträge /// werden von beim Laden befüllt; Link/Unlink-Aktionen /// laufen über die hier übergebenen Callbacks zurück ins ViewModel (statt eigenem Repository- /// Zugriff hier), damit dieser Wrapper ein reiner Anzeige-Baustein bleibt — analog /// . /// public partial class VorgangItem : ObservableObject { private readonly Func _onLink; private readonly Func _onUnlink; private readonly Func _onRemoveClassRegisterEntry; public Vorgang Model { get; } public string StudentNames { get; } public List TagChips { get; } public string StatusLabel => VorgangStatusDisplay.Label(Model.Status); public bool IsOpen => Model.Status == VorgangStatus.Open; public string CreatedLabel => $"Angelegt {Model.CreatedAt:dd.MM.yyyy}"; public ObservableCollection LinkedDocumentation { get; } = []; public ObservableCollection AvailableDocumentation { get; } = []; public ObservableCollection ClassRegisterRows { get; } = []; public bool HasLinkedDocumentation => LinkedDocumentation.Count > 0; public bool HasAvailableDocumentation => AvailableDocumentation.Count > 0; public bool HasClassRegisterRows => ClassRegisterRows.Count > 0; [ObservableProperty] private bool _isRevealed; public VorgangItem(Vorgang model, string studentNames, Func onLink, Func onUnlink, Func onRemoveClassRegisterEntry) { Model = model; StudentNames = studentNames; TagChips = model.Tags.Select(t => new TagChip(t)).ToList(); _onLink = onLink; _onUnlink = onUnlink; _onRemoveClassRegisterEntry = onRemoveClassRegisterEntry; } [RelayCommand] private void Reveal() => IsRevealed = !IsRevealed; [RelayCommand] private async Task Link(DocumentationItem? doc) { if (doc is not null) await _onLink(this, doc); } [RelayCommand] private async Task Unlink(DocumentationItem? doc) { if (doc is not null) await _onUnlink(this, doc); } [RelayCommand] private async Task RemoveClassRegisterEntry(VorgangClassRegisterEntryRow? row) { if (row is not null) await _onRemoveClassRegisterEntry(this, row); } } // ── Dialog: Vorgang anlegen/bearbeiten ──────────────────────────────────── public partial class VorgangDialogViewModel : ObservableObject { private readonly Vorgang? _editing; public ObservableCollection StudentOptions { get; } [ObservableProperty] private string _title = ""; [ObservableProperty] private string _description = ""; [ObservableProperty] private string _newTag = ""; public ObservableCollection Tags { get; } = []; public string[] TagSuggestions => VorgangTagDisplay.Suggestions; [ObservableProperty] private string _titleError = ""; [ObservableProperty] private string _studentsError = ""; public string DialogTitle => _editing is null ? "Vorgang anlegen" : "Vorgang bearbeiten"; public Vorgang? Result { get; private set; } public VorgangDialogViewModel(List rosterStudents, Vorgang? editing) { _editing = editing; StudentOptions = new ObservableCollection(rosterStudents.Select(s => new VorgangStudentOption(s.Id, s.Name) { IsSelected = editing?.StudentIds.Contains(s.Id) == true })); if (editing is null) return; Title = editing.Title; Description = editing.Description; foreach (var tag in editing.Tags) Tags.Add(tag); } [RelayCommand] private void AddTag() { if (string.IsNullOrWhiteSpace(NewTag)) return; var tag = NewTag.Trim(); if (!Tags.Contains(tag)) Tags.Add(tag); NewTag = ""; } [RelayCommand] private void RemoveTag(string? tag) { if (tag is not null) Tags.Remove(tag); } [RelayCommand] private void Save() { TitleError = ""; StudentsError = ""; var valid = true; if (string.IsNullOrWhiteSpace(Title)) { TitleError = "Titel erforderlich."; valid = false; } var selectedIds = StudentOptions.Where(s => s.IsSelected).Select(s => s.StudentId).ToList(); if (selectedIds.Count == 0) { StudentsError = "Mindestens eine/n Schüler*in auswählen."; valid = false; } if (!valid) return; Result = _editing ?? new Vorgang(); Result.Title = Title.Trim(); Result.Description = (Description ?? "").Trim(); Result.StudentIds = selectedIds; Result.Tags = Tags.ToList(); } } // ── Dialog: Klassenbuchzeile an (bestehenden oder neuen) Vorgang anheften ──── public partial class PinToVorgangDialogViewModel : ObservableObject { public string RowSummary { get; } public List MatchingOpenCases { get; } public bool HasMatchingOpenCases => MatchingOpenCases.Count > 0; [ObservableProperty] private VorgangItem? _selectedCase; [ObservableProperty] private string _newTitle = ""; [ObservableProperty] private string _error = ""; public PinToVorgangChoice? Result { get; private set; } public PinToVorgangDialogViewModel(string rowSummary, List matchingOpenCases) { RowSummary = rowSummary; MatchingOpenCases = matchingOpenCases; } [RelayCommand] private void Confirm() { Error = ""; if (SelectedCase is not null) { Result = new PinToVorgangChoice(SelectedCase.Model.Id, null); return; } if (!string.IsNullOrWhiteSpace(NewTitle)) { Result = new PinToVorgangChoice(null, NewTitle.Trim()); return; } Error = "Bestehenden Vorgang wählen oder Titel für einen neuen Vorgang eingeben."; } }