@page "/yearly-summary" @page "/yearly-summary/{Year:int}" @attribute [Authorize] @rendermode InteractiveServer @using System.Globalization @inject global::WorkTracker.Services.WorkDays.IWorkDayService WorkDayService @inject IJSRuntime JS @inject NavigationManager Navigation Yearly Summary

Yearly Summary

@currentYear

Monthly Summary
@if (loading) {

Loading...

} else {
@foreach (var month in summaryByMonth) { }
Month Working Days Worked Hours Hours Off Gross Income Net Income Office Days Home Days Holidays Closure Days Days Off Sick Days
@GetMonthLabel(month) @FormatCount(month.TotalWorkingDays) @FormatHoursCell(month.TotalWorkedHours) @FormatHoursCell(month.TotalHoursOff) @FormatCurrency(month.TotalGrossIncome) @FormatCurrency(month.TotalNetIncome) @FormatCount(month.OfficeDays) @FormatCount(month.HomeDays) @FormatCount(month.HolidayDays) @FormatCount(month.ClosureDays) @FormatCount(month.DaysOff) @FormatCount(month.SickDays)
Total @FormatCount(Totals.TotalWorkingDays) @FormatHoursCell(Totals.TotalWorkedHours) @FormatHoursCell(Totals.TotalHoursOff) @FormatCurrency(Totals.TotalGrossIncome) @FormatCurrency(Totals.TotalNetIncome) @FormatCount(Totals.OfficeDays) @FormatCount(Totals.HomeDays) @FormatCount(Totals.HolidayDays) @FormatCount(Totals.ClosureDays) @FormatCount(Totals.DaysOff) @FormatCount(Totals.SickDays)
} @code { [Parameter] public int? Year { get; set; } private static readonly CultureInfo ItalianCulture = CultureInfo.GetCultureInfo("it-IT"); private const string IncludePreviewPreferenceKey = "worktracker.includePreviewWorkUnits"; private int currentYear; private bool loading = true; private bool includePreview; private IReadOnlyList summaryByMonth = []; private global::WorkTracker.Domain.MonthlySummaryModel Totals => new() { Year = currentYear, TotalWorkingDays = summaryByMonth.Sum(item => item.TotalWorkingDays), CountedWorkUnits = summaryByMonth.Sum(item => item.CountedWorkUnits), TotalWorkedHours = summaryByMonth.Sum(item => item.TotalWorkedHours), TotalPreviewWorkedHours = summaryByMonth.Sum(item => item.TotalPreviewWorkedHours), PreviewWorkUnits = summaryByMonth.Sum(item => item.PreviewWorkUnits), TotalHoursOff = summaryByMonth.Sum(item => item.TotalHoursOff), TotalGrossIncome = summaryByMonth.Sum(item => item.TotalGrossIncome), TotalNetIncome = summaryByMonth.Sum(item => item.TotalNetIncome), OfficeDays = summaryByMonth.Sum(item => item.OfficeDays), HomeDays = summaryByMonth.Sum(item => item.HomeDays), HolidayDays = summaryByMonth.Sum(item => item.HolidayDays), ClosureDays = summaryByMonth.Sum(item => item.ClosureDays), DaysOff = summaryByMonth.Sum(item => item.DaysOff), SickDays = summaryByMonth.Sum(item => item.SickDays) }; protected override async Task OnParametersSetAsync() { currentYear = Year ?? DateTime.Today.Year; await LoadSummary(); } protected override async Task OnAfterRenderAsync(bool firstRender) { if (!firstRender) { return; } var savedIncludePreview = await JS.InvokeAsync("workTrackerPreferences.getBool", IncludePreviewPreferenceKey); if (savedIncludePreview.HasValue && savedIncludePreview.Value != includePreview) { includePreview = savedIncludePreview.Value; await LoadSummary(); await InvokeAsync(StateHasChanged); } } private async Task LoadSummary() { loading = true; summaryByMonth = await WorkDayService.GetYearlySummaryAsync(currentYear, includePreview); loading = false; } private async Task OnIncludePreviewChanged(ChangeEventArgs e) { includePreview = e.Value is bool value && value; await JS.InvokeVoidAsync("workTrackerPreferences.setBool", IncludePreviewPreferenceKey, includePreview); await LoadSummary(); } private Task PreviousYear() { currentYear--; Navigation.NavigateTo($"/yearly-summary/{currentYear}"); return Task.CompletedTask; } private Task NextYear() { currentYear++; Navigation.NavigateTo($"/yearly-summary/{currentYear}"); return Task.CompletedTask; } private static string GetMonthLabel(global::WorkTracker.Domain.MonthlySummaryModel month) { return new DateOnly(month.Year, month.Month, 1).ToString("MMMM", ItalianCulture); } private static string GetYearMonth(global::WorkTracker.Domain.MonthlySummaryModel month) { return $"{month.Year:D4}-{month.Month:D2}"; } private static string FormatHours(decimal value) { return DurationFormatter.FormatHours(value); } private static string FormatHoursCell(decimal value) { return DurationFormatter.FormatHours(value, blankWhenZero: true); } private static string FormatCount(int value) { return value == 0 ? string.Empty : value.ToString(CultureInfo.InvariantCulture); } private static string FormatCurrency(decimal value) { return value == 0m ? string.Empty : $"€{value.ToString("N2", ItalianCulture)}"; } }