Compare commits

...

2 Commits

Author SHA1 Message Date
beo3000 e1a0969d6e Complete phase E1: DayState Fluxor
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 09:31:23 +01:00
beo3000 9a8d435f09 Add DayState Fluxor (E1)
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 09:30:55 +01:00
6 changed files with 877 additions and 2 deletions

View File

@ -6,7 +6,8 @@
"Bash(dotnet test:*)",
"Bash(git checkout:*)",
"Bash(git add:*)",
"Bash(git commit:*)"
"Bash(git commit:*)",
"Bash(mkdir:*)"
]
}
}

View File

@ -341,7 +341,7 @@ NavMenu.razor:
| ✓ | D2 | Personen | Person Pages | 1 Razor |
| ✓ | D3 | Expenses | ExpenseState Fluxor | 4 State-Dateien |
| ✓ | D4 | Expenses | Expense Pages | 1 Razor |
| | E1 | Days | DayState Fluxor | 4 State-Dateien |
| | E1 | Days | DayState Fluxor | 4 State-Dateien |
| ☐ | E2 | Days | Days List Page | 1 Razor |
| ☐ | E3 | Days | Day Details Page | 1 Razor |
| ☐ | E4 | Days | PersonExpense Management | Components in DayDetails |

View File

@ -0,0 +1,171 @@
using Koogle.Application.DTOs;
namespace Koogle.Web.Store.DayState;
// Load Days Actions
/// <summary>
/// Action to load all days for the current club.
/// </summary>
public record LoadDaysAction(DayFilterDto? Filter = null);
/// <summary>
/// Action dispatched when days are loaded successfully.
/// </summary>
public record LoadDaysSuccessAction(IReadOnlyList<DaySummaryDto> Days);
/// <summary>
/// Action dispatched when loading days fails.
/// </summary>
public record LoadDaysFailureAction(string Error);
// Load Day Details Actions
/// <summary>
/// Action to load a single day with full details.
/// </summary>
public record LoadDayDetailsAction(Guid DayId);
/// <summary>
/// Action dispatched when day details are loaded successfully.
/// </summary>
public record LoadDayDetailsSuccessAction(DayDto Day);
/// <summary>
/// Action dispatched when loading day details fails.
/// </summary>
public record LoadDayDetailsFailureAction(string Error);
// Create Day Actions
/// <summary>
/// Action to create a new day.
/// </summary>
public record CreateDayAction(CreateDayDto Dto);
/// <summary>
/// Action dispatched when day is created successfully.
/// </summary>
public record CreateDaySuccessAction(DayDto Day);
/// <summary>
/// Action dispatched when creating day fails.
/// </summary>
public record CreateDayFailureAction(string Error);
// Update Day Actions
/// <summary>
/// Action to update an existing day.
/// </summary>
public record UpdateDayAction(UpdateDayDto Dto);
/// <summary>
/// Action dispatched when day is updated successfully.
/// </summary>
public record UpdateDaySuccessAction(DayDto Day);
/// <summary>
/// Action dispatched when updating day fails.
/// </summary>
public record UpdateDayFailureAction(string Error);
// Delete Day Actions
/// <summary>
/// Action to delete a day.
/// </summary>
public record DeleteDayAction(Guid Id);
/// <summary>
/// Action dispatched when day is deleted successfully.
/// </summary>
public record DeleteDaySuccessAction(Guid Id);
/// <summary>
/// Action dispatched when deleting day fails.
/// </summary>
public record DeleteDayFailureAction(string Error);
// Participant Actions
/// <summary>
/// Action to add a participant to a day.
/// </summary>
public record AddDayParticipantAction(AddDayParticipantDto Dto);
/// <summary>
/// Action dispatched when participant is added successfully.
/// </summary>
public record AddDayParticipantSuccessAction(DayDto Day);
/// <summary>
/// Action dispatched when adding participant fails.
/// </summary>
public record AddDayParticipantFailureAction(string Error);
/// <summary>
/// Action to remove a participant from a day.
/// </summary>
public record RemoveDayParticipantAction(RemoveDayParticipantDto Dto);
/// <summary>
/// Action dispatched when participant is removed successfully.
/// </summary>
public record RemoveDayParticipantSuccessAction(DayDto Day);
/// <summary>
/// Action dispatched when removing participant fails.
/// </summary>
public record RemoveDayParticipantFailureAction(string Error);
// Status Actions
/// <summary>
/// Action to advance day status (New → Started → Closed).
/// </summary>
public record AdvanceDayStatusAction(Guid DayId);
/// <summary>
/// Action dispatched when status is advanced successfully.
/// </summary>
public record AdvanceDayStatusSuccessAction(DayDto Day);
/// <summary>
/// Action dispatched when advancing status fails.
/// </summary>
public record AdvanceDayStatusFailureAction(string Error);
// Available Persons Actions
/// <summary>
/// Action to load available persons for participant selection.
/// </summary>
public record LoadAvailablePersonsAction;
/// <summary>
/// Action dispatched when available persons are loaded successfully.
/// </summary>
public record LoadAvailablePersonsSuccessAction(IReadOnlyList<PersonDto> Persons);
/// <summary>
/// Action dispatched when loading available persons fails.
/// </summary>
public record LoadAvailablePersonsFailureAction(string Error);
// Selection and Error Actions
/// <summary>
/// Action to select a day for editing.
/// </summary>
public record SelectDayAction(DayDto? Day);
/// <summary>
/// Action to clear day error state.
/// </summary>
public record ClearDayErrorAction;
/// <summary>
/// Action to set filter for day list.
/// </summary>
public record SetDayFilterAction(DayFilterDto? Filter);

View File

@ -0,0 +1,221 @@
using Fluxor;
using Koogle.Application.Interfaces;
namespace Koogle.Web.Store.DayState;
/// <summary>
/// Side effects for day state management.
/// Handles async operations like API calls.
/// </summary>
public class DayEffects
{
private readonly IDayService _dayService;
private readonly IPersonService _personService;
private readonly ILogger<DayEffects> _logger;
/// <summary>
/// Initializes a new instance of the DayEffects class.
/// </summary>
public DayEffects(IDayService dayService, IPersonService personService, ILogger<DayEffects> logger)
{
_dayService = dayService;
_personService = personService;
_logger = logger;
}
/// <summary>
/// Handles LoadDaysAction - loads all days from service.
/// </summary>
[EffectMethod]
public async Task HandleLoadDays(LoadDaysAction action, IDispatcher dispatcher)
{
try
{
_logger.LogDebug("Loading days with filter {@Filter}", action.Filter);
var result = await _dayService.GetAllAsync(action.Filter);
dispatcher.Dispatch(new LoadDaysSuccessAction(result.Items));
_logger.LogInformation("Loaded {Count} days", result.Items.Count);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to load days");
dispatcher.Dispatch(new LoadDaysFailureAction(ex.Message));
}
}
/// <summary>
/// Handles LoadDayDetailsAction - loads day details from service.
/// </summary>
[EffectMethod]
public async Task HandleLoadDayDetails(LoadDayDetailsAction action, IDispatcher dispatcher)
{
try
{
_logger.LogDebug("Loading day details for {DayId}", action.DayId);
var day = await _dayService.GetByIdAsync(action.DayId);
if (day is null)
{
dispatcher.Dispatch(new LoadDayDetailsFailureAction("Spieltag nicht gefunden."));
return;
}
dispatcher.Dispatch(new LoadDayDetailsSuccessAction(day));
_logger.LogInformation("Loaded day details for {DayId}", action.DayId);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to load day details for {DayId}", action.DayId);
dispatcher.Dispatch(new LoadDayDetailsFailureAction(ex.Message));
}
}
/// <summary>
/// Handles CreateDayAction - creates a new day.
/// </summary>
[EffectMethod]
public async Task HandleCreateDay(CreateDayAction action, IDispatcher dispatcher)
{
try
{
_logger.LogDebug("Creating day for {PostDate}", action.Dto.PostDate);
var day = await _dayService.CreateAsync(action.Dto);
dispatcher.Dispatch(new CreateDaySuccessAction(day));
_logger.LogInformation("Created day {Id} for {PostDate}", day.Id, day.PostDate);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to create day for {PostDate}", action.Dto.PostDate);
dispatcher.Dispatch(new CreateDayFailureAction(ex.Message));
}
}
/// <summary>
/// Handles UpdateDayAction - updates an existing day.
/// </summary>
[EffectMethod]
public async Task HandleUpdateDay(UpdateDayAction action, IDispatcher dispatcher)
{
try
{
_logger.LogDebug("Updating day {Id}", action.Dto.Id);
var day = await _dayService.UpdateAsync(action.Dto);
dispatcher.Dispatch(new UpdateDaySuccessAction(day));
_logger.LogInformation("Updated day {Id}", day.Id);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to update day {Id}", action.Dto.Id);
dispatcher.Dispatch(new UpdateDayFailureAction(ex.Message));
}
}
/// <summary>
/// Handles DeleteDayAction - deletes a day.
/// </summary>
[EffectMethod]
public async Task HandleDeleteDay(DeleteDayAction action, IDispatcher dispatcher)
{
try
{
_logger.LogDebug("Deleting day {Id}", action.Id);
var success = await _dayService.DeleteAsync(action.Id);
if (success)
{
dispatcher.Dispatch(new DeleteDaySuccessAction(action.Id));
_logger.LogInformation("Deleted day {Id}", action.Id);
}
else
{
dispatcher.Dispatch(new DeleteDayFailureAction("Spieltag konnte nicht gelöscht werden."));
_logger.LogWarning("Failed to delete day {Id} - not found or already deleted", action.Id);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to delete day {Id}", action.Id);
dispatcher.Dispatch(new DeleteDayFailureAction(ex.Message));
}
}
/// <summary>
/// Handles AddDayParticipantAction - adds participant to day.
/// </summary>
[EffectMethod]
public async Task HandleAddDayParticipant(AddDayParticipantAction action, IDispatcher dispatcher)
{
try
{
_logger.LogDebug("Adding participant {PersonId} to day {DayId}", action.Dto.PersonId, action.Dto.DayId);
var day = await _dayService.AddParticipantAsync(action.Dto);
dispatcher.Dispatch(new AddDayParticipantSuccessAction(day));
_logger.LogInformation("Added participant {PersonId} to day {DayId}", action.Dto.PersonId, action.Dto.DayId);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to add participant {PersonId} to day {DayId}", action.Dto.PersonId, action.Dto.DayId);
dispatcher.Dispatch(new AddDayParticipantFailureAction(ex.Message));
}
}
/// <summary>
/// Handles RemoveDayParticipantAction - removes participant from day.
/// </summary>
[EffectMethod]
public async Task HandleRemoveDayParticipant(RemoveDayParticipantAction action, IDispatcher dispatcher)
{
try
{
_logger.LogDebug("Removing participant {PersonId} from day {DayId}", action.Dto.PersonId, action.Dto.DayId);
var day = await _dayService.RemoveParticipantAsync(action.Dto);
dispatcher.Dispatch(new RemoveDayParticipantSuccessAction(day));
_logger.LogInformation("Removed participant {PersonId} from day {DayId}", action.Dto.PersonId, action.Dto.DayId);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to remove participant {PersonId} from day {DayId}", action.Dto.PersonId, action.Dto.DayId);
dispatcher.Dispatch(new RemoveDayParticipantFailureAction(ex.Message));
}
}
/// <summary>
/// Handles AdvanceDayStatusAction - advances day status.
/// </summary>
[EffectMethod]
public async Task HandleAdvanceDayStatus(AdvanceDayStatusAction action, IDispatcher dispatcher)
{
try
{
_logger.LogDebug("Advancing status for day {DayId}", action.DayId);
var day = await _dayService.AdvanceStatusAsync(action.DayId);
dispatcher.Dispatch(new AdvanceDayStatusSuccessAction(day));
_logger.LogInformation("Advanced status for day {DayId} to {Status}", action.DayId, day.Status);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to advance status for day {DayId}", action.DayId);
dispatcher.Dispatch(new AdvanceDayStatusFailureAction(ex.Message));
}
}
/// <summary>
/// Handles LoadAvailablePersonsAction - loads all persons for selection.
/// </summary>
[EffectMethod]
public async Task HandleLoadAvailablePersons(LoadAvailablePersonsAction action, IDispatcher dispatcher)
{
try
{
_logger.LogDebug("Loading available persons");
var persons = await _personService.GetAllAsync();
dispatcher.Dispatch(new LoadAvailablePersonsSuccessAction(persons));
_logger.LogInformation("Loaded {Count} available persons", persons.Count);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to load available persons");
dispatcher.Dispatch(new LoadAvailablePersonsFailureAction(ex.Message));
}
}
}

View File

@ -0,0 +1,423 @@
using Fluxor;
namespace Koogle.Web.Store.DayState;
/// <summary>
/// Reducers for day state management.
/// </summary>
public static class DayReducers
{
// Load Days Reducers
/// <summary>
/// Handles LoadDaysAction - sets loading state and filter.
/// </summary>
[ReducerMethod]
public static DayState OnLoadDays(DayState state, LoadDaysAction action)
=> state with
{
IsLoading = true,
Error = null,
Filter = action.Filter
};
/// <summary>
/// Handles LoadDaysSuccessAction - updates days list.
/// </summary>
[ReducerMethod]
public static DayState OnLoadDaysSuccess(DayState state, LoadDaysSuccessAction action)
=> state with
{
Days = action.Days,
IsLoading = false
};
/// <summary>
/// Handles LoadDaysFailureAction - sets error state.
/// </summary>
[ReducerMethod]
public static DayState OnLoadDaysFailure(DayState state, LoadDaysFailureAction action)
=> state with
{
IsLoading = false,
Error = action.Error
};
// Load Day Details Reducers
/// <summary>
/// Handles LoadDayDetailsAction - sets loading state.
/// </summary>
[ReducerMethod(typeof(LoadDayDetailsAction))]
public static DayState OnLoadDayDetails(DayState state)
=> state with
{
IsLoading = true,
Error = null
};
/// <summary>
/// Handles LoadDayDetailsSuccessAction - sets selected day.
/// </summary>
[ReducerMethod]
public static DayState OnLoadDayDetailsSuccess(DayState state, LoadDayDetailsSuccessAction action)
=> state with
{
SelectedDay = action.Day,
IsLoading = false
};
/// <summary>
/// Handles LoadDayDetailsFailureAction - sets error state.
/// </summary>
[ReducerMethod]
public static DayState OnLoadDayDetailsFailure(DayState state, LoadDayDetailsFailureAction action)
=> state with
{
IsLoading = false,
Error = action.Error
};
// Create Day Reducers
/// <summary>
/// Handles CreateDayAction - sets loading state.
/// </summary>
[ReducerMethod(typeof(CreateDayAction))]
public static DayState OnCreateDay(DayState state)
=> state with
{
IsLoading = true,
Error = null
};
/// <summary>
/// Handles CreateDaySuccessAction - adds day summary to list and sets as selected.
/// </summary>
[ReducerMethod]
public static DayState OnCreateDaySuccess(DayState state, CreateDaySuccessAction action)
{
var summary = new Application.DTOs.DaySummaryDto
{
Id = action.Day.Id,
PostDate = action.Day.PostDate,
Status = action.Day.Status,
ParticipantCount = action.Day.ParticipantCount
};
return state with
{
Days = [.. state.Days, summary],
SelectedDay = action.Day,
IsLoading = false
};
}
/// <summary>
/// Handles CreateDayFailureAction - sets error state.
/// </summary>
[ReducerMethod]
public static DayState OnCreateDayFailure(DayState state, CreateDayFailureAction action)
=> state with
{
IsLoading = false,
Error = action.Error
};
// Update Day Reducers
/// <summary>
/// Handles UpdateDayAction - sets loading state.
/// </summary>
[ReducerMethod(typeof(UpdateDayAction))]
public static DayState OnUpdateDay(DayState state)
=> state with
{
IsLoading = true,
Error = null
};
/// <summary>
/// Handles UpdateDaySuccessAction - updates day in list and selected day.
/// </summary>
[ReducerMethod]
public static DayState OnUpdateDaySuccess(DayState state, UpdateDaySuccessAction action)
{
var updatedDays = state.Days.Select(d =>
d.Id == action.Day.Id
? new Application.DTOs.DaySummaryDto
{
Id = action.Day.Id,
PostDate = action.Day.PostDate,
Status = action.Day.Status,
ParticipantCount = action.Day.ParticipantCount
}
: d).ToList();
return state with
{
Days = updatedDays,
SelectedDay = state.SelectedDay?.Id == action.Day.Id ? action.Day : state.SelectedDay,
IsLoading = false
};
}
/// <summary>
/// Handles UpdateDayFailureAction - sets error state.
/// </summary>
[ReducerMethod]
public static DayState OnUpdateDayFailure(DayState state, UpdateDayFailureAction action)
=> state with
{
IsLoading = false,
Error = action.Error
};
// Delete Day Reducers
/// <summary>
/// Handles DeleteDayAction - sets loading state.
/// </summary>
[ReducerMethod(typeof(DeleteDayAction))]
public static DayState OnDeleteDay(DayState state)
=> state with
{
IsLoading = true,
Error = null
};
/// <summary>
/// Handles DeleteDaySuccessAction - removes day from list.
/// </summary>
[ReducerMethod]
public static DayState OnDeleteDaySuccess(DayState state, DeleteDaySuccessAction action)
=> state with
{
Days = state.Days.Where(d => d.Id != action.Id).ToList(),
SelectedDay = state.SelectedDay?.Id == action.Id ? null : state.SelectedDay,
IsLoading = false
};
/// <summary>
/// Handles DeleteDayFailureAction - sets error state.
/// </summary>
[ReducerMethod]
public static DayState OnDeleteDayFailure(DayState state, DeleteDayFailureAction action)
=> state with
{
IsLoading = false,
Error = action.Error
};
// Participant Reducers
/// <summary>
/// Handles AddDayParticipantAction - sets loading state.
/// </summary>
[ReducerMethod(typeof(AddDayParticipantAction))]
public static DayState OnAddDayParticipant(DayState state)
=> state with
{
IsLoading = true,
Error = null
};
/// <summary>
/// Handles AddDayParticipantSuccessAction - updates selected day and summary.
/// </summary>
[ReducerMethod]
public static DayState OnAddDayParticipantSuccess(DayState state, AddDayParticipantSuccessAction action)
{
var updatedDays = state.Days.Select(d =>
d.Id == action.Day.Id
? new Application.DTOs.DaySummaryDto
{
Id = action.Day.Id,
PostDate = action.Day.PostDate,
Status = action.Day.Status,
ParticipantCount = action.Day.ParticipantCount
}
: d).ToList();
return state with
{
Days = updatedDays,
SelectedDay = action.Day,
IsLoading = false
};
}
/// <summary>
/// Handles AddDayParticipantFailureAction - sets error state.
/// </summary>
[ReducerMethod]
public static DayState OnAddDayParticipantFailure(DayState state, AddDayParticipantFailureAction action)
=> state with
{
IsLoading = false,
Error = action.Error
};
/// <summary>
/// Handles RemoveDayParticipantAction - sets loading state.
/// </summary>
[ReducerMethod(typeof(RemoveDayParticipantAction))]
public static DayState OnRemoveDayParticipant(DayState state)
=> state with
{
IsLoading = true,
Error = null
};
/// <summary>
/// Handles RemoveDayParticipantSuccessAction - updates selected day and summary.
/// </summary>
[ReducerMethod]
public static DayState OnRemoveDayParticipantSuccess(DayState state, RemoveDayParticipantSuccessAction action)
{
var updatedDays = state.Days.Select(d =>
d.Id == action.Day.Id
? new Application.DTOs.DaySummaryDto
{
Id = action.Day.Id,
PostDate = action.Day.PostDate,
Status = action.Day.Status,
ParticipantCount = action.Day.ParticipantCount
}
: d).ToList();
return state with
{
Days = updatedDays,
SelectedDay = action.Day,
IsLoading = false
};
}
/// <summary>
/// Handles RemoveDayParticipantFailureAction - sets error state.
/// </summary>
[ReducerMethod]
public static DayState OnRemoveDayParticipantFailure(DayState state, RemoveDayParticipantFailureAction action)
=> state with
{
IsLoading = false,
Error = action.Error
};
// Status Reducers
/// <summary>
/// Handles AdvanceDayStatusAction - sets loading state.
/// </summary>
[ReducerMethod(typeof(AdvanceDayStatusAction))]
public static DayState OnAdvanceDayStatus(DayState state)
=> state with
{
IsLoading = true,
Error = null
};
/// <summary>
/// Handles AdvanceDayStatusSuccessAction - updates day status.
/// </summary>
[ReducerMethod]
public static DayState OnAdvanceDayStatusSuccess(DayState state, AdvanceDayStatusSuccessAction action)
{
var updatedDays = state.Days.Select(d =>
d.Id == action.Day.Id
? new Application.DTOs.DaySummaryDto
{
Id = action.Day.Id,
PostDate = action.Day.PostDate,
Status = action.Day.Status,
ParticipantCount = action.Day.ParticipantCount
}
: d).ToList();
return state with
{
Days = updatedDays,
SelectedDay = state.SelectedDay?.Id == action.Day.Id ? action.Day : state.SelectedDay,
IsLoading = false
};
}
/// <summary>
/// Handles AdvanceDayStatusFailureAction - sets error state.
/// </summary>
[ReducerMethod]
public static DayState OnAdvanceDayStatusFailure(DayState state, AdvanceDayStatusFailureAction action)
=> state with
{
IsLoading = false,
Error = action.Error
};
// Available Persons Reducers
/// <summary>
/// Handles LoadAvailablePersonsAction - sets loading state.
/// </summary>
[ReducerMethod(typeof(LoadAvailablePersonsAction))]
public static DayState OnLoadAvailablePersons(DayState state)
=> state with
{
IsLoading = true,
Error = null
};
/// <summary>
/// Handles LoadAvailablePersonsSuccessAction - sets available persons.
/// </summary>
[ReducerMethod]
public static DayState OnLoadAvailablePersonsSuccess(DayState state, LoadAvailablePersonsSuccessAction action)
=> state with
{
AvailablePersons = action.Persons,
IsLoading = false
};
/// <summary>
/// Handles LoadAvailablePersonsFailureAction - sets error state.
/// </summary>
[ReducerMethod]
public static DayState OnLoadAvailablePersonsFailure(DayState state, LoadAvailablePersonsFailureAction action)
=> state with
{
IsLoading = false,
Error = action.Error
};
// Selection and Error Reducers
/// <summary>
/// Handles SelectDayAction - sets selected day.
/// </summary>
[ReducerMethod]
public static DayState OnSelectDay(DayState state, SelectDayAction action)
=> state with
{
SelectedDay = action.Day
};
/// <summary>
/// Handles ClearDayErrorAction - clears error state.
/// </summary>
[ReducerMethod(typeof(ClearDayErrorAction))]
public static DayState OnClearError(DayState state)
=> state with
{
Error = null
};
/// <summary>
/// Handles SetDayFilterAction - sets filter.
/// </summary>
[ReducerMethod]
public static DayState OnSetDayFilter(DayState state, SetDayFilterAction action)
=> state with
{
Filter = action.Filter
};
}

View File

@ -0,0 +1,59 @@
using Fluxor;
using Koogle.Application.DTOs;
namespace Koogle.Web.Store.DayState;
/// <summary>
/// Fluxor state for day management.
/// </summary>
[FeatureState]
public record DayState
{
/// <summary>
/// List of day summaries for the current club.
/// </summary>
public IReadOnlyList<DaySummaryDto> Days { get; init; } = [];
/// <summary>
/// Currently selected day with full details.
/// </summary>
public DayDto? SelectedDay { get; init; }
/// <summary>
/// Available persons for participant selection.
/// </summary>
public IReadOnlyList<PersonDto> AvailablePersons { get; init; } = [];
/// <summary>
/// Current filter for day list.
/// </summary>
public DayFilterDto? Filter { get; init; }
/// <summary>
/// Indicates whether a day operation is in progress.
/// </summary>
public bool IsLoading { get; init; }
/// <summary>
/// Error message if operation failed.
/// </summary>
public string? Error { get; init; }
/// <summary>
/// Private constructor for Fluxor initialization.
/// </summary>
private DayState() { }
/// <summary>
/// Creates the initial state.
/// </summary>
public static DayState Initial => new()
{
Days = [],
SelectedDay = null,
AvailablePersons = [],
Filter = null,
IsLoading = false,
Error = null
};
}