WorkTracker/Components/Pages/MonthlySummary.razor
Marco cab549ab3a Refactor AppSettingsDocument and CoeffSnapshotDocument: Remove LunchBreakHours property
Add CalendarEventDocument and CalendarEventType enum for event management

Update WorkDayDocument to include WorkUnitDocument and CalendarEventDocument lists

Enhance CouchbaseLiteWorkDayService with methods for managing WorkUnit and CalendarEvent

Revise MonthlySummaryModel to track preview worked hours and counted work units

Improve CSS for calendar view, including responsive design and new item styles
2026-04-20 16:11:27 +02:00

199 lines
7.2 KiB
Text

@page "/summary"
@page "/summary/{YearMonth}"
@attribute [Authorize]
@rendermode InteractiveServer
@inject IWorkDayService WorkDayService
<PageTitle>Monthly Summary</PageTitle>
<h1>Monthly Summary</h1>
<div class="d-flex align-items-center gap-2 mb-3">
<button class="btn btn-outline-secondary btn-sm" @onclick="PreviousMonth">&laquo; Prev</button>
<h2 class="h5 mb-0">@currentMonth.ToString("MMMM yyyy")</h2>
<button class="btn btn-outline-secondary btn-sm" @onclick="NextMonth">Next &raquo;</button>
</div>
<div class="form-check mb-3">
<input id="include-preview" type="checkbox" class="form-check-input" checked="@includePreview" @onchange="OnIncludePreviewChanged" />
<label class="form-check-label" for="include-preview">Include preview work units in totals</label>
</div>
@if (loading)
{
<p><em>Loading...</em></p>
}
else if (summary is not null)
{
<div class="row g-3">
<div class="col-6 col-md-4 col-xl-3">
<div class="card text-center h-100">
<div class="card-body">
<div class="text-muted small">Working Days</div>
<div class="fs-3 fw-bold">@summary.TotalWorkingDays</div>
</div>
</div>
</div>
<div class="col-6 col-md-4 col-xl-3">
<div class="card text-center h-100">
<div class="card-body">
<div class="text-muted small">Counted Work Units</div>
<div class="fs-3 fw-bold">@summary.CountedWorkUnits</div>
</div>
</div>
</div>
<div class="col-6 col-md-4 col-xl-3">
<div class="card text-center h-100">
<div class="card-body">
<div class="text-muted small">Total Worked Hours</div>
<div class="fs-3 fw-bold">@FormatHours(summary.TotalWorkedHours)</div>
</div>
</div>
</div>
<div class="col-6 col-md-4 col-xl-3">
<div class="card text-center h-100">
<div class="card-body">
<div class="text-muted small">Preview Hours</div>
<div class="fs-3 fw-bold">@FormatHours(summary.TotalPreviewWorkedHours)</div>
</div>
</div>
</div>
<div class="col-6 col-md-4 col-xl-3">
<div class="card text-center h-100">
<div class="card-body">
<div class="text-muted small">Preview Units</div>
<div class="fs-3 fw-bold">@summary.PreviewWorkUnits</div>
</div>
</div>
</div>
<div class="col-6 col-md-4 col-xl-3">
<div class="card text-center h-100">
<div class="card-body">
<div class="text-muted small">Hours Off</div>
<div class="fs-3 fw-bold">@FormatHours(summary.TotalHoursOff)</div>
</div>
</div>
</div>
<div class="col-6 col-md-4 col-xl-3">
<div class="card text-center h-100 border-success">
<div class="card-body">
<div class="text-muted small">Gross Income</div>
<div class="fs-3 fw-bold text-success">€@summary.TotalGrossIncome.ToString("N2")</div>
</div>
</div>
</div>
<div class="col-6 col-md-4 col-xl-3">
<div class="card text-center h-100 border-primary">
<div class="card-body">
<div class="text-muted small">Net Income</div>
<div class="fs-3 fw-bold text-primary">€@summary.TotalNetIncome.ToString("N2")</div>
</div>
</div>
</div>
<div class="col-6 col-md-4 col-xl-3">
<div class="card text-center h-100">
<div class="card-body">
<div class="text-muted small">Office Days</div>
<div class="fs-3 fw-bold">@summary.OfficeDays</div>
</div>
</div>
</div>
<div class="col-6 col-md-4 col-xl-3">
<div class="card text-center h-100">
<div class="card-body">
<div class="text-muted small">Home Days</div>
<div class="fs-3 fw-bold">@summary.HomeDays</div>
</div>
</div>
</div>
<div class="col-6 col-md-4 col-xl-3">
<div class="card text-center h-100">
<div class="card-body">
<div class="text-muted small">Holidays</div>
<div class="fs-3 fw-bold">@summary.HolidayDays</div>
</div>
</div>
</div>
<div class="col-6 col-md-4 col-xl-3">
<div class="card text-center h-100">
<div class="card-body">
<div class="text-muted small">Closure Days</div>
<div class="fs-3 fw-bold">@summary.ClosureDays</div>
</div>
</div>
</div>
<div class="col-6 col-md-4 col-xl-3">
<div class="card text-center h-100">
<div class="card-body">
<div class="text-muted small">Days Off</div>
<div class="fs-3 fw-bold">@summary.DaysOff</div>
</div>
</div>
</div>
<div class="col-6 col-md-4 col-xl-3">
<div class="card text-center h-100">
<div class="card-body">
<div class="text-muted small">Sick Days</div>
<div class="fs-3 fw-bold">@summary.SickDays</div>
</div>
</div>
</div>
</div>
}
@code {
[Parameter] public string? YearMonth { get; set; }
private DateOnly currentMonth;
private bool loading = true;
private bool includePreview;
private MonthlySummaryModel? summary;
protected override async Task OnInitializedAsync()
{
if (!string.IsNullOrEmpty(YearMonth) && DateTime.TryParseExact(YearMonth, "yyyy-MM", null, System.Globalization.DateTimeStyles.None, out var parsed))
{
currentMonth = new DateOnly(parsed.Year, parsed.Month, 1);
}
else
{
currentMonth = new DateOnly(DateTime.Today.Year, DateTime.Today.Month, 1);
}
await LoadSummary();
}
private async Task OnIncludePreviewChanged(ChangeEventArgs e)
{
includePreview = e.Value is bool value && value;
await LoadSummary();
}
private async Task LoadSummary()
{
loading = true;
summary = await WorkDayService.GetMonthlySummaryAsync(currentMonth.Year, currentMonth.Month, includePreview);
loading = false;
}
private async Task PreviousMonth()
{
currentMonth = currentMonth.AddMonths(-1);
await LoadSummary();
}
private async Task NextMonth()
{
currentMonth = currentMonth.AddMonths(1);
await LoadSummary();
}
private static string FormatHours(decimal value)
{
var totalMinutes = (int)Math.Round(value * 60m, MidpointRounding.AwayFromZero);
var hours = totalMinutes / 60;
var minutes = totalMinutes % 60;
return $"{hours:00}:{minutes:00}";
}
}