@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) { }
Date Day Work Units Calendar Events Counted Preview Off Gross € Net €
@row.Date.ToString("dd") @row.Date.ToString("ddd") @if (row.Entry?.WorkUnits.Count > 0) { @foreach (var unit in row.Entry.WorkUnits) {
@unit.Label: @FormatTimeRange(unit.StartTime, unit.EndTime) (@FormatHours(unit.ManualWorkedHours)@(unit.IsPreview ? ", preview" : ""))
} } else { }
@if (row.Entry?.CalendarEvents.Count > 0) { @foreach (var calendarEvent in row.Entry.CalendarEvents) {
@CalendarEventFormatter.GetEventTypeName(calendarEvent.EventType): @CalendarEventFormatter.GetDisplayDescription(calendarEvent)
} } else { }
@FormatHours(GetCountedHours(row)) @FormatHours(GetPreviewHours(row)) @FormatHours(GetHoursOff(row)) @GetGrossIncome(row).ToString("N2") @GetNetIncome(row).ToString("N2")
} @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 "grid-row-weekend"; if (row.Entry is null) return string.Empty; if (row.Entry.CalendarEvents.Any(entry => entry.EventType == CalendarEventType.Holiday)) { return "grid-row-holiday"; } if (row.Entry.CalendarEvents.Any(entry => entry.EventType == CalendarEventType.Closure)) { return "grid-row-closure"; } if (row.Entry.CalendarEvents.Any(entry => entry.EventType == CalendarEventType.Illness)) { return "grid-row-illness"; } if (row.Entry.CalendarEvents.Any(entry => entry.EventType == CalendarEventType.DayOff)) { return "grid-row-dayoff"; } if (row.Entry.WorkUnits.Any(entry => entry.Location == WorkUnitLocation.Home)) { return "grid-row-home"; } return string.Empty; } private static decimal GetCountedHours(CalendarDayRow row) { return row.Entry?.WorkUnits.Where(unit => !unit.IsPreview).Sum(unit => unit.ManualWorkedHours) ?? 0m; } private static decimal GetPreviewHours(CalendarDayRow row) { return row.Entry?.WorkUnits.Where(unit => unit.IsPreview).Sum(unit => unit.ManualWorkedHours) ?? 0m; } private static decimal GetHoursOff(CalendarDayRow row) { if (row.Entry is null || row.Entry.WorkUnits.Count == 0) { return 0m; } var countedUnits = row.Entry.WorkUnits.Where(unit => !unit.IsPreview).ToList(); if (countedUnits.Count == 0) { return 0m; } var standardHours = countedUnits[0].CoeffSnapshot.StandardWorkHoursPerDay; return Math.Max(0m, standardHours - countedUnits.Sum(unit => unit.ManualWorkedHours)); } private static decimal GetGrossIncome(CalendarDayRow row) { return row.Entry?.WorkUnits.Where(unit => !unit.IsPreview).Sum(unit => unit.GrossIncome) ?? 0m; } private static decimal GetNetIncome(CalendarDayRow row) { return row.Entry?.WorkUnits.Where(unit => !unit.IsPreview).Sum(unit => unit.NetIncome) ?? 0m; } private static string FormatTimeRange(TimeOnly? startTime, TimeOnly? endTime) { if (startTime.HasValue && endTime.HasValue) { return $"{startTime:HH:mm}-{endTime:HH:mm}"; } if (startTime.HasValue) { return startTime.Value.ToString("HH:mm"); } return "No time range"; } private static string FormatHours(decimal value) { return DurationFormatter.FormatHours(value); } private sealed class CalendarDayRow { public DateOnly Date { get; set; } public bool IsWeekend { get; set; } public bool IsFestivity { get; set; } public WorkDayDocument? Entry { get; set; } } }