@page "/User/{CodContact}/{IsContact:bool}" @attribute [Authorize] @using AutoMapper @using salesbook.Shared.Components.Layout @using salesbook.Shared.Core.Entity @using salesbook.Shared.Core.Interface @using salesbook.Shared.Components.Layout.Spinner @using salesbook.Shared.Core.Dto @using salesbook.Shared.Components.SingleElements @using salesbook.Shared.Core.Dto.JobProgress @using salesbook.Shared.Core.Dto.PageState @implements IAsyncDisposable @inject IManageDataService ManageData @inject IMapper Mapper @inject IDialogService Dialog @inject IIntegryApiService IntegryApiService @inject UserPageState UserState @if (IsLoading) { } else {
@UtilityString.ExtractInitials(Anag.RagSoc)
@Anag.RagSoc @if (!string.IsNullOrEmpty(Anag.Indirizzo)) { @Anag.Indirizzo } @if (!string.IsNullOrEmpty(Anag.Cap) && !string.IsNullOrEmpty(Anag.Citta) && !string.IsNullOrEmpty(Anag.Prov)) { @($"{Anag.Cap} - {Anag.Citta} ({Anag.Prov})") }
@if (PersRif?.Count > 0) {
@foreach (var person in PersRif) { @if (person != PersRif.Last()) {
} }
}
Aggiungi contatto
@if (IsLoadingCommesse) { } else if (Commesse?.Count == 0) { } else if (Commesse != null) {
@if (IsLoadingSteps) { } @foreach (var commessa in CurrentPageCommesse) {
@if (Steps.TryGetValue(commessa.CodJcom, out var steps)) { } else { @if (IsLoadingStep(commessa.CodJcom)) { } }
} @if (TotalPages > 1) {
5 10 15
} @*
Visualizzate: @CurrentPageCommesse.Count() / @FilteredCommesse.Count() @if (!string.IsNullOrEmpty(SearchTerm)) { Filtro: "@SearchTerm" }
*@
}
} @code { [Parameter] public string CodContact { get; set; } = string.Empty; [Parameter] public bool IsContact { get; set; } // Dati principali private ContactDTO Anag { get; set; } = new(); private List? PersRif { get; set; } private List? Commesse { get; set; } private StbUser? Agente { get; set; } private Dictionary?> Steps { get; set; } = new(); // Stati di caricamento private bool IsLoading { get; set; } = true; private bool IsLoadingCommesse { get; set; } = true; private bool IsLoadingSteps { get; set; } = false; private readonly HashSet _loadingSteps = new(); // Gestione tab private int ActiveTab { get; set; } = 0; // Paginazione e filtri private int _currentPage = 1; private int _selectedPageSize = 5; private string _searchTerm = string.Empty; private List _filteredCommesse = new(); // Cancellation tokens per gestire le richieste asincrone private CancellationTokenSource? _loadingCts; private CancellationTokenSource? _stepsCts; // Timer per il debounce della ricerca private Timer? _searchTimer; private const int SearchDelayMs = 300; #region Properties private int CurrentPage { get => _currentPage; set { if (_currentPage != value) { _currentPage = value; _ = LoadStepsForCurrentPageAsync(); } } } private int SelectedPageSize { get => _selectedPageSize; set { if (_selectedPageSize != value) { _selectedPageSize = value; _currentPage = 1; ApplyFilters(); _ = LoadStepsForCurrentPageAsync(); } } } private string SearchTerm { get => _searchTerm; set { if (_searchTerm != value) { _searchTerm = value; // Debounce della ricerca _searchTimer?.Dispose(); _searchTimer = new Timer(async _ => await InvokeAsync(ApplyFilters), null, SearchDelayMs, Timeout.Infinite); } } } private List FilteredCommesse { get => _filteredCommesse; set { _filteredCommesse = value; StateHasChanged(); } } private int TotalPages => FilteredCommesse.Count == 0 ? 1 : (int)Math.Ceiling(FilteredCommesse.Count / (double)SelectedPageSize); private IEnumerable CurrentPageCommesse => FilteredCommesse.Skip((CurrentPage - 1) * SelectedPageSize).Take(SelectedPageSize); #endregion #region Lifecycle Methods protected override async Task OnInitializedAsync() { try { _loadingCts = new CancellationTokenSource(); if (UserState.CodUser?.Equals(CodContact) == true) { LoadDataFromSession(); } else { await LoadDataAsync(); } } catch (Exception ex) { // Log dell'errore Console.WriteLine($"Errore in OnInitializedAsync: {ex.Message}"); } finally { IsLoading = false; StateHasChanged(); } } public async ValueTask DisposeAsync() { _loadingCts?.Cancel(); _loadingCts?.Dispose(); _stepsCts?.Cancel(); _stepsCts?.Dispose(); _searchTimer?.Dispose(); } #endregion #region Data Loading Methods private async Task LoadDataAsync() { try { // Caricamento dati principali await LoadAnagAsync(); await LoadPersRifAsync(); // Caricamento agente if (!string.IsNullOrEmpty(Anag.CodVage)) { Agente = (await ManageData.GetTable(x => x.UserCode != null && x.UserCode.Equals(Anag.CodVage))) .LastOrDefault(); } // Salvataggio in sessione SaveDataToSession(); // Caricamento commesse in background _ = Task.Run(async () => await LoadCommesseAsync()); } catch (Exception ex) { Console.WriteLine($"Errore nel caricamento dati: {ex.Message}"); } } private async Task LoadAnagAsync() { if (IsContact) { var clie = (await ManageData.GetTable(x => x.CodAnag.Equals(CodContact))).Last(); Anag = Mapper.Map(clie); } else { var pros = (await ManageData.GetTable(x => x.CodPpro.Equals(CodContact))).Last(); Anag = Mapper.Map(pros); } } private async Task LoadPersRifAsync() { if (IsContact) { var pers = await ManageData.GetTable(x => x.CodAnag.Equals(Anag.CodContact)); PersRif = Mapper.Map>(pers); } else { var pers = await ManageData.GetTable(x => x.CodPpro.Equals(Anag.CodContact)); PersRif = Mapper.Map>(pers); } } private async Task LoadCommesseAsync() { try { IsLoadingCommesse = true; Commesse = await ManageData.GetTable(x => x.CodAnag != null && x.CodAnag.Equals(CodContact)); // Ordinamento ottimizzato Commesse = Commesse? .OrderByDescending(x => x.LastUpd ?? DateTime.MinValue) .ThenByDescending(x => x.CodJcom) .ToList(); UserState.Commesse = Commesse; ApplyFilters(); // Caricamento steps per la prima pagina await LoadStepsForCurrentPageAsync(); } catch (Exception ex) { Console.WriteLine($"Errore nel caricamento commesse: {ex.Message}"); } finally { IsLoadingCommesse = false; await InvokeAsync(StateHasChanged); } } private void LoadDataFromSession() { Anag = UserState.Anag; PersRif = UserState.PersRif; Commesse = UserState.Commesse; Agente = UserState.Agente; Steps = UserState.Steps ?? new Dictionary?>(); ApplyFilters(); IsLoadingCommesse = false; } private void SaveDataToSession() { UserState.CodUser = CodContact; UserState.Anag = Anag; UserState.PersRif = PersRif; UserState.Agente = Agente; UserState.Steps = Steps; } #endregion #region Steps Loading Methods private async Task LoadStepsForCurrentPageAsync() { if (CurrentPageCommesse?.Any() != true) return; try { _stepsCts?.Cancel(); _stepsCts = new CancellationTokenSource(); var token = _stepsCts.Token; IsLoadingSteps = true; var tasksToLoad = new List(); var semaphore = new SemaphoreSlim(3); // Limita a 3 richieste simultanee foreach (var commessa in CurrentPageCommesse.Where(c => !Steps.ContainsKey(c.CodJcom))) { if (token.IsCancellationRequested) break; tasksToLoad.Add(LoadStepForCommessaAsync(commessa.CodJcom, semaphore, token)); } if (tasksToLoad.Any()) { await Task.WhenAll(tasksToLoad); UserState.Steps = Steps; } } catch (OperationCanceledException) { // Operazione annullata, non fare nulla } catch (Exception ex) { Console.WriteLine($"Errore nel caricamento steps: {ex.Message}"); } finally { IsLoadingSteps = false; await InvokeAsync(StateHasChanged); } } private async Task LoadStepForCommessaAsync(string codJcom, SemaphoreSlim semaphore, CancellationToken token) { await semaphore.WaitAsync(token); try { _loadingSteps.Add(codJcom); await InvokeAsync(StateHasChanged); var jobProgress = await IntegryApiService.RetrieveJobProgress(codJcom); if (!token.IsCancellationRequested) { Steps[codJcom] = jobProgress.Steps; await InvokeAsync(StateHasChanged); } } catch (Exception ex) { Console.WriteLine($"Errore nel caricamento step per {codJcom}: {ex.Message}"); if (!token.IsCancellationRequested) { Steps[codJcom] = null; } } finally { _loadingSteps.Remove(codJcom); semaphore.Release(); } } private bool IsLoadingStep(string codJcom) => _loadingSteps.Contains(codJcom); #endregion #region UI Methods private void SwitchTab(int tabIndex) { ActiveTab = tabIndex; // Se si passa alle commesse e non sono ancora state caricate if (tabIndex == 1 && Commesse == null) { _ = Task.Run(async () => await LoadCommesseAsync()); } else if (tabIndex == 1 && Steps.IsNullOrEmpty()) { _ = Task.Run(async () => await LoadStepsForCurrentPageAsync()); } } private void ApplyFilters() { if (Commesse == null) { FilteredCommesse = new List(); return; } var filtered = Commesse.AsEnumerable(); // Filtro per testo di ricerca if (!string.IsNullOrWhiteSpace(SearchTerm)) { var searchLower = SearchTerm.ToLowerInvariant(); filtered = filtered.Where(c => c.CodJcom?.ToLowerInvariant().Contains(searchLower) == true || c.Descrizione?.ToLowerInvariant().Contains(searchLower) == true || c.CodAnag?.ToLowerInvariant().Contains(searchLower) == true); } FilteredCommesse = filtered.ToList(); // Reset della pagina se necessario if (CurrentPage > TotalPages && TotalPages > 0) { _currentPage = 1; } // Carica gli steps per la pagina corrente _ = LoadStepsForCurrentPageAsync(); } private void ClearSearch() { SearchTerm = string.Empty; ApplyFilters(); } #endregion #region Modal Methods private async Task OpenPersRifForm() { var result = await ModalHelpers.OpenPersRifForm(Dialog, null, Anag, PersRif); if (result is { Canceled: false, Data: not null } && result.Data.GetType() == typeof(PersRifDTO)) { await LoadPersRifAsync(); UserState.PersRif = PersRif; } } private async Task OpenUserForm(ContactDTO anag) { var result = await ModalHelpers.OpenUserForm(Dialog, anag); if (result is { Canceled: false }) { // Aggiorna i dati se necessario await LoadAnagAsync(); SaveDataToSession(); } } #endregion }