K14: add Categories UI page + dialog
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
d0beb200b3
commit
05d1622bd4
|
|
@ -0,0 +1,199 @@
|
||||||
|
@page "/cashbook/categories"
|
||||||
|
@attribute [Authorize(Policy = "ClubTreasurer")]
|
||||||
|
|
||||||
|
@inherits Fluxor.Blazor.Web.Components.FluxorComponent
|
||||||
|
|
||||||
|
@using Fluxor
|
||||||
|
@using Koogle.Application.DTOs
|
||||||
|
@using Koogle.Domain.Enums
|
||||||
|
@using Koogle.Web.Store.CategoryState
|
||||||
|
@using Microsoft.AspNetCore.Authorization
|
||||||
|
|
||||||
|
@inject IState<CategoryState> CategoryState
|
||||||
|
@inject IDispatcher Dispatcher
|
||||||
|
@inject ISnackbar Snackbar
|
||||||
|
@inject IDialogService DialogService
|
||||||
|
|
||||||
|
<PageTitle>Buchungskategorien</PageTitle>
|
||||||
|
|
||||||
|
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center" Class="mb-4">
|
||||||
|
<MudText Typo="Typo.h4">Buchungskategorien</MudText>
|
||||||
|
<MudButton Variant="Variant.Outlined" Color="Color.Secondary" StartIcon="@Icons.Material.Filled.ArrowBack"
|
||||||
|
Href="/cashbook">
|
||||||
|
Zurück zum Kassenbuch
|
||||||
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
@if (CategoryState.Value.Error is not null)
|
||||||
|
{
|
||||||
|
<MudAlert Severity="Severity.Error" Class="mb-4" ShowCloseIcon="true" CloseIconClicked="ClearError">
|
||||||
|
@CategoryState.Value.Error
|
||||||
|
</MudAlert>
|
||||||
|
}
|
||||||
|
|
||||||
|
<MudTable Items="CategoryState.Value.Categories" Dense="true" Hover="true" Loading="CategoryState.Value.IsLoading">
|
||||||
|
<ToolBarContent>
|
||||||
|
<MudSwitch @bind-Value="_showInactive" Label="Inaktive anzeigen" Color="Color.Primary" Class="mr-4" />
|
||||||
|
<MudSpacer />
|
||||||
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Add"
|
||||||
|
OnClick="OpenCreateDialog">
|
||||||
|
Neue Kategorie
|
||||||
|
</MudButton>
|
||||||
|
</ToolBarContent>
|
||||||
|
<HeaderContent>
|
||||||
|
<MudTh>Farbe</MudTh>
|
||||||
|
<MudTh>Name</MudTh>
|
||||||
|
<MudTh>Beschreibung</MudTh>
|
||||||
|
<MudTh>Typ</MudTh>
|
||||||
|
<MudTh>Status</MudTh>
|
||||||
|
<MudTh>Aktionen</MudTh>
|
||||||
|
</HeaderContent>
|
||||||
|
<RowTemplate>
|
||||||
|
<MudTd DataLabel="Farbe">
|
||||||
|
@if (!string.IsNullOrWhiteSpace(context.Color))
|
||||||
|
{
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Circle" Style="@($"color: {context.Color}")" Size="Size.Small" />
|
||||||
|
}
|
||||||
|
</MudTd>
|
||||||
|
<MudTd DataLabel="Name" Style="@(context.IsActive ? "" : "opacity: 0.5;")">
|
||||||
|
@if (!string.IsNullOrWhiteSpace(context.Icon))
|
||||||
|
{
|
||||||
|
<MudIcon Icon="@context.Icon" Size="Size.Small" Class="mr-1" />
|
||||||
|
}
|
||||||
|
@context.Name
|
||||||
|
@if (context.IsSystemCategory)
|
||||||
|
{
|
||||||
|
<MudTooltip Text="System-Kategorie (nicht löschbar)">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Lock" Size="Size.Small" Color="Color.Secondary" Class="ml-1" />
|
||||||
|
</MudTooltip>
|
||||||
|
}
|
||||||
|
</MudTd>
|
||||||
|
<MudTd DataLabel="Beschreibung" Style="@(context.IsActive ? "" : "opacity: 0.5;")">
|
||||||
|
@context.Description
|
||||||
|
</MudTd>
|
||||||
|
<MudTd DataLabel="Typ">
|
||||||
|
<MudChip T="string" Size="Size.Small" Color="@GetTypeColor(context.CategoryType)">
|
||||||
|
@GetTypeLabel(context.CategoryType)
|
||||||
|
</MudChip>
|
||||||
|
</MudTd>
|
||||||
|
<MudTd DataLabel="Status">
|
||||||
|
@if (context.IsActive)
|
||||||
|
{
|
||||||
|
<MudChip T="string" Size="Size.Small" Color="Color.Success">Aktiv</MudChip>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudChip T="string" Size="Size.Small" Color="Color.Default">Inaktiv</MudChip>
|
||||||
|
}
|
||||||
|
</MudTd>
|
||||||
|
<MudTd DataLabel="Aktionen">
|
||||||
|
<MudTooltip Text="Bearbeiten">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.Edit"
|
||||||
|
Size="Size.Small"
|
||||||
|
OnClick="@(() => OpenEditDialog(context))"/>
|
||||||
|
</MudTooltip>
|
||||||
|
@if (!context.IsSystemCategory)
|
||||||
|
{
|
||||||
|
<MudTooltip Text="Löschen">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.Delete"
|
||||||
|
Size="Size.Small"
|
||||||
|
Color="Color.Error"
|
||||||
|
OnClick="@(() => ConfirmDelete(context))"/>
|
||||||
|
</MudTooltip>
|
||||||
|
}
|
||||||
|
</MudTd>
|
||||||
|
</RowTemplate>
|
||||||
|
<PagerContent>
|
||||||
|
<MudTablePager />
|
||||||
|
</PagerContent>
|
||||||
|
<NoRecordsContent>
|
||||||
|
<MudText>Keine Kategorien gefunden</MudText>
|
||||||
|
</NoRecordsContent>
|
||||||
|
</MudTable>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private bool _showInactive;
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
base.OnInitialized();
|
||||||
|
LoadCategories();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadCategories()
|
||||||
|
{
|
||||||
|
Dispatcher.Dispatch(new LoadCategoriesAction(_showInactive));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
base.OnParametersSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearError()
|
||||||
|
{
|
||||||
|
Dispatcher.Dispatch(new ClearCategoryErrorAction());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetTypeLabel(BookingCategoryType type) => type switch
|
||||||
|
{
|
||||||
|
BookingCategoryType.Income => "Einnahme",
|
||||||
|
BookingCategoryType.Expense => "Ausgabe",
|
||||||
|
_ => type.ToString()
|
||||||
|
};
|
||||||
|
|
||||||
|
private static Color GetTypeColor(BookingCategoryType type) => type switch
|
||||||
|
{
|
||||||
|
BookingCategoryType.Income => Color.Success,
|
||||||
|
BookingCategoryType.Expense => Color.Error,
|
||||||
|
_ => Color.Default
|
||||||
|
};
|
||||||
|
|
||||||
|
private async Task OpenCreateDialog()
|
||||||
|
{
|
||||||
|
var dialog = await DialogService.ShowAsync<CategoryDialog>("Neue Kategorie");
|
||||||
|
var result = await dialog.Result;
|
||||||
|
|
||||||
|
if (result != null && !result.Canceled && result.Data is CreateBookingCategoryDto dto)
|
||||||
|
{
|
||||||
|
Dispatcher.Dispatch(new CreateCategoryAction(dto));
|
||||||
|
Snackbar.Add("Kategorie wird erstellt...", Severity.Info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OpenEditDialog(BookingCategoryDto category)
|
||||||
|
{
|
||||||
|
var parameters = new DialogParameters
|
||||||
|
{
|
||||||
|
{ "Category", category }
|
||||||
|
};
|
||||||
|
|
||||||
|
var dialog = await DialogService.ShowAsync<CategoryDialog>("Kategorie bearbeiten", parameters);
|
||||||
|
var result = await dialog.Result;
|
||||||
|
|
||||||
|
if (result != null && !result.Canceled && result.Data is UpdateBookingCategoryDto dto)
|
||||||
|
{
|
||||||
|
Dispatcher.Dispatch(new UpdateCategoryAction(dto));
|
||||||
|
Snackbar.Add("Kategorie wird aktualisiert...", Severity.Info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ConfirmDelete(BookingCategoryDto category)
|
||||||
|
{
|
||||||
|
var parameters = new DialogParameters
|
||||||
|
{
|
||||||
|
{ "ContentText", $"Möchtest du \"{category.Name}\" wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden." },
|
||||||
|
{ "ButtonText", "Löschen" },
|
||||||
|
{ "Color", Color.Error }
|
||||||
|
};
|
||||||
|
|
||||||
|
var dialog = await DialogService.ShowAsync<Shared.ConfirmDialog>("Kategorie löschen", parameters);
|
||||||
|
var result = await dialog.Result;
|
||||||
|
|
||||||
|
if (result != null && !result.Canceled)
|
||||||
|
{
|
||||||
|
Dispatcher.Dispatch(new DeleteCategoryAction(category.Id));
|
||||||
|
Snackbar.Add("Kategorie wird gelöscht...", Severity.Info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,187 @@
|
||||||
|
@using Koogle.Application.DTOs
|
||||||
|
@using Koogle.Domain.Enums
|
||||||
|
@using MudBlazor.Utilities
|
||||||
|
|
||||||
|
<MudDialog>
|
||||||
|
<TitleContent>
|
||||||
|
<MudText Typo="Typo.h6">
|
||||||
|
@(IsEditMode ? "Kategorie bearbeiten" : "Neue Kategorie")
|
||||||
|
</MudText>
|
||||||
|
</TitleContent>
|
||||||
|
<DialogContent>
|
||||||
|
<MudForm @ref="_form" @bind-IsValid="_isValid">
|
||||||
|
<MudTextField @bind-Value="_name"
|
||||||
|
Label="Name"
|
||||||
|
Required="true"
|
||||||
|
RequiredError="Name ist erforderlich"
|
||||||
|
Immediate="true"
|
||||||
|
Class="mb-4" />
|
||||||
|
|
||||||
|
<MudTextField @bind-Value="_description"
|
||||||
|
Label="Beschreibung"
|
||||||
|
Lines="2"
|
||||||
|
Class="mb-4" />
|
||||||
|
|
||||||
|
@if (!IsEditMode)
|
||||||
|
{
|
||||||
|
<MudSelect T="BookingCategoryType" @bind-Value="_categoryType"
|
||||||
|
Label="Typ"
|
||||||
|
Required="true"
|
||||||
|
AnchorOrigin="Origin.BottomCenter"
|
||||||
|
Class="mb-4">
|
||||||
|
<MudSelectItem Value="BookingCategoryType.Income">
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.ArrowUpward" Color="Color.Success" Size="Size.Small" />
|
||||||
|
<MudText>Einnahme</MudText>
|
||||||
|
</MudStack>
|
||||||
|
</MudSelectItem>
|
||||||
|
<MudSelectItem Value="BookingCategoryType.Expense">
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.ArrowDownward" Color="Color.Error" Size="Size.Small" />
|
||||||
|
<MudText>Ausgabe</MudText>
|
||||||
|
</MudStack>
|
||||||
|
</MudSelectItem>
|
||||||
|
</MudSelect>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.body2" Color="Color.Secondary" Class="mb-4">
|
||||||
|
Typ: @GetTypeLabel(Category!.CategoryType)
|
||||||
|
</MudText>
|
||||||
|
}
|
||||||
|
|
||||||
|
<MudDivider Class="my-4" />
|
||||||
|
<MudText Typo="Typo.subtitle2" Class="mb-2">Darstellung</MudText>
|
||||||
|
|
||||||
|
<MudStack Row="true" Spacing="4" Class="mb-4">
|
||||||
|
<MudColorPicker @bind-Value="_color"
|
||||||
|
Label="Farbe"
|
||||||
|
DisableAlpha="true"
|
||||||
|
ColorPickerMode="ColorPickerMode.HEX"
|
||||||
|
Style="width: 150px;" />
|
||||||
|
@if (!string.IsNullOrWhiteSpace(_color?.Value))
|
||||||
|
{
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Circle" Style="@($"color: {_color.Value}; align-self: center;")" />
|
||||||
|
}
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
<MudSelect T="string" @bind-Value="_icon"
|
||||||
|
Label="Icon"
|
||||||
|
Clearable="true"
|
||||||
|
AnchorOrigin="Origin.BottomCenter"
|
||||||
|
Class="mb-4">
|
||||||
|
@foreach (var iconOption in IconOptions)
|
||||||
|
{
|
||||||
|
<MudSelectItem Value="@iconOption.Value">
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center">
|
||||||
|
<MudIcon Icon="@iconOption.Value" Size="Size.Small" />
|
||||||
|
<MudText>@iconOption.Label</MudText>
|
||||||
|
</MudStack>
|
||||||
|
</MudSelectItem>
|
||||||
|
}
|
||||||
|
</MudSelect>
|
||||||
|
|
||||||
|
@if (IsEditMode && !Category!.IsSystemCategory)
|
||||||
|
{
|
||||||
|
<MudDivider Class="my-4" />
|
||||||
|
<MudCheckBox @bind-Value="_isActive" Label="Aktiv" Color="Color.Primary" />
|
||||||
|
}
|
||||||
|
</MudForm>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<MudButton OnClick="Cancel">Abbrechen</MudButton>
|
||||||
|
<MudButton Color="Color.Primary" Variant="Variant.Filled" Disabled="!_isValid" OnClick="Submit">
|
||||||
|
@(IsEditMode ? "Speichern" : "Erstellen")
|
||||||
|
</MudButton>
|
||||||
|
</DialogActions>
|
||||||
|
</MudDialog>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[CascadingParameter]
|
||||||
|
private IMudDialogInstance? MudDialog { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public BookingCategoryDto? Category { get; set; }
|
||||||
|
|
||||||
|
private MudForm? _form;
|
||||||
|
private bool _isValid;
|
||||||
|
private string _name = "";
|
||||||
|
private string? _description;
|
||||||
|
private BookingCategoryType _categoryType = BookingCategoryType.Income;
|
||||||
|
private MudColor? _color;
|
||||||
|
private string? _icon;
|
||||||
|
private bool _isActive = true;
|
||||||
|
|
||||||
|
private bool IsEditMode => Category is not null;
|
||||||
|
|
||||||
|
private static readonly List<(string Label, string Value)> IconOptions =
|
||||||
|
[
|
||||||
|
("Geld", Icons.Material.Filled.AttachMoney),
|
||||||
|
("Strafe", Icons.Material.Filled.Gavel),
|
||||||
|
("Mitglied", Icons.Material.Filled.CardMembership),
|
||||||
|
("Korrektur", Icons.Material.Filled.Build),
|
||||||
|
("Saldo", Icons.Material.Filled.Balance),
|
||||||
|
("Kasse", Icons.Material.Filled.PointOfSale),
|
||||||
|
("Geschenk", Icons.Material.Filled.CardGiftcard),
|
||||||
|
("Einkauf", Icons.Material.Filled.ShoppingCart),
|
||||||
|
("Essen", Icons.Material.Filled.Restaurant),
|
||||||
|
("Getränk", Icons.Material.Filled.LocalBar),
|
||||||
|
("Miete", Icons.Material.Filled.Home),
|
||||||
|
("Sport", Icons.Material.Filled.SportsScore),
|
||||||
|
("Veranstaltung", Icons.Material.Filled.Event),
|
||||||
|
("Sonstiges", Icons.Material.Filled.MoreHoriz)
|
||||||
|
];
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
if (Category is not null)
|
||||||
|
{
|
||||||
|
_name = Category.Name;
|
||||||
|
_description = Category.Description;
|
||||||
|
_categoryType = Category.CategoryType;
|
||||||
|
_color = string.IsNullOrWhiteSpace(Category.Color) ? null : new MudColor(Category.Color);
|
||||||
|
_icon = Category.Icon;
|
||||||
|
_isActive = Category.IsActive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetTypeLabel(BookingCategoryType type) => type switch
|
||||||
|
{
|
||||||
|
BookingCategoryType.Income => "Einnahme",
|
||||||
|
BookingCategoryType.Expense => "Ausgabe",
|
||||||
|
_ => type.ToString()
|
||||||
|
};
|
||||||
|
|
||||||
|
private void Cancel() => MudDialog?.Cancel();
|
||||||
|
|
||||||
|
private void Submit()
|
||||||
|
{
|
||||||
|
if (!_isValid) return;
|
||||||
|
|
||||||
|
if (IsEditMode)
|
||||||
|
{
|
||||||
|
var dto = new UpdateBookingCategoryDto
|
||||||
|
{
|
||||||
|
Id = Category!.Id,
|
||||||
|
Name = _name,
|
||||||
|
Description = _description,
|
||||||
|
Color = _color?.Value,
|
||||||
|
Icon = _icon,
|
||||||
|
IsActive = Category.IsSystemCategory ? true : _isActive
|
||||||
|
};
|
||||||
|
MudDialog?.Close(DialogResult.Ok(dto));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var dto = new CreateBookingCategoryDto
|
||||||
|
{
|
||||||
|
Name = _name,
|
||||||
|
Description = _description,
|
||||||
|
CategoryType = _categoryType,
|
||||||
|
Color = _color?.Value,
|
||||||
|
Icon = _icon
|
||||||
|
};
|
||||||
|
MudDialog?.Close(DialogResult.Ok(dto));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue