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

Gestione Associazioni Chiavi

Visualizza e gestisci le associazioni basate sui valori delle chiavi. Ogni associazione lega un valore di chiave univoco a un record di destinazione, indipendentemente dalla sorgente che ha generato quel valore.

@if (statistics != null) {

@statistics.TotalAssociations

Totali

@statistics.ActiveAssociations

Attive

@statistics.InactiveAssociations

Disattive

@statistics.UniqueKeyValues

Chiavi Uniche

@statistics.UniqueDestinationEntities

Entità

@(statistics.OldestAssociation?.ToString("dd/MM/yy") ?? "N/A")

Più Vecchia

}
Gestione Associazioni
Operazioni di Pulizia
Operazioni Avanzate
@if (isProcessing) {
@processingMessage
} @if (!string.IsNullOrEmpty(operationMessage)) { }
@if (isLoading) {
Caricamento...

Caricamento associazioni...

} else if (!filteredAssociations.Any()) {
@if (!allAssociations.Any()) { Nessuna associazione trovata. Le associazioni vengono create automaticamente durante il trasferimento dati quando il sistema di associazioni è abilitato. } else { Nessuna associazione corrisponde ai filtri applicati. Prova a modificare i criteri di ricerca. }
} else {
Associazioni Chiavi @filteredAssociations.Count
@foreach (var association in pagedAssociations) { }
Valore Chiave Campo Sorgente Campo Destinazione Entità Destinazione ID Destinazione Credenziale Stato Creata Verificata Azioni
@association.KeyValue @association.SourceKeyField @association.DestinationKeyField @association.DestinationEntity @association.DestinationId @association.RestCredentialName @if (association.IsActive) { Attiva } else { Disattivata } @association.CreatedAt.ToString("dd/MM/yyyy HH:mm") @(association.LastVerifiedAt?.ToString("dd/MM/yyyy HH:mm") ?? "Mai")
@if (association.IsActive) { } else { }
@if (totalPages > 1) { } }
@code { // Dati private List allAssociations = new(); private List filteredAssociations = new(); private List pagedAssociations = new(); private AssociationStatistics? statistics; // Filtri private string keyValueFilter = ""; private string entityFilter = ""; private string credentialFilter = ""; // Paginazione private int currentPage = 1; private int pageSize = 25; private int totalPages = 1; // Stato private bool isLoading = true; private bool isProcessing = false; private string processingMessage = ""; private string operationMessage = ""; private string operationMessageType = ""; protected override async Task OnInitializedAsync() { await RefreshAssociations(); } private async Task RefreshAssociations() { isLoading = true; operationMessage = ""; StateHasChanged(); try { allAssociations = await CredentialService.GetAllKeyAssociationsAsync(); statistics = await CredentialService.GetKeyAssociationStatisticsAsync(); ApplyFilters(); } catch (Exception ex) { Logger.LogError(ex, "Errore nel caricamento delle associazioni"); SetOperationMessage($"Errore nel caricamento: {ex.Message}", "danger"); } finally { isLoading = false; StateHasChanged(); } } private void ApplyFilters() { filteredAssociations = allAssociations.Where(a => (string.IsNullOrEmpty(keyValueFilter) || a.KeyValue.Contains(keyValueFilter, 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() { keyValueFilter = ""; entityFilter = ""; credentialFilter = ""; ApplyFilters(); } private void ChangePage(int page) { if (page >= 1 && page <= totalPages) { currentPage = page; UpdatePagedAssociations(); } } private void UpdatePagedAssociations() { totalPages = (int)Math.Ceiling((double)filteredAssociations.Count / pageSize); 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.DeactivateKeyAssociationAsync(id); if (success) { SetOperationMessage("Associazione disattivata con successo!", "success"); await RefreshAssociations(); } else { SetOperationMessage("Errore nella disattivazione dell'associazione.", "danger"); } } catch (Exception ex) { Logger.LogError(ex, "Errore nella disattivazione dell'associazione {Id}", id); SetOperationMessage($"Errore: {ex.Message}", "danger"); } } } private async Task ActivateAssociation(int id) { try { var association = allAssociations.FirstOrDefault(a => a.Id == id); if (association != null) { association.IsActive = true; association.UpdatedAt = DateTime.UtcNow; var success = await CredentialService.UpdateKeyAssociationAsync(association); if (success) { SetOperationMessage("Associazione riattivata con successo!", "success"); await RefreshAssociations(); } else { SetOperationMessage("Errore nella riattivazione dell'associazione.", "danger"); } } } catch (Exception ex) { Logger.LogError(ex, "Errore nella riattivazione dell'associazione {Id}", id); SetOperationMessage($"Errore: {ex.Message}", "danger"); } } private async Task DeleteAssociation(int id) { if (await JSRuntime.InvokeAsync("confirm", "Sei sicuro di voler eliminare questa associazione? Questa operazione non può essere annullata.")) { try { var success = await CredentialService.DeleteKeyAssociationAsync(id); if (success) { SetOperationMessage("Associazione eliminata con successo!", "success"); await RefreshAssociations(); } else { SetOperationMessage("Errore nell'eliminazione dell'associazione.", "danger"); } } catch (Exception ex) { Logger.LogError(ex, "Errore nell'eliminazione dell'associazione {Id}", id); SetOperationMessage($"Errore: {ex.Message}", "danger"); } } } private async Task ShowAssociationDetails(KeyAssociation association) { var info = $"Dettagli associazione:\n\n"; info += $"ID: {association.Id}\n"; info += $"Valore Chiave: {association.KeyValue}\n"; info += $"Campo Sorgente: {association.SourceKeyField}\n"; info += $"Campo Destinazione: {association.DestinationKeyField}\n"; info += $"Entità: {association.DestinationEntity}\n"; info += $"ID Destinazione: {association.DestinationId}\n"; info += $"Credenziale: {association.RestCredentialName}\n"; info += $"Creata: {association.CreatedAt:dd/MM/yyyy HH:mm}\n"; if (association.UpdatedAt.HasValue) info += $"Aggiornata: {association.UpdatedAt:dd/MM/yyyy HH:mm}\n"; if (association.LastVerifiedAt.HasValue) info += $"Verificata: {association.LastVerifiedAt:dd/MM/yyyy HH:mm}\n"; info += $"Stato: {(association.IsActive ? "Attiva" : "Disattivata")}\n"; if (!string.IsNullOrEmpty(association.SourcesInfo)) info += $"\nSorgenti:\n{association.SourcesInfo}\n"; if (!string.IsNullOrEmpty(association.AdditionalInfo)) info += $"\nInformazioni aggiuntive:\n{association.AdditionalInfo}"; await JSRuntime.InvokeVoidAsync("alert", info); } private async Task ValidateAssociations() { 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.GetInvalidKeyAssociationsAsync( 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 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.CleanupInvalidKeyAssociationsAsync( 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 non valide!", "success"); await RefreshAssociations(); } } catch (Exception ex) { Logger.LogError(ex, "Errore nella pulizia delle associazioni"); SetOperationMessage($"Errore nella pulizia: {ex.Message}", "danger"); } finally { isProcessing = false; processingMessage = ""; StateHasChanged(); } } private async Task ExportAssociations() { try { var csv = "Valore Chiave,Campo Sorgente,Campo Destinazione,Entità Destinazione,ID Destinazione,Credenziale,Stato,Creata,Aggiornata,Verificata\n"; foreach (var association in filteredAssociations) { csv += $"\"{association.KeyValue}\",\"{association.SourceKeyField}\",\"{association.DestinationKeyField}\","; 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") ?? "")}\","; csv += $"\"{(association.LastVerifiedAt?.ToString("dd/MM/yyyy HH:mm") ?? "")}\"\n"; } var bytes = System.Text.Encoding.UTF8.GetBytes(csv); var fileName = $"associazioni_chiavi_{DateTime.Now:yyyyMMdd_HHmmss}.csv"; await JSRuntime.InvokeVoidAsync("downloadFile", fileName, "text/csv", System.Convert.ToBase64String(bytes)); SetOperationMessage($"File {fileName} esportato con successo!", "success"); } catch (Exception ex) { Logger.LogError(ex, "Errore nell'esportazione"); SetOperationMessage($"Errore nell'esportazione: {ex.Message}", "danger"); } } private async Task ClearAllAssociations() { if (await JSRuntime.InvokeAsync("confirm", "ATTENZIONE: Questa operazione eliminerà TUTTE le associazioni dal sistema. Sei assolutamente sicuro di voler procedere?")) { try { isProcessing = true; processingMessage = "Eliminazione di tutte le associazioni..."; StateHasChanged(); var deletedCount = await CredentialService.ClearAllKeyAssociationsAsync(); SetOperationMessage($"Eliminate {deletedCount} associazioni dal sistema!", "success"); await RefreshAssociations(); } catch (Exception ex) { Logger.LogError(ex, "Errore nell'eliminazione di massa"); SetOperationMessage($"Errore: {ex.Message}", "danger"); } finally { isProcessing = false; processingMessage = ""; StateHasChanged(); } } } private void SetOperationMessage(string message, string type) { operationMessage = message; operationMessageType = type; } }