Completato form attività e filtri

This commit is contained in:
2025-06-16 11:58:49 +02:00
parent 0032648e76
commit 7ca4de628b
44 changed files with 1241 additions and 924 deletions

View File

@@ -1,73 +0,0 @@
@using Template.Shared.Core.Dto
@using Template.Shared.Core.Interface
@using Template.Shared.Components.Layout.Spinner
@inject IManageDataService manageData
<div class="calendar">
@if (Load)
{
<SpinnerLayout FullScreen="false" />
}
else
{
@if (!Activities.IsNullOrEmpty())
{
@foreach (var activity in Activities!)
{
<ActivityCard Activity="activity"/>
}
}
else
{
<NoDataAvailable Text="Nessuna attività trovata" ImageSource="_content/Template.Shared/images/undraw_file-search_cbur.svg"/>
}
}
</div>
@code
{
[Parameter] public required DateTime? Date { get; set; }
[Parameter] public EventCallback<DateTime?> DateChanged { get; set; }
private List<ActivityDTO>? Activities { get; set; }
private bool Load { get; set; } = true;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await LoadData();
}
}
protected override async Task OnParametersSetAsync()
{
await LoadData();
}
private async Task LoadData()
{
Load = true;
StateHasChanged();
await Task.Delay(500);
var refreshActivity = await RefreshActivity();
Activities = refreshActivity;
Load = false;
StateHasChanged();
}
private async Task<List<ActivityDTO>> RefreshActivity()
{
var activityDto = await Task.Run(async () =>
{
return (await manageData.GetActivity(x =>
(x.EffectiveDate == null && x.EstimatedDate.Equals(Date)) || x.EffectiveDate.Equals(Date)))
.OrderBy(x => x.EffectiveDate ?? x.EstimatedDate)
.ToList();
});
return activityDto;
}
}

View File

@@ -1,17 +0,0 @@
.calendar {
width: 100%;
display: flex;
flex-direction: column;
flex-wrap: nowrap;
gap: 1rem;
overflow-y: auto;
overflow-x: hidden;
padding-bottom: 1rem;
}
.calendar::-webkit-scrollbar { display: none; }
.calendar {
-ms-overflow-style: none;
scrollbar-width: none;
}

View File

@@ -1,150 +0,0 @@
@using Template.Shared.Core.Dto
@using Template.Shared.Core.Interface
@inject IManageDataService manageData
<div class="calendar-activity">
<div class="calendar">
@foreach (var nomeGiorno in _giorniSettimana)
{
<div class="calendar-header">@nomeGiorno</div>
}
@for (var i = 0; i < StartDays; i++)
{
<div class="calendar-day disabled @(i == 0 ? "radiusTopLeft" : "")"></div>
}
@for (var day = 1; day <= DaysInMonth; day++)
{
var currentDate = new DateTime(Date.Year, Date.Month, day);
var events = GetEventsForDay(currentDate);
var daySelected = SelectedDate == currentDate;
var isToday = currentDate == DateTime.Today;
var topRight = StartDays == 0 ? 7 : 7 - StartDays;
var bottomLeft = DaysInMonth - (6 - EndDays);
var categoryActivityCount = events.Select(x => x.Category).Distinct().ToList();
<div @onclick="() => SelectDay(currentDate)" class="calendar-day @(isToday ? "today" : daySelected ? "selectedDay" : "")
@(StartDays == 0 && day == 1 ? "radiusTopLeft" : "")
@(EndDays == 0 && day == DaysInMonth ? "radiusBottomRight" : "")
@(bottomLeft == day ? "radiusBottomLeft" : "")
@(topRight == day ? "radiusTopRight" : "")">
<div class="calendar-day-wrapper">
<span class="titleDay">@day</span>
@if (events.Any())
{
<div class="event-dot-container">
@foreach (var activityCategory in categoryActivityCount)
{
<div class="event-dot @activityCategory.ConvertToHumanReadable()"></div>
}
</div>
}
</div>
</div>
}
@for (var i = 0; i < EndDays; i++)
{
<div class="calendar-day disabled @(i + 1 == EndDays ? "radiusBottomRight" : "")"></div>
}
</div>
<div class="activityContainer">
@if (!FilteredActivityList.IsNullOrEmpty())
{
@foreach (var activity in FilteredActivityList!)
{
<ActivityCard Activity="activity"/>
}
}
</div>
</div>
@code
{
[Parameter] public required DateTime Date { get; set; }
[Parameter] public EventCallback<DateTime> DateChanged { get; set; }
private List<ActivityDTO> ActivityList { get; set; } = [];
private List<ActivityDTO> FilteredActivityList { get; set; } = [];
private DateTime SelectedDate { get; set; } = DateTime.Today;
private int DaysInMonth { get; set; }
private int StartDays { get; set; }
private int EndDays { get; set; }
readonly string[] _giorniSettimana = ["Lu", "Ma", "Me", "Gi", "Ve", "Sa", "Do"];
protected override async Task OnInitializedAsync()
{
await ChangeMonth();
}
protected override async Task OnParametersSetAsync()
{
await ChangeMonth();
}
private async Task ChangeMonth()
{
var firstDay = Date;
DaysInMonth = DateTime.DaysInMonth(firstDay.Year, firstDay.Month);
var dayOfWeek = (int)firstDay.DayOfWeek;
StartDays = dayOfWeek == 0 ? 6 : dayOfWeek - 1;
var tempTotalCell = (int)Math.Ceiling((double)(DaysInMonth + StartDays) / 7);
var totalCell = tempTotalCell * 7;
EndDays = totalCell - (DaysInMonth + StartDays);
await LoadData();
}
private async Task LoadData()
{
// Load = true;
// StateHasChanged();
await Task.Delay(500);
var refreshActivity = await RefreshActivity();
ActivityList = refreshActivity;
// Load = false;
StateHasChanged();
}
private async Task<List<ActivityDTO>> RefreshActivity()
{
var startDate = Date;
var endDate = Date.AddDays(DateTime.DaysInMonth(startDate.Year, startDate.Month) - 1);
var dateRange = new DateRange(Date, endDate);
var activityDto = await Task.Run(async () =>
{
return (await manageData.GetActivity(x =>
(x.EffectiveDate == null && x.EstimatedDate >= dateRange.Start && x.EstimatedDate <= dateRange.End) ||
(x.EffectiveDate >= dateRange.Start && x.EffectiveDate <= dateRange.End)
))
.OrderBy(x => x.EffectiveDate ?? x.EstimatedDate)
.ToList();
});
return activityDto;
}
private List<ActivityDTO> GetEventsForDay(DateTime day)
{
return ActivityList.IsNullOrEmpty() ? [] : ActivityList.Where(x => (x.EffectiveDate ?? x.EstimatedDate) == day.Date).ToList();
}
private void SelectDay(DateTime currentDate)
{
SelectedDate = currentDate;
StateHasChanged();
FilteredActivityList = GetEventsForDay(currentDate);
}
}

View File

@@ -1,101 +0,0 @@
.calendar {
display: grid;
grid-template-columns: repeat(7, 1fr);
}
.calendar-header, .calendar-day {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
min-height: 65px;
font-size: 0.85rem;
padding: 4px;
}
.calendar-day { border: 1px solid var(--mud-palette-gray-light); }
.calendar-day.disabled { border: 1px solid hsl(from var(--mud-palette-gray-light) h s 88%); }
.calendar-header {
font-weight: bold;
min-height: 25px;
display: flex;
justify-content: end;
}
.today > .calendar-day-wrapper > .titleDay {
background-color: var(--mud-palette-primary);
color: var(--mud-palette-appbar-text);
font-weight: 700;
}
.selectedDay > .calendar-day-wrapper > .titleDay {
border: 1px solid var(--mud-palette-primary);
font-weight: 700;
}
.calendar-day-wrapper > .titleDay {
padding: 6px;
border-radius: 50%;
position: absolute;
line-height: normal;
}
.event-dot-container {
position: absolute;
bottom: 0;
width: 100%;
display: flex;
flex-direction: column;
gap: .2rem;
}
.event-dot {
width: 100%;
height: 6px;
border-radius: 4px;
background-color: var(--mud-palette-secondary);
}
.event-dot.memo { background-color: var(--mud-palette-info-darken); }
.event-dot.interna { background-color: var(--mud-palette-success-darken); }
.event-dot.commessa { background-color: var(--mud-palette-warning); }
.calendar-day:hover .event-popup { display: block; }
.calendar-day-wrapper {
position: relative;
width: 100%;
height: 100%;
}
.radiusTopLeft { border-top-left-radius: 12px; }
.radiusTopRight { border-top-right-radius: 12px; }
.radiusBottomLeft { border-bottom-left-radius: 12px; }
.radiusBottomRight { border-bottom-right-radius: 12px; }
.activityContainer { margin-top: 1rem; }
.calendar-activity {
width: 100%;
display: flex;
flex-direction: column;
flex-wrap: nowrap;
gap: 1rem;
overflow-y: auto;
overflow-x: hidden;
padding-bottom: 1rem;
}
.calendar-activity::-webkit-scrollbar { display: none; }
.calendar-activity {
-ms-overflow-style: none;
scrollbar-width: none;
}

View File

@@ -1,88 +0,0 @@
@using Template.Shared.Core.Dto
@using Template.Shared.Core.Interface
@using Template.Shared.Components.Layout.Spinner
@inject IManageDataService manageData
<div class="calendar">
@{
DateTime? currentDate = null;
}
@if (Load)
{
<SpinnerLayout FullScreen="false"/>
}
else
{
@if (!Activities.IsNullOrEmpty())
{
foreach (var activity in Activities!)
{
var dateToShow = activity.EffectiveDate ?? activity.EstimatedDate;
if (currentDate != dateToShow?.Date)
{
currentDate = dateToShow?.Date;
<div class="week-info">
<span>@($"{currentDate:D}")</span>
</div>
}
<ActivityCard Activity="activity"/>
}
}
else
{
<NoDataAvailable Text="Nessuna attività trovata" ImageSource="_content/Template.Shared/images/undraw_file-search_cbur.svg"/>
}
}
</div>
@code
{
[Parameter] public required DateRange Date { get; set; }
[Parameter] public EventCallback<DateRange> DateChanged { get; set; }
private List<ActivityDTO>? Activities { get; set; }
private bool Load { get; set; } = true;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await LoadData();
}
}
protected override async Task OnParametersSetAsync()
{
await LoadData();
}
private async Task LoadData()
{
Load = true;
StateHasChanged();
await Task.Delay(500);
var refreshActivity = await RefreshActivity();
Activities = refreshActivity;
Load = false;
StateHasChanged();
}
private async Task<List<ActivityDTO>> RefreshActivity()
{
var activityDto = await Task.Run(async () =>
{
return (await manageData.GetActivity(x =>
(x.EffectiveDate == null && x.EstimatedDate >= Date.Start && x.EstimatedDate <= Date.End) ||
(x.EffectiveDate >= Date.Start && x.EffectiveDate <= Date.End)
))
.OrderBy(x => x.EffectiveDate ?? x.EstimatedDate)
.ToList();
});
return activityDto;
}
}

View File

@@ -1,26 +0,0 @@
.calendar {
width: 100%;
display: flex;
flex-direction: column;
flex-wrap: nowrap;
gap: 1rem;
overflow-y: auto;
overflow-x: hidden;
padding-bottom: 1rem;
}
.calendar::-webkit-scrollbar { display: none; }
.calendar {
-ms-overflow-style: none;
scrollbar-width: none;
}
.week-info {
background: var(--mud-palette-action-disabled-background);
width: 100%;
padding: .3rem .5rem;
border-radius: 8px;
text-transform: capitalize;
font-weight: 700;
}

View File

@@ -1,8 +1,9 @@
@using Template.Shared.Core.Dto
@using Template.Shared.Core.Entity
@using Template.Shared.Core.Helpers.Enum
@inject IDialogService Dialog
<div class="activity-card @Activity.Category.ConvertToHumanReadable()" @onclick="() => ModalHelpers.OpenActivityForm(Dialog, Activity.ActivityId)">
<div class="activity-card @Activity.Category.ConvertToHumanReadable()" @onclick="OpenActivity">
<div class="activity-left-section">
<div class="activity-body-section">
<div class="title-section">
@@ -63,6 +64,7 @@
@code {
[Parameter] public ActivityDTO Activity { get; set; } = new();
[Parameter] public EventCallback<string> ActivityChanged { get; set; }
private TimeSpan? Durata { get; set; }
@@ -75,4 +77,14 @@
_ => null
};
}
private async Task OpenActivity()
{
var result = await ModalHelpers.OpenActivityForm(Dialog, Activity.ActivityId);
if (result is { Canceled: false, Data: not null } && result.Data.GetType() == typeof(StbActivity))
{
await ActivityChanged.InvokeAsync(((StbActivity)result.Data).ActivityId);
}
}
}

View File

@@ -50,6 +50,6 @@
}
.activity-info-section {
width: min-content;
display: flex;
flex-wrap: wrap;
}

View File

@@ -0,0 +1,172 @@
@using Template.Shared.Core.Dto
@using Template.Shared.Components.Layout
@using Template.Shared.Core.Entity
@using Template.Shared.Core.Interface
@using Template.Shared.Components.Layout.Overlay
@inject IManageDataService ManageData
@inject INetworkService NetworkService
@inject IIntegryApiService IntegryApiService
<MudDialog Class="customDialog-form">
<DialogContent>
<HeaderLayout Cancel="true" OnCancel="() => MudDialog.Cancel()" LabelSave="@LabelSave" OnSave="Save" ShowNotifications="false" Title="@(IsNew ? "Nuova" : $"{ActivityModel.ActivityId}")" />
<div class="content">
<div class="input-card">
<MudTextField ReadOnly="IsView" T="string?" Placeholder="Descrizione" Variant="Variant.Text" Lines="3" @bind-Value="ActivityModel.ActivityDescription" @bind-Value:after="OnAfterChangeValue" DebounceInterval="500" OnDebounceIntervalElapsed="OnAfterChangeValue" />
</div>
<div class="input-card">
<div class="form-container">
<MudInput ReadOnly="IsView" T="string?" Placeholder="Cliente" @bind-Value="ActivityModel.Cliente" @bind-Value:after="OnAfterChangeValue" />
</div>
<div class="divider"></div>
<div class="form-container">
<MudInput ReadOnly="IsView" 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 ReadOnly="IsView" T="DateTime?" Format="yyyy-MM-ddTHH:mm" InputType="InputType.DateTimeLocal" @bind-Value="ActivityModel.EstimatedTime" @bind-Value:after="OnAfterChangeValue" DebounceInterval="500" OnDebounceIntervalElapsed="OnAfterChangeValue" />
</div>
<div class="divider"></div>
<div class="form-container">
<span>Fine</span>
<MudTextField ReadOnly="IsView" T="DateTime?" Format="yyyy-MM-ddTHH:mm" InputType="InputType.DateTimeLocal" @bind-Value="ActivityModel.EstimatedEndtime" @bind-Value:after="OnAfterChangeValue" DebounceInterval="500" OnDebounceIntervalElapsed="OnAfterChangeValue" />
</div>
<div class="divider"></div>
<div class="form-container">
<span>Avviso</span>
<MudSwitch ReadOnly="IsView" T="bool" Disabled="true" Color="Color.Primary" />
</div>
</div>
<div class="input-card">
<div class="form-container">
<span class="disable-full-width">Assegnata a</span>
<MudSelectExtended FullWidth="true" ReadOnly="IsView" T="string?" Variant="Variant.Text" @bind-Value="ActivityModel.UserName" @bind-Value:after="OnAfterChangeValue" Class="customIcon-select" AdornmentIcon="@Icons.Material.Filled.Code">
@foreach (var user in Users)
{
<MudSelectItemExtended Class="custom-item-select" Value="@user.UserName">@user.FullName</MudSelectItemExtended>
}
</MudSelectExtended>
</div>
<div class="divider"></div>
<div class="form-container">
<span class="disable-full-width">Tipo</span>
<MudSelectExtended ReadOnly="IsView" FullWidth="true" T="string?" Variant="Variant.Text" @bind-Value="ActivityModel.ActivityTypeId" @bind-Value:after="OnAfterChangeValue" Class="customIcon-select" AdornmentIcon="@Icons.Material.Filled.Code">
@foreach (var type in ActivityType)
{
<MudSelectItemExtended Class="custom-item-select" Value="@type.ActivityTypeId">@type.ActivityTypeId</MudSelectItemExtended>
}
</MudSelectExtended>
</div>
<div class="divider"></div>
<div class="form-container">
<span class="disable-full-width">Esito</span>
<MudSelectExtended ReadOnly="IsView" FullWidth="true" T="string?" Variant="Variant.Text" @bind-Value="ActivityModel.ActivityResultId" @bind-Value:after="OnAfterChangeValue" Class="customIcon-select" AdornmentIcon="@Icons.Material.Filled.Code">
@foreach (var result in ActivityResult)
{
<MudSelectItemExtended Class="custom-item-select" Value="@result.ActivityResultId">@result.ActivityResultId</MudSelectItemExtended>
}
</MudSelectExtended>
</div>
</div>
<div class="input-card">
<MudTextField ReadOnly="IsView" T="string?" Placeholder="Note" Variant="Variant.Text" Lines="4" @bind-Value="ActivityModel.Note" @bind-Value:after="OnAfterChangeValue" DebounceInterval="500" OnDebounceIntervalElapsed="OnAfterChangeValue" />
</div>
</div>
</DialogContent>
</MudDialog>
<SaveOverlay VisibleOverlay="VisibleOverlay" SuccessAnimation="SuccessAnimation" />
@code {
[CascadingParameter] private IMudDialogInstance MudDialog { get; set; }
[Parameter] public string? Id { get; set; }
private ActivityDTO OriginalModel { get; set; } = new();
private ActivityDTO ActivityModel { get; set; } = new();
private List<StbActivityResult> ActivityResult { get; set; } = [];
private List<StbActivityType> ActivityType { get; set; } = [];
private List<StbUser> Users { get; set; } = [];
private bool IsNew => Id.IsNullOrEmpty();
private bool IsView => !IsNew && !NetworkService.IsNetworkAvailable();
private string? LabelSave { get; set; }
//Overlay for save
private bool VisibleOverlay { get; set; }
private bool SuccessAnimation { get; set; }
protected override async Task OnInitializedAsync()
{
_ = LoadData();
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 Save()
{
VisibleOverlay = true;
StateHasChanged();
var newActivity = await IntegryApiService.SaveActivity(ActivityModel);
await ManageData.InsertOrUpdate(newActivity);
SuccessAnimation = true;
StateHasChanged();
await Task.Delay(1250);
MudDialog.Close(newActivity);
}
private async Task LoadData()
{
Users = await ManageData.GetTable<StbUser>();
ActivityResult = await ManageData.GetTable<StbActivityResult>();
ActivityType = await ManageData.GetTable<StbActivityType>(x => x.FlagTipologia.Equals("A"));
}
private void OnAfterChangeValue()
{
LabelSave = !OriginalModel.Equals(ActivityModel) ? "Aggiorna" : null;
StateHasChanged();
}
}