Add evaluation components (F2)
- DayEvaluationComponent: shows day expense summary per person - PersonEvaluationComponent: shows person expense summary + details 🤖 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
1cbd4c17e7
commit
bc99bc25db
|
|
@ -0,0 +1,186 @@
|
||||||
|
@using Koogle.Application.DTOs
|
||||||
|
@using Koogle.Application.Interfaces
|
||||||
|
@using Koogle.Domain.Enums
|
||||||
|
|
||||||
|
@inject IPersonExpenseService PersonExpenseService
|
||||||
|
@inject ISnackbar Snackbar
|
||||||
|
@inject NavigationManager NavigationManager
|
||||||
|
|
||||||
|
@if (_isLoading)
|
||||||
|
{
|
||||||
|
<MudProgressLinear Indeterminate="true" Color="Color.Primary" />
|
||||||
|
}
|
||||||
|
else if (_error is not null)
|
||||||
|
{
|
||||||
|
<MudAlert Severity="Severity.Error" Class="mb-4">@_error</MudAlert>
|
||||||
|
}
|
||||||
|
else if (_evaluation is not null)
|
||||||
|
{
|
||||||
|
<MudCard Elevation="2">
|
||||||
|
<MudCardHeader>
|
||||||
|
<CardHeaderContent>
|
||||||
|
<MudText Typo="Typo.h6">Auswertung: @_evaluation.PostDate.ToString("dd.MM.yyyy")</MudText>
|
||||||
|
</CardHeaderContent>
|
||||||
|
<CardHeaderActions>
|
||||||
|
<MudChip T="string" Size="Size.Small" Color="GetStatusColor(_evaluation.Status)">
|
||||||
|
@GetStatusLabel(_evaluation.Status)
|
||||||
|
</MudChip>
|
||||||
|
</CardHeaderActions>
|
||||||
|
</MudCardHeader>
|
||||||
|
<MudCardContent>
|
||||||
|
<!-- Summary -->
|
||||||
|
<div class="d-flex justify-space-between mb-4">
|
||||||
|
<MudText Typo="Typo.body1">
|
||||||
|
<strong>Gesamt:</strong> @_evaluation.TotalAmount.ToString("C")
|
||||||
|
</MudText>
|
||||||
|
<MudText Typo="Typo.body2" Color="Color.Secondary">
|
||||||
|
@_evaluation.ExpenseCount Strafen
|
||||||
|
</MudText>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (_evaluation.PersonEvaluations.Count == 0)
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.body2" Color="Color.Secondary">Keine Strafen erfasst</MudText>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudTable Items="_evaluation.PersonEvaluations" Dense="true" Hover="true">
|
||||||
|
<HeaderContent>
|
||||||
|
<MudTh>Person</MudTh>
|
||||||
|
<MudTh Style="text-align: right">Offen</MudTh>
|
||||||
|
<MudTh Style="text-align: right">Bezahlt</MudTh>
|
||||||
|
<MudTh Style="text-align: right">Gesamt</MudTh>
|
||||||
|
<MudTh Style="text-align: center">Anzahl</MudTh>
|
||||||
|
</HeaderContent>
|
||||||
|
<RowTemplate>
|
||||||
|
<MudTd DataLabel="Person">
|
||||||
|
@if (ShowPersonLinks)
|
||||||
|
{
|
||||||
|
<MudLink OnClick="@(() => NavigateToPersonDetails(context.PersonId))">
|
||||||
|
@context.PersonName
|
||||||
|
</MudLink>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@context.PersonName
|
||||||
|
}
|
||||||
|
</MudTd>
|
||||||
|
<MudTd DataLabel="Offen" Style="text-align: right">
|
||||||
|
@if (context.OpenAmount > 0)
|
||||||
|
{
|
||||||
|
<MudText Color="Color.Warning">@context.OpenAmount.ToString("C")</MudText>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudText>-</MudText>
|
||||||
|
}
|
||||||
|
</MudTd>
|
||||||
|
<MudTd DataLabel="Bezahlt" Style="text-align: right">
|
||||||
|
@if (context.PaidAmount > 0)
|
||||||
|
{
|
||||||
|
<MudText Color="Color.Success">@context.PaidAmount.ToString("C")</MudText>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudText>-</MudText>
|
||||||
|
}
|
||||||
|
</MudTd>
|
||||||
|
<MudTd DataLabel="Gesamt" Style="text-align: right">
|
||||||
|
<strong>@context.TotalAmount.ToString("C")</strong>
|
||||||
|
</MudTd>
|
||||||
|
<MudTd DataLabel="Anzahl" Style="text-align: center">
|
||||||
|
@context.ExpenseCount
|
||||||
|
</MudTd>
|
||||||
|
</RowTemplate>
|
||||||
|
</MudTable>
|
||||||
|
}
|
||||||
|
</MudCardContent>
|
||||||
|
</MudCard>
|
||||||
|
}
|
||||||
|
|
||||||
|
@code {
|
||||||
|
/// <summary>
|
||||||
|
/// The unique identifier of the day to evaluate.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Guid DayId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to show links to person details.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool ShowPersonLinks { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event callback when data is loaded.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback<DayEvaluationDto> OnLoaded { get; set; }
|
||||||
|
|
||||||
|
private DayEvaluationDto? _evaluation;
|
||||||
|
private bool _isLoading = true;
|
||||||
|
private string? _error;
|
||||||
|
|
||||||
|
protected override async Task OnParametersSetAsync()
|
||||||
|
{
|
||||||
|
if (DayId != Guid.Empty)
|
||||||
|
{
|
||||||
|
await LoadEvaluationAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reloads the evaluation data.
|
||||||
|
/// </summary>
|
||||||
|
public async Task RefreshAsync()
|
||||||
|
{
|
||||||
|
await LoadEvaluationAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadEvaluationAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_isLoading = true;
|
||||||
|
_error = null;
|
||||||
|
_evaluation = await PersonExpenseService.GetDayEvaluationAsync(DayId);
|
||||||
|
|
||||||
|
if (OnLoaded.HasDelegate)
|
||||||
|
{
|
||||||
|
await OnLoaded.InvokeAsync(_evaluation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_error = $"Fehler beim Laden der Auswertung: {ex.Message}";
|
||||||
|
Snackbar.Add(_error, Severity.Error);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 void NavigateToPersonDetails(Guid personId)
|
||||||
|
{
|
||||||
|
NavigationManager.NavigateTo($"/persons/{personId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,190 @@
|
||||||
|
@using Koogle.Application.DTOs
|
||||||
|
@using Koogle.Application.Interfaces
|
||||||
|
@using Koogle.Domain.Enums
|
||||||
|
|
||||||
|
@inject IPersonExpenseService PersonExpenseService
|
||||||
|
@inject IPersonService PersonService
|
||||||
|
@inject ISnackbar Snackbar
|
||||||
|
@inject NavigationManager NavigationManager
|
||||||
|
|
||||||
|
@if (_isLoading)
|
||||||
|
{
|
||||||
|
<MudProgressLinear Indeterminate="true" Color="Color.Primary" />
|
||||||
|
}
|
||||||
|
else if (_error is not null)
|
||||||
|
{
|
||||||
|
<MudAlert Severity="Severity.Error" Class="mb-4">@_error</MudAlert>
|
||||||
|
}
|
||||||
|
else if (_evaluation is not null)
|
||||||
|
{
|
||||||
|
<MudCard Elevation="2">
|
||||||
|
<MudCardHeader>
|
||||||
|
<CardHeaderContent>
|
||||||
|
<MudText Typo="Typo.h6">Auswertung: @_evaluation.PersonName</MudText>
|
||||||
|
</CardHeaderContent>
|
||||||
|
</MudCardHeader>
|
||||||
|
<MudCardContent>
|
||||||
|
<!-- Summary Cards -->
|
||||||
|
<MudGrid Class="mb-4">
|
||||||
|
<MudItem xs="6" sm="3">
|
||||||
|
<div class="d-flex flex-column align-center">
|
||||||
|
<MudText Typo="Typo.h5" Color="Color.Warning">@_evaluation.TotalOpenAmount.ToString("C")</MudText>
|
||||||
|
<MudText Typo="Typo.caption" Color="Color.Secondary">Offen</MudText>
|
||||||
|
</div>
|
||||||
|
</MudItem>
|
||||||
|
<MudItem xs="6" sm="3">
|
||||||
|
<div class="d-flex flex-column align-center">
|
||||||
|
<MudText Typo="Typo.h5" Color="Color.Success">@_evaluation.TotalPaidAmount.ToString("C")</MudText>
|
||||||
|
<MudText Typo="Typo.caption" Color="Color.Secondary">Bezahlt</MudText>
|
||||||
|
</div>
|
||||||
|
</MudItem>
|
||||||
|
<MudItem xs="6" sm="3">
|
||||||
|
<div class="d-flex flex-column align-center">
|
||||||
|
<MudText Typo="Typo.h5">@_evaluation.TotalExpenseCount</MudText>
|
||||||
|
<MudText Typo="Typo.caption" Color="Color.Secondary">Strafen</MudText>
|
||||||
|
</div>
|
||||||
|
</MudItem>
|
||||||
|
<MudItem xs="6" sm="3">
|
||||||
|
<div class="d-flex flex-column align-center">
|
||||||
|
<MudText Typo="Typo.h5">@_evaluation.DaysParticipated</MudText>
|
||||||
|
<MudText Typo="Typo.caption" Color="Color.Secondary">Teilnahmen</MudText>
|
||||||
|
</div>
|
||||||
|
</MudItem>
|
||||||
|
</MudGrid>
|
||||||
|
|
||||||
|
@if (ShowExpenseDetails && _expenses.Count > 0)
|
||||||
|
{
|
||||||
|
<MudDivider Class="my-4" />
|
||||||
|
<MudText Typo="Typo.subtitle1" Class="mb-2">Strafenliste</MudText>
|
||||||
|
<MudTable Items="_expenses" Dense="true" Hover="true">
|
||||||
|
<HeaderContent>
|
||||||
|
<MudTh>Datum</MudTh>
|
||||||
|
<MudTh>Strafe</MudTh>
|
||||||
|
<MudTh Style="text-align: right">Betrag</MudTh>
|
||||||
|
<MudTh Style="text-align: center">Status</MudTh>
|
||||||
|
</HeaderContent>
|
||||||
|
<RowTemplate>
|
||||||
|
<MudTd DataLabel="Datum">
|
||||||
|
@if (ShowDayLinks)
|
||||||
|
{
|
||||||
|
<MudLink OnClick="@(() => NavigateToDayDetails(context.DayId))">
|
||||||
|
@context.DayPostDate.ToString("dd.MM.yyyy")
|
||||||
|
</MudLink>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@context.DayPostDate.ToString("dd.MM.yyyy")
|
||||||
|
}
|
||||||
|
</MudTd>
|
||||||
|
<MudTd DataLabel="Strafe">@context.Name</MudTd>
|
||||||
|
<MudTd DataLabel="Betrag" Style="text-align: right">@context.Price.ToString("C")</MudTd>
|
||||||
|
<MudTd DataLabel="Status" Style="text-align: center">
|
||||||
|
<MudChip T="string" Size="Size.Small" Color="GetExpenseStatusColor(context.PersonExpenseStatus)">
|
||||||
|
@GetExpenseStatusLabel(context.PersonExpenseStatus)
|
||||||
|
</MudChip>
|
||||||
|
</MudTd>
|
||||||
|
</RowTemplate>
|
||||||
|
<PagerContent>
|
||||||
|
<MudTablePager PageSizeOptions="new[] { 10, 25, 50 }" />
|
||||||
|
</PagerContent>
|
||||||
|
</MudTable>
|
||||||
|
}
|
||||||
|
</MudCardContent>
|
||||||
|
</MudCard>
|
||||||
|
}
|
||||||
|
|
||||||
|
@code {
|
||||||
|
/// <summary>
|
||||||
|
/// The unique identifier of the person to evaluate.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Guid PersonId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to show the detailed expense list.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool ShowExpenseDetails { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to show links to day details.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool ShowDayLinks { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event callback when data is loaded.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback<PersonEvaluationSummaryDto> OnLoaded { get; set; }
|
||||||
|
|
||||||
|
private PersonEvaluationSummaryDto? _evaluation;
|
||||||
|
private List<PersonExpenseDto> _expenses = [];
|
||||||
|
private bool _isLoading = true;
|
||||||
|
private string? _error;
|
||||||
|
|
||||||
|
protected override async Task OnParametersSetAsync()
|
||||||
|
{
|
||||||
|
if (PersonId != Guid.Empty)
|
||||||
|
{
|
||||||
|
await LoadEvaluationAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reloads the evaluation data.
|
||||||
|
/// </summary>
|
||||||
|
public async Task RefreshAsync()
|
||||||
|
{
|
||||||
|
await LoadEvaluationAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadEvaluationAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_isLoading = true;
|
||||||
|
_error = null;
|
||||||
|
|
||||||
|
_evaluation = await PersonExpenseService.GetPersonEvaluationAsync(PersonId);
|
||||||
|
|
||||||
|
if (ShowExpenseDetails)
|
||||||
|
{
|
||||||
|
_expenses = await PersonExpenseService.GetByPersonIdAsync(PersonId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OnLoaded.HasDelegate)
|
||||||
|
{
|
||||||
|
await OnLoaded.InvokeAsync(_evaluation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_error = $"Fehler beim Laden der Auswertung: {ex.Message}";
|
||||||
|
Snackbar.Add(_error, Severity.Error);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetExpenseStatusLabel(PersonExpenseStatus status) => status switch
|
||||||
|
{
|
||||||
|
PersonExpenseStatus.Open => "Offen",
|
||||||
|
PersonExpenseStatus.Done => "Bezahlt",
|
||||||
|
_ => status.ToString()
|
||||||
|
};
|
||||||
|
|
||||||
|
private static Color GetExpenseStatusColor(PersonExpenseStatus status) => status switch
|
||||||
|
{
|
||||||
|
PersonExpenseStatus.Open => Color.Warning,
|
||||||
|
PersonExpenseStatus.Done => Color.Success,
|
||||||
|
_ => Color.Default
|
||||||
|
};
|
||||||
|
|
||||||
|
private void NavigateToDayDetails(Guid dayId)
|
||||||
|
{
|
||||||
|
NavigationManager.NavigateTo($"/days/{dayId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue