From 33fc3323e27fed3482651ecb8e3ba62eb9206404 Mon Sep 17 00:00:00 2001 From: beo3000 Date: Tue, 23 Dec 2025 18:26:01 +0100 Subject: [PATCH] add fluxor club states --- src/Koogle.Web/Store/ClubState/ClubActions.cs | 73 ++++++++ src/Koogle.Web/Store/ClubState/ClubEffects.cs | 112 ++++++++++++ .../Store/ClubState/ClubReducers.cs | 163 ++++++++++++++++++ src/Koogle.Web/Store/ClubState/ClubState.cs | 47 +++++ 4 files changed, 395 insertions(+) create mode 100644 src/Koogle.Web/Store/ClubState/ClubActions.cs create mode 100644 src/Koogle.Web/Store/ClubState/ClubEffects.cs create mode 100644 src/Koogle.Web/Store/ClubState/ClubReducers.cs create mode 100644 src/Koogle.Web/Store/ClubState/ClubState.cs diff --git a/src/Koogle.Web/Store/ClubState/ClubActions.cs b/src/Koogle.Web/Store/ClubState/ClubActions.cs new file mode 100644 index 0000000..83d087f --- /dev/null +++ b/src/Koogle.Web/Store/ClubState/ClubActions.cs @@ -0,0 +1,73 @@ +using Koogle.Application.DTOs; + +namespace Koogle.Web.Store.ClubState; + +/// +/// Action to load all clubs. +/// +public record LoadClubsAction; + +/// +/// Action dispatched when clubs are loaded successfully. +/// +public record LoadClubsSuccessAction(IReadOnlyList Clubs); + +/// +/// Action dispatched when loading clubs fails. +/// +public record LoadClubsFailureAction(string Error); + +/// +/// Action to create a new club. +/// +public record CreateClubAction(CreateClubDto Dto); + +/// +/// Action dispatched when club is created successfully. +/// +public record CreateClubSuccessAction(ClubDto Club); + +/// +/// Action dispatched when creating club fails. +/// +public record CreateClubFailureAction(string Error); + +/// +/// Action to update an existing club. +/// +public record UpdateClubAction(UpdateClubDto Dto); + +/// +/// Action dispatched when club is updated successfully. +/// +public record UpdateClubSuccessAction(ClubDto Club); + +/// +/// Action dispatched when updating club fails. +/// +public record UpdateClubFailureAction(string Error); + +/// +/// Action to delete a club. +/// +public record DeleteClubAction(Guid Id); + +/// +/// Action dispatched when club is deleted successfully. +/// +public record DeleteClubSuccessAction(Guid Id); + +/// +/// Action dispatched when deleting club fails. +/// +public record DeleteClubFailureAction(string Error); + +/// +/// Action to select a club for editing. +/// +public record SelectClubAction(ClubDto? Club); + +/// +/// Action to clear club error state. +/// +public record ClearClubErrorAction; diff --git a/src/Koogle.Web/Store/ClubState/ClubEffects.cs b/src/Koogle.Web/Store/ClubState/ClubEffects.cs new file mode 100644 index 0000000..7eb3906 --- /dev/null +++ b/src/Koogle.Web/Store/ClubState/ClubEffects.cs @@ -0,0 +1,112 @@ +using Fluxor; +using Koogle.Application.Interfaces; + +namespace Koogle.Web.Store.ClubState; + +/// +/// Side effects for club state management. +/// Handles async operations like API calls. +/// +public class ClubEffects +{ + private readonly IClubService _clubService; + private readonly ILogger _logger; + + /// + /// Initializes a new instance of the ClubEffects class. + /// + public ClubEffects(IClubService clubService, ILogger logger) + { + _clubService = clubService; + _logger = logger; + } + + /// + /// Handles LoadClubsAction - loads all clubs from service. + /// + [EffectMethod] + public async Task HandleLoadClubs(LoadClubsAction action, IDispatcher dispatcher) + { + try + { + _logger.LogDebug("Loading clubs"); + var clubs = await _clubService.GetAllAsync(); + dispatcher.Dispatch(new LoadClubsSuccessAction(clubs)); + _logger.LogInformation("Loaded {Count} clubs", clubs.Count); + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to load clubs"); + dispatcher.Dispatch(new LoadClubsFailureAction(ex.Message)); + } + } + + /// + /// Handles CreateClubAction - creates a new club. + /// + [EffectMethod] + public async Task HandleCreateClub(CreateClubAction action, IDispatcher dispatcher) + { + try + { + _logger.LogDebug("Creating club {Name}", action.Dto.Name); + var club = await _clubService.CreateAsync(action.Dto); + dispatcher.Dispatch(new CreateClubSuccessAction(club)); + _logger.LogInformation("Created club {Name} with ID {Id}", club.Name, club.Id); + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to create club {Name}", action.Dto.Name); + dispatcher.Dispatch(new CreateClubFailureAction(ex.Message)); + } + } + + /// + /// Handles UpdateClubAction - updates an existing club. + /// + [EffectMethod] + public async Task HandleUpdateClub(UpdateClubAction action, IDispatcher dispatcher) + { + try + { + _logger.LogDebug("Updating club {Id}", action.Dto.Id); + var club = await _clubService.UpdateAsync(action.Dto); + dispatcher.Dispatch(new UpdateClubSuccessAction(club)); + _logger.LogInformation("Updated club {Name}", club.Name); + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to update club {Id}", action.Dto.Id); + dispatcher.Dispatch(new UpdateClubFailureAction(ex.Message)); + } + } + + /// + /// Handles DeleteClubAction - deletes a club. + /// + [EffectMethod] + public async Task HandleDeleteClub(DeleteClubAction action, IDispatcher dispatcher) + { + try + { + _logger.LogDebug("Deleting club {Id}", action.Id); + var success = await _clubService.DeleteAsync(action.Id); + + if (success) + { + dispatcher.Dispatch(new DeleteClubSuccessAction(action.Id)); + _logger.LogInformation("Deleted club {Id}", action.Id); + } + else + { + dispatcher.Dispatch(new DeleteClubFailureAction("Club konnte nicht gelöscht werden.")); + _logger.LogWarning("Failed to delete club {Id} - not found or already deleted", action.Id); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to delete club {Id}", action.Id); + dispatcher.Dispatch(new DeleteClubFailureAction(ex.Message)); + } + } +} diff --git a/src/Koogle.Web/Store/ClubState/ClubReducers.cs b/src/Koogle.Web/Store/ClubState/ClubReducers.cs new file mode 100644 index 0000000..56f69cc --- /dev/null +++ b/src/Koogle.Web/Store/ClubState/ClubReducers.cs @@ -0,0 +1,163 @@ +using Fluxor; + +namespace Koogle.Web.Store.ClubState; + +/// +/// Reducers for club state management. +/// +public static class ClubReducers +{ + /// + /// Handles LoadClubsAction - sets loading state. + /// + [ReducerMethod(typeof(LoadClubsAction))] + public static ClubState OnLoadClubs(ClubState state) + => state with + { + IsLoading = true, + Error = null + }; + + /// + /// Handles LoadClubsSuccessAction - updates clubs list. + /// + [ReducerMethod] + public static ClubState OnLoadClubsSuccess(ClubState state, LoadClubsSuccessAction action) + => state with + { + Clubs = action.Clubs, + IsLoading = false + }; + + /// + /// Handles LoadClubsFailureAction - sets error state. + /// + [ReducerMethod] + public static ClubState OnLoadClubsFailure(ClubState state, LoadClubsFailureAction action) + => state with + { + IsLoading = false, + Error = action.Error + }; + + /// + /// Handles CreateClubAction - sets loading state. + /// + [ReducerMethod(typeof(CreateClubAction))] + public static ClubState OnCreateClub(ClubState state) + => state with + { + IsLoading = true, + Error = null + }; + + /// + /// Handles CreateClubSuccessAction - adds club to list. + /// + [ReducerMethod] + public static ClubState OnCreateClubSuccess(ClubState state, CreateClubSuccessAction action) + => state with + { + Clubs = [.. state.Clubs, action.Club], + IsLoading = false + }; + + /// + /// Handles CreateClubFailureAction - sets error state. + /// + [ReducerMethod] + public static ClubState OnCreateClubFailure(ClubState state, CreateClubFailureAction action) + => state with + { + IsLoading = false, + Error = action.Error + }; + + /// + /// Handles UpdateClubAction - sets loading state. + /// + [ReducerMethod(typeof(UpdateClubAction))] + public static ClubState OnUpdateClub(ClubState state) + => state with + { + IsLoading = true, + Error = null + }; + + /// + /// Handles UpdateClubSuccessAction - replaces club in list. + /// + [ReducerMethod] + public static ClubState OnUpdateClubSuccess(ClubState state, UpdateClubSuccessAction action) + => state with + { + Clubs = state.Clubs.Select(c => c.Id == action.Club.Id ? action.Club : c).ToList(), + SelectedClub = state.SelectedClub?.Id == action.Club.Id ? action.Club : state.SelectedClub, + IsLoading = false + }; + + /// + /// Handles UpdateClubFailureAction - sets error state. + /// + [ReducerMethod] + public static ClubState OnUpdateClubFailure(ClubState state, UpdateClubFailureAction action) + => state with + { + IsLoading = false, + Error = action.Error + }; + + /// + /// Handles DeleteClubAction - sets loading state. + /// + [ReducerMethod(typeof(DeleteClubAction))] + public static ClubState OnDeleteClub(ClubState state) + => state with + { + IsLoading = true, + Error = null + }; + + /// + /// Handles DeleteClubSuccessAction - removes club from list. + /// + [ReducerMethod] + public static ClubState OnDeleteClubSuccess(ClubState state, DeleteClubSuccessAction action) + => state with + { + Clubs = state.Clubs.Where(c => c.Id != action.Id).ToList(), + SelectedClub = state.SelectedClub?.Id == action.Id ? null : state.SelectedClub, + IsLoading = false + }; + + /// + /// Handles DeleteClubFailureAction - sets error state. + /// + [ReducerMethod] + public static ClubState OnDeleteClubFailure(ClubState state, DeleteClubFailureAction action) + => state with + { + IsLoading = false, + Error = action.Error + }; + + /// + /// Handles SelectClubAction - sets selected club. + /// + [ReducerMethod] + public static ClubState OnSelectClub(ClubState state, SelectClubAction action) + => state with + { + SelectedClub = action.Club + }; + + /// + /// Handles ClearClubErrorAction - clears error state. + /// + [ReducerMethod(typeof(ClearClubErrorAction))] + public static ClubState OnClearError(ClubState state) + => state with + { + Error = null + }; +} diff --git a/src/Koogle.Web/Store/ClubState/ClubState.cs b/src/Koogle.Web/Store/ClubState/ClubState.cs new file mode 100644 index 0000000..d8ed7fd --- /dev/null +++ b/src/Koogle.Web/Store/ClubState/ClubState.cs @@ -0,0 +1,47 @@ +using Fluxor; +using Koogle.Application.DTOs; + +namespace Koogle.Web.Store.ClubState; + +/// +/// Fluxor state for club management. +/// +[FeatureState] +public record ClubState +{ + /// + /// List of all clubs. + /// + public IReadOnlyList Clubs { get; init; } = []; + + /// + /// Currently selected club for editing. + /// + public ClubDto? SelectedClub { get; init; } + + /// + /// Indicates whether a club operation is in progress. + /// + public bool IsLoading { get; init; } + + /// + /// Error message if operation failed. + /// + public string? Error { get; init; } + + /// + /// Private constructor for Fluxor initialization. + /// + private ClubState() { } + + /// + /// Creates the initial state. + /// + public static ClubState Initial => new() + { + Clubs = [], + SelectedClub = null, + IsLoading = false, + Error = null + }; +}