@page "/days/{DayId:guid}" @attribute [Authorize(Policy = "ClubViewer")] @inherits Fluxor.Blazor.Web.Components.FluxorComponent @using Fluxor @using Koogle.Application.DTOs @using Koogle.Domain.Enums @using Koogle.Web.Store.DayState @using Microsoft.AspNetCore.Authorization @inject IState DayState @inject IDispatcher Dispatcher @inject ISnackbar Snackbar @inject IDialogService DialogService @inject NavigationManager NavigationManager Spieltag Details @if (DayState.Value.IsLoading && DayState.Value.SelectedDay is null) { } else if (DayState.Value.Error is not null) { @DayState.Value.Error } else if (Day is null) { Spieltag nicht gefunden } else { @Day.PostDate.ToString("dddd, dd. MMMM yyyy") @GetStatusLabel(Day.Status) @if (Day.Status != DayStatus.Closed) { @GetNextStatusLabel(Day.Status) } @if (Day.Status == DayStatus.New) { Löschen } Teilnehmer (@Day.ParticipantCount) @if (Day.Status != DayStatus.Closed) { Hinzufügen } @if (Day.Participants.Count == 0) { Keine Teilnehmer } else { @foreach (var participant in Day.Participants.OrderBy(p => p.PersonName)) { @participant.PersonName[0] @participant.PersonName @if (participant.PersonStatus == PersonStatus.Guest) { Gast } @if (Day.Status != DayStatus.Closed) { } } } Details Status @GetStatusLabel(Day.Status) Datum @Day.PostDate.ToString("dd.MM.yyyy") Teilnehmer @Day.ParticipantCount Erstellt @Day.CreatedAt.ToString("dd.MM.yyyy HH:mm") @if (Day.ModifiedAt.HasValue) { Geändert @Day.ModifiedAt.Value.ToString("dd.MM.yyyy HH:mm") } Status-Workflow Neu Gestartet Abgeschlossen Strafen / Kosten Die Verwaltung von Strafen und Kosten wird in Phase E4 implementiert. } @code { [Parameter] public Guid DayId { get; set; } private DayDto? Day => DayState.Value.SelectedDay; protected override void OnInitialized() { base.OnInitialized(); Dispatcher.Dispatch(new LoadDayDetailsAction(DayId)); Dispatcher.Dispatch(new LoadAvailablePersonsAction()); } protected override void OnParametersSet() { base.OnParametersSet(); if (Day?.Id != DayId) { Dispatcher.Dispatch(new LoadDayDetailsAction(DayId)); } } private void ClearError() { Dispatcher.Dispatch(new ClearDayErrorAction()); } private void NavigateBack() { NavigationManager.NavigateTo("/days"); } private static string GetStatusLabel(DayStatus status) => status switch { DayStatus.New => "Neu", DayStatus.Started => "Gestartet", DayStatus.Postponed => "Verschoben", DayStatus.Closed => "Abgeschlossen", _ => status.ToString() }; private static Color GetStatusColor(DayStatus status) => status switch { DayStatus.New => Color.Info, DayStatus.Started => Color.Warning, DayStatus.Postponed => Color.Secondary, DayStatus.Closed => Color.Success, _ => Color.Default }; private static string GetNextStatusLabel(DayStatus status) => status switch { DayStatus.New => "Starten", DayStatus.Started => "Abschließen", DayStatus.Postponed => "Fortsetzen", _ => "" }; private static Color GetNextStatusColor(DayStatus status) => status switch { DayStatus.New => Color.Warning, DayStatus.Started => Color.Success, DayStatus.Postponed => Color.Warning, _ => Color.Default }; private static string GetNextStatusIcon(DayStatus status) => status switch { DayStatus.New => Icons.Material.Filled.PlayArrow, DayStatus.Started => Icons.Material.Filled.Done, DayStatus.Postponed => Icons.Material.Filled.PlayArrow, _ => Icons.Material.Filled.Help }; private Color GetWorkflowStepColor(DayStatus targetStatus) { if (Day is null) return Color.Default; var statusOrder = new[] { DayStatus.New, DayStatus.Started, DayStatus.Closed }; var currentIndex = Array.IndexOf(statusOrder, Day.Status); var targetIndex = Array.IndexOf(statusOrder, targetStatus); if (targetIndex < currentIndex) return Color.Success; // Completed if (targetIndex == currentIndex) return GetStatusColor(Day.Status); // Current return Color.Default; // Future } private string GetWorkflowStepIcon(DayStatus targetStatus) { if (Day is null) return Icons.Material.Filled.Circle; var statusOrder = new[] { DayStatus.New, DayStatus.Started, DayStatus.Closed }; var currentIndex = Array.IndexOf(statusOrder, Day.Status); var targetIndex = Array.IndexOf(statusOrder, targetStatus); if (targetIndex < currentIndex) return Icons.Material.Filled.Check; // Completed if (targetIndex == currentIndex) return targetStatus switch { DayStatus.Started => Icons.Material.Filled.PlayArrow, DayStatus.Closed => Icons.Material.Filled.Done, _ => Icons.Material.Filled.Circle }; return Icons.Material.Filled.Circle; // Future } private void AdvanceStatus() { if (Day is null) return; Dispatcher.Dispatch(new AdvanceDayStatusAction(Day.Id)); Snackbar.Add("Status wird aktualisiert...", Severity.Info); } private async Task ConfirmDelete() { if (Day is null) return; var parameters = new DialogParameters { { "ContentText", $"Möchten Sie den Spieltag vom {Day.PostDate:dd.MM.yyyy} wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden." }, { "ButtonText", "Löschen" }, { "Color", Color.Error } }; var dialog = await DialogService.ShowAsync("Spieltag löschen", parameters); var result = await dialog.Result; if (result != null && !result.Canceled) { Dispatcher.Dispatch(new DeleteDayAction(Day.Id)); Snackbar.Add("Spieltag wird gelöscht...", Severity.Info); NavigationManager.NavigateTo("/days"); } } private async Task OpenAddParticipantDialog() { if (Day is null) return; var existingIds = Day.Participants.Select(p => p.PersonId).ToHashSet(); var availablePersons = DayState.Value.AvailablePersons .Where(p => !existingIds.Contains(p.Id)) .ToList(); if (availablePersons.Count == 0) { Snackbar.Add("Alle Personen sind bereits Teilnehmer", Severity.Info); return; } var parameters = new DialogParameters { { "AvailablePersons", availablePersons }, { "DayId", Day.Id } }; var dialog = await DialogService.ShowAsync("Teilnehmer hinzufügen", parameters); var result = await dialog.Result; if (result != null && !result.Canceled && result.Data is Guid personId) { var dto = new AddDayParticipantDto { DayId = Day.Id, PersonId = personId }; Dispatcher.Dispatch(new AddDayParticipantAction(dto)); Snackbar.Add("Teilnehmer wird hinzugefügt...", Severity.Info); } } private async Task RemoveParticipant(DayParticipantDto participant) { if (Day is null) return; var parameters = new DialogParameters { { "ContentText", $"Möchten Sie \"{participant.PersonName}\" wirklich von diesem Spieltag entfernen?" }, { "ButtonText", "Entfernen" }, { "Color", Color.Warning } }; var dialog = await DialogService.ShowAsync("Teilnehmer entfernen", parameters); var result = await dialog.Result; if (result != null && !result.Canceled) { var dto = new RemoveDayParticipantDto { DayId = Day.Id, PersonId = participant.PersonId }; Dispatcher.Dispatch(new RemoveDayParticipantAction(dto)); Snackbar.Add("Teilnehmer wird entfernt...", Severity.Info); } } }