Cambiata visualizzazione calendario e aggiunto formAttività

This commit is contained in:
2025-06-11 10:11:20 +02:00
parent d462e9faca
commit d8f2588e0e
52 changed files with 1308 additions and 4734 deletions

View File

@@ -0,0 +1,157 @@
@page "/activity"
@page "/activity/{Id}"
@using Template.Shared.Core.Dto
@using Template.Shared.Components.Layout
@using Template.Shared.Core.Interface
@inject NavigationManager NavigationManager
@inject IManageDataService manageData
<HeaderLayout Cancel="true" LabelSave="@LabelSave" ShowNotifications="false" Title="@(IsNew ? "Nuova" : $"{ActivityModel.ActivityId}")"/>
<div class="content">
<div class="input-card">
<MudTextField T="string?" Placeholder="Descrizione" Variant="Variant.Text" Lines="3" @bind-Value="ActivityModel.ActivityDescription" @bind-Value:after="OnAfterChangeValue"/>
</div>
<div class="input-card">
<div class="form-container">
<MudInput T="string?" Placeholder="Cliente" @bind-Value="ActivityModel.Cliente" @bind-Value:after="OnAfterChangeValue"/>
</div>
<div class="divider"></div>
<div class="form-container">
<MudInput T="string?" Placeholder="Commessa" @bind-Value="ActivityModel.Commessa" @bind-Value:after="OnAfterChangeValue"/>
</div>
</div>
<div class="input-card">
<div class="form-container">
<span>Inizio</span>
<MudTextField T="DateTime?" Format="yyyy-MM-ddTHH:mm" InputType="InputType.DateTimeLocal" @bind-Value="ActivityModel.EstimatedTime" @bind-Value:after="OnAfterChangeValue"/>
</div>
<div class="divider"></div>
<div class="form-container">
<span>Fine</span>
<MudTextField T="DateTime?" Format="yyyy-MM-ddTHH:mm" InputType="InputType.DateTimeLocal" @bind-Value="ActivityModel.EstimatedEndtime" @bind-Value:after="OnAfterChangeValue"/>
</div>
<div class="divider"></div>
<div class="form-container">
<span>Avviso</span>
<MudSwitch T="bool" Disabled="true" Color="Color.Primary"/>
</div>
</div>
<div class="input-card">
<div class="form-container text-align-end">
<span>Assegnata a</span>
<MudInput T="string" Placeholder="Nessuno" @bind-Value="ActivityModel.UserName" @bind-Value:after="OnAfterChangeValue"/>
</div>
<div class="divider"></div>
<div class="form-container">
<span>Tipo</span>
<MudSelect T="string?" Variant="Variant.Text" @bind-Value="ActivityModel.ActivityTypeId" @bind-Value:after="OnAfterChangeValue" Class="customIcon-select" AdornmentIcon="@Icons.Material.Filled.Code">
@foreach (var state in ActivityResult)
{
<MudSelectItem Value="@state">@state</MudSelectItem>
}
</MudSelect>
</div>
<div class="divider"></div>
<div class="form-container">
<span>Esito</span>
<MudSelect T="string?" Variant="Variant.Text" @bind-Value="ActivityModel.ActivityResultId" @bind-Value:after="OnAfterChangeValue" Class="customIcon-select" AdornmentIcon="@Icons.Material.Filled.Code">
@foreach (var state in ActivityResult)
{
<MudSelectItem Value="@state">@state</MudSelectItem>
}
</MudSelect>
</div>
</div>
<div class="input-card">
<MudTextField T="string?" Placeholder="Note" Variant="Variant.Text" Lines="4" @bind-Value="ActivityModel.Note" @bind-Value:after="OnAfterChangeValue" />
</div>
<MudOverlay @bind-Visible="_selectEstimatedTime" DarkBackground="true" AutoClose="true">
<MudDatePicker @bind-Date:after="CloseDatePicker" @bind-Date="ActivityModel.EstimatedTime" PickerVariant="PickerVariant.Static"/>
</MudOverlay>
<MudOverlay @bind-Visible="_selectEstimatedEndTime" DarkBackground="true" AutoClose="true">
<MudDatePicker @bind-Date:after="CloseDatePicker" @bind-Date="ActivityModel.EstimatedEndtime" PickerVariant="PickerVariant.Static"/>
</MudOverlay>
</div>
@code {
[Parameter] public string? Id { get; set; }
private ActivityDTO OriginalModel { get; set; } = new();
private ActivityDTO ActivityModel { get; set; } = new();
private List<ActivityResultDTO> ActivityResult { get; set; } = [];
private bool IsNew => Id.IsNullOrEmpty();
private bool _selectEstimatedTime;
private bool _selectEstimatedEndTime;
private string? LabelSave { get; set; }
protected override async Task OnInitializedAsync()
{
LabelSave = IsNew ? "Aggiungi" : null;
if (!Id.IsNullOrEmpty())
ActivityModel = (await manageData.GetActivity(x => x.ActivityId.Equals(Id))).Last();
if (IsNew)
{
ActivityModel.EstimatedTime = DateTime.Today.Add(TimeSpan.FromHours(DateTime.Now.Hour));
ActivityModel.EstimatedEndtime = DateTime.Today.Add(TimeSpan.FromHours(DateTime.Now.Hour) + TimeSpan.FromHours(1));
}
OriginalModel = ActivityModel.Clone();
}
private async Task HandleValidSubmit()
{
// Salva su database (qui simulato)
await Task.Delay(200);
NavigationManager.NavigateTo("/attivita");
}
private void Annulla()
{
NavigationManager.NavigateTo("/attivita");
}
private void OnAfterChangeValue()
{
if (OriginalModel.Equals(ActivityModel))
LabelSave = "Aggiorna";
StateHasChanged();
}
private async Task CloseDatePicker()
{
await Task.Delay(150);
_selectEstimatedTime = false;
_selectEstimatedEndTime = false;
StateHasChanged();
}
}

View File

@@ -0,0 +1,50 @@
.input-card {
width: 100%;
background: var(--mud-palette-background-gray);
border-radius: 9px;
padding: .5rem 1rem;
margin-bottom: 1.5rem;
}
.input-card ::deep > .divider { margin: 0 !important; }
.form-container {
display: flex;
justify-content: space-between;
align-items: center;
min-height: 35px;
}
.form-container > span {
font-weight: 600;
width: 50%;
}
.dateTime-picker {
display: flex;
gap: .3rem;
flex-direction: row;
align-items: center;
}
/*Custom mudBlazor*/
.form-container ::deep .mud-input.mud-input-underline:before { border-bottom: none !important; }
.form-container ::deep .mud-input.mud-input-underline:after { border-bottom: none !important; }
.form-container.text-align-end ::deep .mud-input-slot { text-align: end; }
.input-card ::deep .mud-input.mud-input-underline:before { border-bottom: none !important; }
.input-card ::deep .mud-input.mud-input-underline:after { border-bottom: none !important; }
.form-container ::deep .customIcon-select .mud-icon-root.mud-svg-icon {
rotate: 90deg !important;
font-size: 1.1rem;
}
.input-card ::deep .mud-input {
width: 100%;
font-weight: 500;
}

View File

@@ -1,180 +1,302 @@
@page "/Calendar"
@attribute [Authorize]
@page "/Calendar"
@using Template.Shared.Core.Dto
@using Template.Shared.Core.Interface
@using Template.Shared.Components.Layout
@using Template.Shared.Components.SingleElements.Calendar
@using Template.Shared.Components.SingleElements
@using Template.Shared.Components.Layout.Spinner
@inject IManageDataService manageData
@inject IJSRuntime JS
<HeaderLayout Title="Agenda" ShowFilter="true"/>
<HeaderLayout Title="@CurrentMonth.ToString("MMMM yyyy", new System.Globalization.CultureInfo("it-IT")).FirstCharToUpper()"
ShowFilter="true"
ShowCalendarToggle="true"
OnCalendarToggle="ToggleExpanded"/>
<div class="content">
<MudButtonGroup Size="Size.Small" Class="custom-mudButtonGroup" Color="Color.Surface" OverrideStyles="true" Variant="Variant.Outlined" DropShadow="true">
<MudButton StartIcon="@Icons.Material.Filled.ViewStream" Class="@(FilterByDay ? "custom-button-active" : "")" OnClick="SelectDay">Giorno</MudButton>
<MudButton StartIcon="@Icons.Material.Filled.CalendarViewDay" Class="@(FilterByWeek ? "custom-button-active" : "")" OnClick="SelectWeek">Settimana</MudButton>
<MudButton StartIcon="@Icons.Material.Filled.CalendarViewMonth" Class="@(FilterByMonth ? "custom-button-active" : "")" OnClick="SelectMonth">Mese</MudButton>
</MudButtonGroup>
<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>
}
<div class="activity-filter">
<div class="date-controller">
<MudIconButton Icon="@Icons.Material.Filled.ChevronLeft" @onclick="() => ChangeDate(-1)" Color="Color.Surface"/>
<MudButton Variant="Variant.Text" Color="Color.Surface" OnClick="OpenCalendar">
@if (FilterByDay)
@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())
{
@($"{DateFilter:M}")
<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>
}
else if (FilterByWeek)
{
@($"{(DateRangeFilter.Start!.Value.Month == DateRangeFilter.End!.Value.Month ? DateRangeFilter.Start!.Value.Day : DateRangeFilter.Start!.Value.ToString("M"))} - {DateRangeFilter.End!.Value:M}")
}
else if (FilterByMonth)
{
@($"{DateFilter:Y}")
}
</MudButton>
<MudIconButton Icon="@Icons.Material.Filled.ChevronRight" @onclick="() => ChangeDate(1)" Color="Color.Surface"/>
</div>
</div>
}
<MudOverlay @bind-Visible="_isVisible" DarkBackground="true" AutoClose="true">
<MudDatePicker @bind-Date:after="CloseDatePicker" @bind-Date="DateFilter" PickerVariant="PickerVariant.Static">
<PickerActions>
@if (DateFilter != DateTime.Today)
@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())
{
<MudButton Class="mr-auto align-self-start" OnClick="() => DateFilter = DateTime.Today">Oggi</MudButton>
<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>
}
</PickerActions>
</MudDatePicker>
</MudOverlay>
</div>
<div class="card-container">
@if (FilterByDay)
{
<DayView @bind-Date="DateFilter"/>
</div>
</div>
}
else if (FilterByWeek)
{
<WeekView @bind-Date="DateRangeFilter" />
}
else if (FilterByMonth)
{
<MonthView @bind-Date="DateTimeForMonthView"/>
}
</div>
<MudMenu PopoverClass="custom_popover" Class="custom-mudfab" AnchorOrigin="Origin.TopLeft" TransformOrigin="Origin.BottomRight">
<ActivatorContent>
<MudFab Color="Color.Primary" Size="Size.Small" StartIcon="@Icons.Material.Filled.Add"/>
</ActivatorContent>
<ChildContent>
<MudMenuItem>Nuovo contatto</MudMenuItem>
<MudMenuItem>Nuova attivit<69></MudMenuItem>
</ChildContent>
</MudMenu>
}
</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>
<MudMenu PopoverClass="custom_popover" Class="custom-mudfab" AnchorOrigin="Origin.TopLeft" TransformOrigin="Origin.BottomRight">
<ActivatorContent>
<MudFab Color="Color.Primary" Size="Size.Small" StartIcon="@Icons.Material.Filled.Add"/>
</ActivatorContent>
<ChildContent>
<MudMenuItem>Nuovo contatto</MudMenuItem>
<MudMenuItem OnClick="OpenActivityForm">Nuova attività</MudMenuItem>
</ChildContent>
</MudMenu>
@code {
private bool FilterByDay { get; set; } = true;
private bool FilterByWeek { get; set; }
private bool FilterByMonth { get; set; }
private DateTime? DateFilter { get; set; } = DateTime.Today;
private DateRange DateRangeFilter { get; set; } = new();
private DateTime DateTimeForMonthView { get; set; }
private bool _isVisible;
// Stato UI
private bool Expanded { get; set; } = false;
private string SliderAnimation { get; set; } = string.Empty;
private ElementReference weekSliderRef;
private DotNetObjectReference<Calendar>? dotNetHelper;
protected override void OnInitialized()
// 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
{
CalcDateRange();
}
public void OpenCalendar()
{
_isVisible = true;
StateHasChanged();
}
private void SelectDay()
{
ResetFilterCalendar();
FilterByDay = !FilterByDay;
StateHasChanged();
}
private void SelectWeek()
{
ResetFilterCalendar();
FilterByWeek = !FilterByWeek;
CalcDateRange();
StateHasChanged();
}
private void SelectMonth()
{
ResetFilterCalendar();
FilterByMonth = !FilterByMonth;
DateTimeForMonthView = new DateTime(DateFilter!.Value.Year, DateFilter!.Value.Month, 1);
StateHasChanged();
}
private void ResetFilterCalendar(bool forceSelectDay = false)
{
FilterByDay = false;
FilterByWeek = false;
FilterByMonth = false;
}
private void ChangeDate(int value)
{
var date = DateFilter!.Value;
if (FilterByDay)
get
{
DateFilter = date.AddDays(value);
var totalCells = (int)Math.Ceiling((DaysInMonth + StartOffset) / 7.0) * 7;
return totalCells - (DaysInMonth + StartOffset);
}
else if (FilterByWeek)
{
DateFilter = DateRangeFilter.Start!.Value.AddDays(value > 0 ? 7 : -7);
}
else if (FilterByMonth)
{
var year = date.Year;
var month = value > 0 ? date.Month + 1 : date.Month - 1;
}
switch (month)
// 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()
{
CambiaPeriodo(1);
await LoadMonthData();
StateHasChanged();
}
[JSInvokable]
public async Task OnSwipeRight()
{
CambiaPeriodo(-1);
await LoadMonthData();
StateHasChanged();
}
// Cambio periodo mese/settimana
private void 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)
{
case > 12:
year++;
month = 1;
break;
case < 1:
year--;
month = 12;
break;
y--;
m = 12;
}
DateFilter = new DateTime(year, month, 1);
DateTimeForMonthView = DateFilter.Value;
if (m > 12)
{
y++;
m = 1;
}
_internalMonth = new DateTime(y, m, 1);
}
else
{
// Cambio settimana: aggiorno anche il giorno selezionato
SelectedDate = SelectedDate.AddDays(7 * direzione);
_internalMonth = new DateTime(SelectedDate.Year, SelectedDate.Month, 1);
}
}
// Cambio modalità
private async Task ToggleExpanded()
{
if (Expanded)
{
SliderAnimation = "collapse-animation";
StateHasChanged();
Expanded = false;
}
else
{
Expanded = true;
SliderAnimation = "expand-animation";
StateHasChanged();
}
CalcDateRange();
SliderAnimation = "";
StateHasChanged();
await LoadMonthData();
}
// 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();
// Filtro per il giorno selezionato (solo se il giorno selezionato è visibile nel mese corrente)
if (SelectedDate.Month == CurrentMonth.Month && SelectedDate.Year == CurrentMonth.Year)
FilteredActivities = GetEventsForDay(SelectedDate);
else
FilteredActivities = [];
IsLoading = false;
StateHasChanged();
}
private void CalcDateRange()
// Selezione giorno in settimana
private void SelezionaData(DateTime day)
{
var giornoSettimana = DateFilter!.Value.DayOfWeek;
var diffInizio = (7 + (giornoSettimana - DayOfWeek.Monday)) % 7;
DateRangeFilter.Start = DateFilter!.Value.AddDays(-diffInizio).Date;
DateRangeFilter.End = DateRangeFilter.Start.Value.AddDays(6);
}
private async Task CloseDatePicker()
{
DateTimeForMonthView = new DateTime(DateFilter!.Value.Year, DateFilter!.Value.Month, 1);
CalcDateRange();
await Task.Delay(150);
_isVisible = false;
SelectedDate = day;
_internalMonth = new DateTime(day.Year, day.Month, 1); // Sync il mese visualizzato solo se cambio settimana!
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();
await LoadMonthData(); // Aggiorna anche la lista attività (se hai cambiato mese)
}
// 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();
}
private void OpenActivityForm() =>
NavigationManager.NavigateTo("/activity");
}

View File

@@ -1,32 +1,151 @@
.activity-filter { margin-top: .2rem; }
.arrow-btn {
background: none;
border: none;
color: var(--mud-palette-primary);
font-size: 1.4rem;
padding: 0 0.5rem;
cursor: pointer;
line-height: 1;
transition: color 0.2s;
}
.card-container {
margin-top: .2rem;
.arrow-btn:active { color: #2a27b2; }
.calendar {
overflow: hidden;
position: relative;
}
.week-slider {
width: 100%;
transition: all 0.4s ease;
transform-origin: center center;
transform: scaleY(1);
opacity: 1;
touch-action: pan-x;
user-select: none;
margin: 0 auto;
}
.week-slider:not(.expanded) {
display: flex;
flex-direction: row;
justify-content: center;
align-items: flex-start;
gap: 0.6rem;
padding: 1rem 0.3rem;
overflow-x: hidden;
overflow-y: visible;
}
.week-slider.expanded {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 0.4rem;
padding: 1rem;
overflow-y: auto;
}
.week-slider.expand-animation { animation: expandFromCenter 0.3s ease forwards; }
.week-slider.collapse-animation { animation: collapseToCenter 0.3s ease forwards; }
@keyframes expandFromCenter {
from {
transform: scaleY(0.6);
opacity: 0;
}
to {
transform: scaleY(1);
opacity: 1;
}
}
@keyframes collapseToCenter {
from {
transform: scaleY(1);
opacity: 1;
}
to {
transform: scaleY(0.6);
opacity: 0;
}
}
.week-day {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
gap: 1rem;
overflow: hidden;
}
.date-controller {
display: flex;
align-items: center;
max-width: 60px;
flex: 1 1 0;
gap: 0.2rem;
}
.content ::deep > .custom-mudButtonGroup {
width: 100%;
.week-day > div:first-child {
font-size: 0.8rem;
color: var(--mud-palette-primary);
margin-bottom: 0.2rem;
font-weight: 500;
}
.content ::deep > .custom-mudButtonGroup .mud-button-root {
border-radius: 12px;
padding: .2rem 1.5rem;
text-transform: none !important;
font-size: .985rem;
border: 1px solid var(--mud-palette-gray-light);
.day {
background: var(--mud-palette-surface);
border-radius: 10px;
text-align: center;
cursor: pointer;
transition: background 0.3s ease, transform 0.2s ease;
font-size: 0.95rem;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
width: 38px;
height: 38px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin: 0 auto;
}
.content ::deep > .custom-mudButtonGroup .custom-button-active {
background-color: hsl(from var(--mud-palette-primary) h s 95%);
.day:hover { transform: scale(1.08); }
.week-slider:not(.expanded) .day {
padding: 0;
min-width: 38px;
min-height: 38px;
max-width: 48px;
max-height: 48px;
}
.day.selected {
background: var(--mud-palette-primary);
color: white;
}
.appointments {
display: flex;
gap: 1rem;
overflow-y: auto;
flex-direction: column;
height: 70vh;
-ms-overflow-style: none;
scrollbar-width: none;
}
.appointments::-webkit-scrollbar { display: none; }
.appointment {
background: var(--mud-palette-surface);
border-radius: 8px;
padding: 0.8rem;
margin-bottom: 0.5rem;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.toggle-month {
background: none;
border: none;
color: var(--mud-palette-primary);
font-size: 1rem;
cursor: pointer;
}

View File

@@ -2,27 +2,46 @@
@page "/settings/{BackTo}"
@using Template.Shared.Components.Layout
<HeaderLayout BackTo="@BackTo" ShowNotifications="false" Back="true" Title="Impostazioni" />
<HeaderLayout BackTo="@BackTo" ShowNotifications="false" Back="true" Title="Impostazioni"/>
<div class="content">
<MudButton Class="user-button"
<MudButton Class="button-settings"
FullWidth="true"
Size="Size.Medium"
StartIcon="@Icons.Material.Outlined.Sync"
OnClick="UpdateDb"
Variant="Variant.Outlined">Sincronizza</MudButton>
Size="Size.Medium"
OnClick="() => UpdateDb(true)"
Variant="Variant.Outlined">
Sincronizza
</MudButton>
<div class="divider"></div>
<MudButton Class="button-settings"
FullWidth="true"
StartIcon="@Icons.Material.Outlined.Sync"
Size="Size.Medium"
OnClick="() => UpdateDb()"
Variant="Variant.Outlined">
Ripristina dati
</MudButton>
</div>
@code {
[Parameter] public string BackTo { get; set; } = "";
private void UpdateDb()
private void UpdateDb(bool withData = false)
{
var absoluteUri = NavigationManager.ToAbsoluteUri(NavigationManager.Uri);
var pathAndQuery = absoluteUri.Segments.Length > 1 ? absoluteUri.PathAndQuery : null;
var path = pathAndQuery == null ? $"/sync/{DateTime.Today:yyyy-MM-dd}" : $"/sync/{DateTime.Today:yyyy-MM-dd}?path=" + System.Web.HttpUtility.UrlEncode(pathAndQuery);
string path;
NavigationManager.NavigateTo(path);
if (withData)
path = pathAndQuery == null ? $"/sync/{DateTime.Today:yyyy-MM-dd}" : $"/sync/{DateTime.Today:yyyy-MM-dd}?path=" + System.Web.HttpUtility.UrlEncode(pathAndQuery);
else
path = pathAndQuery == null ? "/sync" : "/sync?path=" + System.Web.HttpUtility.UrlEncode(pathAndQuery);
NavigationManager.NavigateTo(path, replace: true);
}
}
}

View File

@@ -3,6 +3,7 @@
@using Template.Shared.Components.Layout.Spinner
@using Template.Shared.Core.Interface
@inject ISyncDbService syncDb
@inject IManageDataService manageData
<SyncSpinner Elements="@Elements"/>
@@ -11,27 +12,55 @@
private Dictionary<string, bool> Elements { get; set; } = new();
protected override async Task OnInitializedAsync()
private bool _hasStarted = false;
private int _completedCount = 0;
protected override void OnInitialized()
{
Elements.Add("Attività", false);
Elements.Add("Clienti", false);
Elements.Add("Commesse", false);
StateHasChanged();
Elements["Attività"] = false;
Elements["Commesse"] = false;
Elements["Clienti"] = false;
Elements["Prospect"] = false;
}
await Task.WhenAll(SetActivity(), SetClienti(), SetCommesse());
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && !_hasStarted)
{
_hasStarted = true;
LocalStorage.Set("last-sync", DateTime.Now);
if (DateFilter is null)
{
await manageData.ClearDb();
}
var pathQuery = System.Web.HttpUtility.ParseQueryString(new UriBuilder(NavigationManager.Uri).Query);
var originalPath = pathQuery["path"] ?? null;
var path = originalPath ?? "/Calendar";
NavigationManager.NavigateTo(path);
await Task.WhenAll(RunAndTrack(SetActivity), RunAndTrack(SetClienti), RunAndTrack(SetProspect), RunAndTrack(SetCommesse));
}
}
private async Task RunAndTrack(Func<Task> func)
{
await func();
_completedCount++;
if (_completedCount == Elements.Count)
{
LocalStorage.Set("last-sync", DateTime.Now);
var pathQuery = System.Web.HttpUtility.ParseQueryString(new UriBuilder(NavigationManager.Uri).Query);
var originalPath = pathQuery["path"] ?? null;
var path = originalPath ?? "/Calendar";
NavigationManager.NavigateTo(path, replace: true);
}
}
private async Task SetActivity()
{
await syncDb.GetAndSaveActivity(DateFilter);
await Task.Run(async () =>
{
await syncDb.GetAndSaveActivity(DateFilter);
});
Elements["Attività"] = true;
StateHasChanged();
@@ -39,16 +68,32 @@
private async Task SetClienti()
{
await syncDb.GetAndSaveClienti(DateFilter);
await syncDb.GetAndSaveProspect(DateFilter);
await Task.Run(async () =>
{
await syncDb.GetAndSaveClienti(DateFilter);
});
Elements["Clienti"] = true;
StateHasChanged();
}
private async Task SetProspect()
{
await Task.Run(async () =>
{
await syncDb.GetAndSaveProspect(DateFilter);
});
Elements["Prospect"] = true;
StateHasChanged();
}
private async Task SetCommesse()
{
await syncDb.GetAndSaveCommesse(DateFilter);
await Task.Run(async () =>
{
await syncDb.GetAndSaveCommesse(DateFilter);
});
Elements["Commesse"] = true;
StateHasChanged();