@page "/record-associations" @using CredentialManager.Models @using DataConnection.CredentialManagement.Interfaces @using Microsoft.AspNetCore.Components.Forms @using Microsoft.JSInterop @inject IDataConnectionCredentialService CredentialService @inject IJSRuntime JSRuntime @inject ILogger Logger Associazioni Record

Associazioni Record

Visualizza e gestisci le associazioni tra record sorgente e destinazione

Gestione Associazioni
Pulizia Associazioni
Validazione Associazioni
Esportazione
@if (isProcessing) {
@processingMessage
} @if (!string.IsNullOrEmpty(operationMessage)) {
@operationMessage
}

@filteredAssociations.Count

Associazioni Totali

@filteredAssociations.Where(a => a.IsActive).Count()

Attive

@filteredAssociations.Where(a => !a.IsActive).Count()

Disattivate

@filteredAssociations.Select(a => a.SourceName).Distinct().Count()

Sorgenti Diverse

@if (isLoading) {
Caricamento...

Caricamento associazioni...

} else if (!filteredAssociations.Any()) {
@if (!allAssociations.Any()) { Nessuna associazione trovata. Le associazioni vengono create automaticamente durante il trasferimento dati. } else { Nessuna associazione corrisponde ai filtri applicati. Prova a modificare i criteri di ricerca. }
} else {
Associazioni Record @filteredAssociations.Count
@foreach (var association in pagedAssociations) { }
Sorgente Tipo Chiave Sorgente Entità Destinazione ID Destinazione Credenziale REST Stato Creata Aggiornata Azioni
@association.SourceName @association.SourceType @association.SourceKey @association.DestinationEntity @association.DestinationId @association.RestCredentialName @if (association.IsActive) { Attiva } else { Disattivata } @association.CreatedAt.ToString("dd/MM/yyyy HH:mm") @(association.UpdatedAt?.ToString("dd/MM/yyyy HH:mm") ?? "-")
@if (association.IsActive) { } else { } @if (!string.IsNullOrEmpty(association.AdditionalInfo)) { }
@if (filteredAssociations.Count > pageSize) { } } @if (filteredAssociations.Any()) {
Azioni di Massa
}
@if (showConfirmModal) { } @code { private List allAssociations = new(); private List filteredAssociations = new(); private List pagedAssociations = new(); private bool isLoading = true; // Filtri private string sourceFilter = ""; private string entityFilter = ""; private string credentialFilter = ""; // Paginazione private int currentPage = 1; private int pageSize = 25; private int totalPages => (int)Math.Ceiling((double)filteredAssociations.Count / pageSize); // Gestione operazioni private bool isProcessing = false; private string processingMessage = ""; private string operationMessage = ""; private string operationMessageType = ""; // Modal di conferma private bool showConfirmModal = false; private bool isDeleteAll = false; private string confirmMessage = ""; protected override async Task OnInitializedAsync() { await RefreshAssociations(); } private async Task RefreshAssociations() { try { isLoading = true; allAssociations = await CredentialService.GetAllActiveRecordAssociationsAsync(); ApplyFilters(); } catch (Exception ex) { Logger.LogError(ex, "Errore nel caricamento delle associazioni"); await JSRuntime.InvokeVoidAsync("alert", $"Errore nel caricamento delle associazioni: {ex.Message}"); } finally { isLoading = false; } } private void ApplyFilters() { filteredAssociations = allAssociations.Where(a => (string.IsNullOrEmpty(sourceFilter) || a.SourceName.Contains(sourceFilter, StringComparison.OrdinalIgnoreCase)) && (string.IsNullOrEmpty(entityFilter) || a.DestinationEntity.Contains(entityFilter, StringComparison.OrdinalIgnoreCase)) && (string.IsNullOrEmpty(credentialFilter) || a.RestCredentialName.Contains(credentialFilter, StringComparison.OrdinalIgnoreCase)) ).OrderByDescending(a => a.CreatedAt).ToList(); currentPage = 1; UpdatePagedAssociations(); StateHasChanged(); } private void ClearFilters() { sourceFilter = ""; entityFilter = ""; credentialFilter = ""; ApplyFilters(); } private void ChangePage(int page) { if (page >= 1 && page <= totalPages) { currentPage = page; UpdatePagedAssociations(); } } private void UpdatePagedAssociations() { var startIndex = (currentPage - 1) * pageSize; pagedAssociations = filteredAssociations.Skip(startIndex).Take(pageSize).ToList(); } private async Task DeactivateAssociation(int id) { if (await JSRuntime.InvokeAsync("confirm", "Sei sicuro di voler disattivare questa associazione?")) { try { var success = await CredentialService.DeactivateRecordAssociationAsync(id); if (success) { await JSRuntime.InvokeVoidAsync("alert", "Associazione disattivata con successo!"); await RefreshAssociations(); } else { await JSRuntime.InvokeVoidAsync("alert", "Errore nella disattivazione dell'associazione."); } } catch (Exception ex) { Logger.LogError(ex, "Errore nella disattivazione dell'associazione {Id}", id); await JSRuntime.InvokeVoidAsync("alert", $"Errore: {ex.Message}"); } } } private async Task ActivateAssociation(int id) { try { var association = allAssociations.FirstOrDefault(a => a.Id == id); if (association != null) { association.IsActive = true; var success = await CredentialService.UpdateRecordAssociationAsync(association); if (success) { await JSRuntime.InvokeVoidAsync("alert", "Associazione riattivata con successo!"); await RefreshAssociations(); } else { await JSRuntime.InvokeVoidAsync("alert", "Errore nella riattivazione dell'associazione."); } } } catch (Exception ex) { Logger.LogError(ex, "Errore nella riattivazione dell'associazione {Id}", id); await JSRuntime.InvokeVoidAsync("alert", $"Errore: {ex.Message}"); } } private async Task DeleteAssociation(int id) { if (await JSRuntime.InvokeAsync("confirm", "Sei sicuro di voler eliminare definitivamente questa associazione? Questa azione non può essere annullata.")) { try { var success = await CredentialService.DeleteRecordAssociationAsync(id); if (success) { await JSRuntime.InvokeVoidAsync("alert", "Associazione eliminata con successo!"); await RefreshAssociations(); } else { await JSRuntime.InvokeVoidAsync("alert", "Errore nell'eliminazione dell'associazione."); } } catch (Exception ex) { Logger.LogError(ex, "Errore nell'eliminazione dell'associazione {Id}", id); await JSRuntime.InvokeVoidAsync("alert", $"Errore: {ex.Message}"); } } } private async Task ShowAdditionalInfo(RecordAssociation association) { var info = $"Informazioni aggiuntive per l'associazione:\n\n"; info += $"ID: {association.Id}\n"; info += $"Sorgente: {association.SourceName} ({association.SourceType})\n"; info += $"Chiave Sorgente: {association.SourceKey}\n"; info += $"Destinazione: {association.DestinationEntity}\n"; info += $"ID Destinazione: {association.DestinationId}\n"; info += $"Credenziale REST: {association.RestCredentialName}\n"; info += $"Creata: {association.CreatedAt}\n"; if (association.UpdatedAt.HasValue) info += $"Aggiornata: {association.UpdatedAt}\n"; info += $"Stato: {(association.IsActive ? "Attiva" : "Disattivata")}\n"; if (!string.IsNullOrEmpty(association.AdditionalInfo)) info += $"\nInformazioni aggiuntive:\n{association.AdditionalInfo}"; await JSRuntime.InvokeVoidAsync("alert", info); } private async Task DeactivateAllInactive() { if (await JSRuntime.InvokeAsync("confirm", "Sei sicuro di voler disattivare tutte le associazioni che non sono attualmente in uso?")) { try { // Implementa logica per disattivare associazioni inattive await JSRuntime.InvokeVoidAsync("alert", "Funzionalità in via di sviluppo."); } catch (Exception ex) { Logger.LogError(ex, "Errore nella disattivazione di massa"); await JSRuntime.InvokeVoidAsync("alert", $"Errore: {ex.Message}"); } } } private async Task DeleteAllInactive() { if (await JSRuntime.InvokeAsync("confirm", "Sei sicuro di voler eliminare definitivamente tutte le associazioni disattivate? Questa azione non può essere annullata.")) { try { var inactiveAssociations = allAssociations.Where(a => !a.IsActive).ToList(); var deletedCount = 0; foreach (var association in inactiveAssociations) { if (await CredentialService.DeleteRecordAssociationAsync(association.Id)) { deletedCount++; } } await JSRuntime.InvokeVoidAsync("alert", $"Eliminate {deletedCount} associazioni disattivate."); await RefreshAssociations(); } catch (Exception ex) { Logger.LogError(ex, "Errore nell'eliminazione di massa"); await JSRuntime.InvokeVoidAsync("alert", $"Errore: {ex.Message}"); } } } private async Task ExportAssociations() { try { var csv = "Sorgente,Tipo,Chiave Sorgente,Entità Destinazione,ID Destinazione,Credenziale REST,Stato,Creata,Aggiornata\n"; foreach (var association in filteredAssociations) { csv += $"\"{association.SourceName}\",\"{association.SourceType}\",\"{association.SourceKey}\","; csv += $"\"{association.DestinationEntity}\",\"{association.DestinationId}\",\"{association.RestCredentialName}\","; csv += $"\"{(association.IsActive ? "Attiva" : "Disattivata")}\",\"{association.CreatedAt:dd/MM/yyyy HH:mm}\","; csv += $"\"{(association.UpdatedAt?.ToString("dd/MM/yyyy HH:mm") ?? "")}\"\n"; } var fileName = $"associazioni_record_{DateTime.Now:yyyyMMdd_HHmmss}.csv"; var bytes = System.Text.Encoding.UTF8.GetBytes(csv); var base64 = Convert.ToBase64String(bytes); await JSRuntime.InvokeVoidAsync("downloadFile", fileName, base64); } catch (Exception ex) { Logger.LogError(ex, "Errore nell'esportazione delle associazioni"); await JSRuntime.InvokeVoidAsync("alert", $"Errore nell'esportazione: {ex.Message}"); } } private void ShowClearConfirmation(bool deleteAll) { isDeleteAll = deleteAll; if (deleteAll) { confirmMessage = "Sei sicuro di voler eliminare TUTTE le associazioni dal sistema? Questa operazione non può essere annullata."; } else { var filteredCount = filteredAssociations.Count; if (filteredCount == 0) { SetOperationMessage("Nessuna associazione da eliminare con i filtri attuali.", "warning"); return; } confirmMessage = $"Sei sicuro di voler eliminare {filteredCount} associazioni filtrate? Questa operazione non può essere annullata."; } showConfirmModal = true; StateHasChanged(); } private async Task ConfirmClearAssociations() { showConfirmModal = false; isProcessing = true; try { int deletedCount = 0; if (isDeleteAll) { processingMessage = "Eliminazione di tutte le associazioni..."; StateHasChanged(); deletedCount = await CredentialService.ClearAllRecordAssociationsAsync(); SetOperationMessage($"Eliminate tutte le {deletedCount} associazioni dal sistema.", "success"); } else { processingMessage = "Eliminazione associazioni filtrate..."; StateHasChanged(); // Elimina le associazioni filtrate una per una foreach (var association in filteredAssociations.ToList()) { await CredentialService.DeleteRecordAssociationAsync(association.Id); deletedCount++; } SetOperationMessage($"Eliminate {deletedCount} associazioni filtrate.", "success"); } await RefreshAssociations(); } catch (Exception ex) { Logger.LogError(ex, "Errore nell'eliminazione delle associazioni"); SetOperationMessage($"Errore nell'eliminazione: {ex.Message}", "danger"); } finally { isProcessing = false; processingMessage = ""; StateHasChanged(); } } private void CancelClearConfirmation() { showConfirmModal = false; StateHasChanged(); } private async Task ValidateAllAssociations() { isProcessing = true; processingMessage = "Validazione associazioni in corso..."; StateHasChanged(); try { int invalidCount = 0; var uniqueDestinations = allAssociations .GroupBy(a => new { a.DestinationEntity, a.RestCredentialName }) .ToList(); foreach (var group in uniqueDestinations) { var invalidAssociations = await CredentialService.GetInvalidRecordAssociationsAsync( group.Key.DestinationEntity, group.Key.RestCredentialName); invalidCount += invalidAssociations.Count; } if (invalidCount == 0) { SetOperationMessage("Tutte le associazioni sono valide! Non sono stati trovati ID di destinazione non più esistenti.", "success"); } else { SetOperationMessage($"Trovate {invalidCount} associazioni con ID di destinazione non più validi. Usa 'Pulisci Associazioni Non Valide' per rimuoverle.", "warning"); } } catch (Exception ex) { Logger.LogError(ex, "Errore nella validazione delle associazioni"); SetOperationMessage($"Errore nella validazione: {ex.Message}", "danger"); } finally { isProcessing = false; processingMessage = ""; StateHasChanged(); } } private async Task CleanupInvalidAssociations() { isProcessing = true; processingMessage = "Pulizia associazioni non valide..."; StateHasChanged(); try { int totalCleaned = 0; var uniqueDestinations = allAssociations .GroupBy(a => new { a.DestinationEntity, a.RestCredentialName }) .ToList(); foreach (var group in uniqueDestinations) { var cleanedCount = await CredentialService.CleanupInvalidRecordAssociationsAsync( group.Key.DestinationEntity, group.Key.RestCredentialName); totalCleaned += cleanedCount; } if (totalCleaned == 0) { SetOperationMessage("Nessuna associazione non valida trovata da pulire.", "info"); } else { SetOperationMessage($"Pulite {totalCleaned} associazioni con ID di destinazione non più validi.", "success"); await RefreshAssociations(); } } catch (Exception ex) { Logger.LogError(ex, "Errore nella pulizia delle associazioni non valide"); SetOperationMessage($"Errore nella pulizia: {ex.Message}", "danger"); } finally { isProcessing = false; processingMessage = ""; StateHasChanged(); } } private async Task ExportToCsv() { isProcessing = true; processingMessage = "Esportazione in corso..."; StateHasChanged(); try { await ExportAssociations(); SetOperationMessage($"Esportate {filteredAssociations.Count} associazioni in CSV.", "success"); } catch (Exception ex) { Logger.LogError(ex, "Errore nell'esportazione"); SetOperationMessage($"Errore nell'esportazione: {ex.Message}", "danger"); } finally { isProcessing = false; processingMessage = ""; StateHasChanged(); } } private async Task ShowImportDialog() { // Placeholder per import dialog await JSRuntime.InvokeVoidAsync("alert", "Funzionalità di importazione non ancora implementata."); } private void SetOperationMessage(string message, string type) { operationMessage = message; operationMessageType = type; StateHasChanged(); // Auto-hide success messages after 5 seconds if (type == "success") { _ = Task.Delay(5000).ContinueWith(_ => { operationMessage = ""; StateHasChanged(); }); } } }