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