using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using LehrerApp.Core.Interfaces; using LehrerApp.Core.Models; using System.Collections.ObjectModel; using System.Globalization; namespace LehrerApp.Desktop.ViewModels.Workload; // ── Anzeige-Hilfen für die Enums (6.1) ────────────────────────────────────── public static class WorkTaskStatusDisplay { public static string Label(WorkTaskStatus s) => s switch { WorkTaskStatus.Open => "Offen", WorkTaskStatus.InProgress => "In Bearbeitung", WorkTaskStatus.Done => "Erledigt", _ => s.ToString(), }; public static string ColorHex(WorkTaskStatus s) => s switch { WorkTaskStatus.Open => "#9E9E9E", WorkTaskStatus.InProgress => "#1976D2", WorkTaskStatus.Done => "#43A047", _ => "#9E9E9E", }; // Klick auf die Status-Kachel wechselt reihum (6.1.3). public static WorkTaskStatus Next(WorkTaskStatus s) => s switch { WorkTaskStatus.Open => WorkTaskStatus.InProgress, WorkTaskStatus.InProgress => WorkTaskStatus.Done, _ => WorkTaskStatus.Open, }; } public static class TaskCategoryDisplay { public static string Label(TaskCategory c) => c switch { TaskCategory.Correction => "Korrektur", TaskCategory.Preparation => "Vorbereitung", TaskCategory.Admin => "Verwaltung", TaskCategory.Meeting => "Besprechung", TaskCategory.Other => "Sonstiges", _ => c.ToString(), }; public static string[] Options { get; } = Enum.GetValues().Select(Label).ToArray(); public static TaskCategory FromLabel(string? label) => Enum.GetValues().FirstOrDefault(c => Label(c) == label, TaskCategory.Other); } // ── Aufgabenliste (6.1.1, 6.1.3) ───────────────────────────────────────────── public partial class WorkTaskListViewModel : ObservableObject { private readonly IWorkTaskRepository _tasks; private readonly IGroupRepository _groups; public const string AllFilter = "Alle"; public const string ActiveFilter = "Offene Aufgaben"; public const string DoneFilter = "Erledigt"; public const string NoGroupFilter = "Ohne Gruppe"; [ObservableProperty] private string _statusFilter = ActiveFilter; [ObservableProperty] private string _categoryFilter = AllFilter; [ObservableProperty] private string _groupFilter = AllFilter; public List StatusFilterOptions { get; } = [ActiveFilter, AllFilter, DoneFilter]; public List CategoryFilterOptions { get; } = [AllFilter, .. TaskCategoryDisplay.Options]; public ObservableCollection GroupFilterOptions { get; } = [AllFilter, NoGroupFilter]; public ObservableCollection Tasks { get; } = []; public string CountSummary => $"{Tasks.Count} Aufgabe(n)"; public Func>? OnEditTask { get; set; } private Dictionary _groupNames = []; public WorkTaskListViewModel(IWorkTaskRepository tasks, IGroupRepository groups) { _tasks = tasks; _groups = groups; Load(); } partial void OnStatusFilterChanged(string value) => Refresh(); partial void OnCategoryFilterChanged(string value) => Refresh(); partial void OnGroupFilterChanged(string value) => Refresh(); public void Load() { var groups = _groups.GetAll(includeInactive: true); _groupNames = groups.ToDictionary(g => g.Id, g => g.Name); GroupFilterOptions.Clear(); GroupFilterOptions.Add(AllFilter); GroupFilterOptions.Add(NoGroupFilter); foreach (var name in groups.Select(g => g.Name).OrderBy(n => n)) GroupFilterOptions.Add(name); Refresh(); } private void Refresh() { Tasks.Clear(); var all = _tasks.GetAll().AsEnumerable(); all = StatusFilter switch { ActiveFilter => all.Where(t => t.Status != WorkTaskStatus.Done), DoneFilter => all.Where(t => t.Status == WorkTaskStatus.Done), _ => all, }; if (CategoryFilter != AllFilter) { var category = TaskCategoryDisplay.FromLabel(CategoryFilter); all = all.Where(t => t.Category == category); } if (GroupFilter == NoGroupFilter) all = all.Where(t => t.GroupId is null); else if (GroupFilter != AllFilter) all = all.Where(t => t.GroupId.HasValue && _groupNames.GetValueOrDefault(t.GroupId.Value) == GroupFilter); // Fälligkeit: fällige zuerst, unbefristete ans Ende (6.1.1). foreach (var t in all.OrderBy(t => t.DueDate ?? DateOnly.MaxValue).ThenBy(t => t.Title)) Tasks.Add(new WorkTaskListItem(t, _groupNames.GetValueOrDefault(t.GroupId ?? Guid.Empty))); OnPropertyChanged(nameof(CountSummary)); } [RelayCommand] private async Task AddTask() { if (OnEditTask is null) return; var result = await OnEditTask(null); if (result is null) return; _tasks.Save(result); Refresh(); } [RelayCommand] private async Task EditTask(WorkTaskListItem? item) { if (item is null || OnEditTask is null) return; var result = await OnEditTask(item.Model); if (result is null) return; _tasks.Save(result); Refresh(); } [RelayCommand] private void CycleStatus(WorkTaskListItem? item) { if (item is null) return; item.Model.Status = WorkTaskStatusDisplay.Next(item.Model.Status); _tasks.Save(item.Model); Refresh(); } [RelayCommand] private void DeleteTask(WorkTaskListItem? item) { if (item is null) return; _tasks.Delete(item.Model.Id); Refresh(); } } public class WorkTaskListItem(WorkTask model, string? groupName) { public WorkTask Model { get; } = model; public string Title => Model.Title; public string CategoryLabel => TaskCategoryDisplay.Label(Model.Category); public string GroupName => groupName ?? ""; public string DueDateDisplay => Model.DueDate?.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture) ?? ""; public bool IsOverdue => Model.DueDate is { } d && d < DateOnly.FromDateTime(DateTime.Today) && Model.Status != WorkTaskStatus.Done; public string DueDateColorHex => IsOverdue ? "#D32F2F" : "#9E9E9E"; public string StatusLabel => WorkTaskStatusDisplay.Label(Model.Status); public string StatusColorHex => WorkTaskStatusDisplay.ColorHex(Model.Status); public string EstimatedMinutesDisplay => Model.EstimatedMinutes is { } m ? $"{m} min" : ""; } // ── Dialog: Aufgabe anlegen/bearbeiten (6.1.2) ─────────────────────────────── public partial class AddEditWorkTaskDialogViewModel : ObservableObject { private readonly WorkTask? _source; [ObservableProperty] private string _title = ""; [ObservableProperty] private string _selectedCategory = TaskCategoryDisplay.Options[0]; [ObservableProperty] private LearningGroup? _selectedGroup; [ObservableProperty] private string _dueDateText = ""; [ObservableProperty] private string _estimatedMinutesText = ""; [ObservableProperty] private string _notes = ""; [ObservableProperty] private string _titleError = ""; [ObservableProperty] private string _dueDateError = ""; [ObservableProperty] private string _estimatedMinutesError = ""; public string DialogTitle => _source is null ? "Aufgabe anlegen" : "Aufgabe bearbeiten"; public List CategoryOptions { get; } = [.. TaskCategoryDisplay.Options]; public List Groups { get; } public WorkTask? Result { get; private set; } public AddEditWorkTaskDialogViewModel(WorkTask? source, List groups) { _source = source; Groups = groups; if (source is null) return; Title = source.Title; SelectedCategory = TaskCategoryDisplay.Label(source.Category); SelectedGroup = source.GroupId is { } id ? groups.FirstOrDefault(g => g.Id == id) : null; DueDateText = source.DueDate?.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture) ?? ""; EstimatedMinutesText = source.EstimatedMinutes?.ToString(CultureInfo.InvariantCulture) ?? ""; Notes = source.Notes ?? ""; } [RelayCommand] private void Save() { TitleError = ""; DueDateError = ""; EstimatedMinutesError = ""; var valid = true; if (string.IsNullOrWhiteSpace(Title)) { TitleError = "Titel erforderlich."; valid = false; } DateOnly? dueDate = null; if (!string.IsNullOrWhiteSpace(DueDateText)) { if (!DateOnly.TryParseExact(DueDateText, "dd.MM.yyyy", null, DateTimeStyles.None, out var d)) { DueDateError = "Format TT.MM.JJJJ."; valid = false; } else dueDate = d; } int? estimatedMinutes = null; if (!string.IsNullOrWhiteSpace(EstimatedMinutesText)) { if (!int.TryParse(EstimatedMinutesText, out var m) || m <= 0) { EstimatedMinutesError = "Ganze Zahl > 0 erwartet."; valid = false; } else estimatedMinutes = m; } if (!valid) return; Result = new WorkTask { Id = _source?.Id ?? Guid.NewGuid(), Title = Title.Trim(), Category = TaskCategoryDisplay.FromLabel(SelectedCategory), GroupId = SelectedGroup?.Id, DueDate = dueDate, EstimatedMinutes = estimatedMinutes, Status = _source?.Status ?? WorkTaskStatus.Open, Notes = string.IsNullOrWhiteSpace(Notes) ? null : Notes.Trim(), CreatedAt = _source?.CreatedAt ?? DateTime.UtcNow, }; } }