Files
TaskHybrid/Template.Shared/Components/Pages/Calendar.razor
2025-06-11 16:38:36 +02:00

296 lines
9.2 KiB
Plaintext

@page "/Calendar"
@using Template.Shared.Core.Dto
@using Template.Shared.Core.Interface
@using Template.Shared.Components.Layout
@using Template.Shared.Components.SingleElements
@using Template.Shared.Components.Layout.Spinner
@inject IManageDataService manageData
@inject IJSRuntime JS
<HeaderLayout Title="@CurrentMonth.ToString("MMMM yyyy", new System.Globalization.CultureInfo("it-IT")).FirstCharToUpper()"
ShowFilter="true"
ShowCalendarToggle="true"
OnCalendarToggle="ToggleExpanded"/>
<div @ref="weekSliderRef" class="week-slider @(Expanded ? "expanded" : "") @(SliderAnimation)">
@if (Expanded)
{
<!-- Vista mensile -->
@foreach (var nomeGiorno in GiorniSettimana)
{
<div class="week-day">
<div>@nomeGiorno</div>
</div>
}
@foreach (var unused in Enumerable.Range(0, StartOffset))
{
<div class="day" style="visibility: hidden"></div>
}
@for (var d = 1; d <= DaysInMonth; d++)
{
var day = new DateTime(CurrentMonth.Year, CurrentMonth.Month, d);
var isSelected = IsSameDay(day, SelectedDate);
var isToday = IsSameDay(day, DateTime.Today);
var events = GetEventsForDay(day);
<div class="day @(isSelected ? "selected" : (isToday ? "today" : ""))"
@onclick="() => SelezionaDataDalMese(day)">
<div>@d</div>
@if (events.Any())
{
<div class="event-dot-container" style="margin-top: 2px;">
@foreach (var cat in events.Select(x => x.Category).Distinct())
{
<div class="event-dot @cat.ConvertToHumanReadable()" title="@cat.ConvertToHumanReadable()"></div>
}
</div>
}
</div>
}
@foreach (var unused in Enumerable.Range(0, EndOffset))
{
<div class="day" style="visibility: hidden"></div>
}
}
else
{
<!-- Vista settimanale -->
@foreach (var day in DaysOfWeek)
{
var isSelected = IsSameDay(day, SelectedDate);
var isToday = IsSameDay(day, DateTime.Today);
<div class="week-day">
<div>@day.ToString("ddd", new System.Globalization.CultureInfo("it-IT"))</div>
<div class="day @(isSelected ? "selected" : (isToday ? "today" : ""))"
@onclick="() => SelezionaData(day)">
<div>@day.Day</div>
@if (GetEventsForDay(day).Any())
{
<div class="event-dot-container" style="margin-top: 2px;">
@foreach (var cat in GetEventsForDay(day).Select(x => x.Category).Distinct())
{
<div class="event-dot @cat.ConvertToHumanReadable()" title="@cat.ConvertToHumanReadable()"></div>
}
</div>
}
</div>
</div>
}
}
</div>
<div class="appointments">
@if (IsLoading)
{
<SpinnerLayout FullScreen="false"/>
}
else if (FilteredActivities is { Count: > 0 })
{
@foreach (var activity in FilteredActivities)
{
<ActivityCard Activity="activity"/>
}
}
else
{
<NoDataAvailable Text="Nessuna attività trovata" ImageSource="_content/Template.Shared/images/undraw_file-search_cbur.svg"/>
}
</div>
@code {
// Stato UI
private bool Expanded { get; set; } = false;
private string SliderAnimation { get; set; } = string.Empty;
private ElementReference weekSliderRef;
private DotNetObjectReference<Calendar>? dotNetHelper;
// Stato calendario
private DateTime SelectedDate { get; set; } = DateTime.Today;
private DateTime _internalMonth = DateTime.Today;
private DateTime CurrentMonth => new(_internalMonth.Year, _internalMonth.Month, 1);
// Stato attività
private List<ActivityDTO> MonthActivities { get; set; } = [];
private List<ActivityDTO> FilteredActivities { get; set; } = [];
private bool IsLoading { get; set; } = true;
// Supporto rendering mese
private static readonly string[] GiorniSettimana = ["Lu", "Ma", "Me", "Gi", "Ve", "Sa", "Do"];
private int DaysInMonth => DateTime.DaysInMonth(CurrentMonth.Year, CurrentMonth.Month);
private int StartOffset => (int)CurrentMonth.DayOfWeek == 0 ? 6 : (int)CurrentMonth.DayOfWeek - 1;
private int EndOffset
{
get
{
var totalCells = (int)Math.Ceiling((DaysInMonth + StartOffset) / 7.0) * 7;
return totalCells - (DaysInMonth + StartOffset);
}
}
// Supporto rendering settimana
private IEnumerable<DateTime> DaysOfWeek
{
get
{
var start = GetStartOfWeek(SelectedDate);
return Enumerable.Range(0, 7).Select(i => start.AddDays(i));
}
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
dotNetHelper = DotNetObjectReference.Create(this);
await JS.InvokeVoidAsync("calendarSwipe.register", weekSliderRef, dotNetHelper);
_internalMonth = new DateTime(SelectedDate.Year, SelectedDate.Month, 1);
await LoadMonthData();
}
}
[JSInvokable]
public async Task OnSwipeLeft()
{
await CambiaPeriodo(1);
StateHasChanged();
if (Expanded)
{
await LoadMonthData();
}
}
[JSInvokable]
public async Task OnSwipeRight()
{
await CambiaPeriodo(-1);
StateHasChanged();
if (Expanded)
{
await LoadMonthData();
}
}
// Cambio periodo mese/settimana
private async Task CambiaPeriodo(int direzione)
{
if (Expanded)
{
// Cambio solo il mese visualizzato, NON cambiare SelectedDate
var y = CurrentMonth.Year;
var m = CurrentMonth.Month + direzione;
if (m < 1)
{
y--;
m = 12;
}
if (m > 12)
{
y++;
m = 1;
}
_internalMonth = new DateTime(y, m, 1);
}
else
{
// Cambio settimana: aggiorno anche il giorno selezionato
await SelezionaData(SelectedDate.AddDays(7 * direzione));
_internalMonth = new DateTime(SelectedDate.Year, SelectedDate.Month, 1);
}
}
// Cambio modalità
private void ToggleExpanded()
{
if (Expanded)
{
SliderAnimation = "collapse-animation";
StateHasChanged();
Expanded = false;
}
else
{
Expanded = true;
SliderAnimation = "expand-animation";
StateHasChanged();
}
SliderAnimation = "";
StateHasChanged();
}
// Caricamento attività al cambio mese
private async Task LoadMonthData()
{
IsLoading = true;
StateHasChanged();
// Carica tutte le attività del mese corrente visualizzato
var start = CurrentMonth;
var end = start.AddDays(DaysInMonth - 1);
var activities = await manageData.GetActivity(x =>
(x.EffectiveDate == null && x.EstimatedDate >= start && x.EstimatedDate <= end) ||
(x.EffectiveDate >= start && x.EffectiveDate <= end));
MonthActivities = activities.OrderBy(x => x.EffectiveDate ?? x.EstimatedDate).ToList();
IsLoading = false;
StateHasChanged();
}
// Selezione giorno in settimana
private async Task SelezionaData(DateTime day)
{
SelectedDate = day;
StateHasChanged();
var cacheInternalMonth = _internalMonth;
_internalMonth = new DateTime(day.Year, day.Month, 1);
if (cacheInternalMonth != _internalMonth)
{
await LoadMonthData();
}
FilteredActivities = GetEventsForDay(day);
StateHasChanged();
}
// Selezione giorno dal mese (chiude la vista mese!)
private async Task SelezionaDataDalMese(DateTime day)
{
SelectedDate = day;
FilteredActivities = GetEventsForDay(day);
// Chiudi la vista mese e passa alla settimana, con animazione
SliderAnimation = "collapse-animation";
StateHasChanged();
Expanded = false;
_internalMonth = new DateTime(day.Year, day.Month, 1); // Sync il mese visualizzato
SliderAnimation = "";
StateHasChanged();
}
// Utility
private static bool IsSameDay(DateTime d1, DateTime d2) => d1.Date == d2.Date;
private static DateTime GetStartOfWeek(DateTime date)
{
var day = date.DayOfWeek == DayOfWeek.Sunday ? 7 : (int)date.DayOfWeek;
return date.AddDays(-day + 1).Date;
}
private List<ActivityDTO> GetEventsForDay(DateTime day)
=> MonthActivities?.Where(x => (x.EffectiveDate ?? x.EstimatedDate) == day.Date).ToList() ?? [];
public void Dispose()
{
dotNetHelper?.Dispose();
}
}