@page "/grid" @page "/grid/{YearMonth}" @attribute [Authorize] @rendermode InteractiveServer @inject IWorkDayService WorkDayService @inject IItalianFestivitySource FestivitySource @inject NavigationManager Navigation Grid View

Grid View

@currentDate.ToString("MMMM yyyy")

@if (loading) {

Loading...

} else {
@foreach (var row in calendarDays) { @if (row.Entry is not null) { } else { } }
Date Day Type Start Projected Actual Worked Extra Off Gross € Net €
@row.Date.ToString("dd") @row.Date.ToString("ddd")@row.Entry.DayType @(row.Entry.StartTime?.ToString("HH:mm") ?? "") @(row.Entry.ProjectedExitTime?.ToString("HH:mm") ?? "") @(row.Entry.ActualExitTime?.ToString("HH:mm") ?? "") @row.Entry.WorkedHoursFinal.ToString("N2") @FormatDelta(row.Entry.ExtraHoursDelta) @row.Entry.HoursOff.ToString("N2") @row.Entry.GrossIncome.ToString("N2") @row.Entry.NetIncome.ToString("N2") Edit
} @code { [Parameter] public string? YearMonth { get; set; } private DateOnly currentDate; private bool loading = true; private List calendarDays = []; private IReadOnlyCollection festivities = []; protected override async Task OnInitializedAsync() { if (!string.IsNullOrEmpty(YearMonth) && DateTime.TryParseExact(YearMonth, "yyyy-MM", null, System.Globalization.DateTimeStyles.None, out var parsed)) { currentDate = new DateOnly(parsed.Year, parsed.Month, 1); } else { currentDate = new DateOnly(DateTime.Today.Year, DateTime.Today.Month, 1); } await LoadMonth(); } private async Task LoadMonth() { loading = true; festivities = FestivitySource.GetFestivities(currentDate.Year); var from = currentDate; var to = currentDate.AddMonths(1).AddDays(-1); var entries = await WorkDayService.GetRangeAsync(from, to); var lookup = entries.ToDictionary(e => e.Date); calendarDays = []; for (var d = from; d <= to; d = d.AddDays(1)) { calendarDays.Add(new CalendarDayRow { Date = d, IsWeekend = d.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday, IsFestivity = festivities.Contains(d), Entry = lookup.GetValueOrDefault(d) }); } loading = false; } private async Task PreviousMonth() { currentDate = currentDate.AddMonths(-1); await LoadMonth(); } private async Task NextMonth() { currentDate = currentDate.AddMonths(1); await LoadMonth(); } private string GetRowClass(CalendarDayRow row) { if (row.IsWeekend || row.IsFestivity) return "table-danger"; if (row.Entry is null) return ""; return row.Entry.DayType switch { DayType.Closure => "table-warning", DayType.Illness => "table-info", DayType.DayOff => "table-secondary", DayType.Holiday => "table-success", DayType.Home => "table-light", _ => "" }; } private static string FormatDelta(decimal d) => d switch { > 0 => $"+{d:N2}", < 0 => d.ToString("N2"), _ => "—" }; private sealed class CalendarDayRow { public DateOnly Date { get; set; } public bool IsWeekend { get; set; } public bool IsFestivity { get; set; } public WorkDayDocument? Entry { get; set; } } }