Files
Data-Coupler/Components/ProfileManagement.razor
Alessio 7e450a358b feat: Aggiunto sistema completo di gestione profili per Data Coupler
- Creata nuova libreria Components con componenti Blazor riutilizzabili
  * ProfileSelector: dropdown per selezione profili salvati
  * ProfileSaver: componente per salvare configurazioni correnti come profili
  * ProfileManagement: modale per gestione profili salvati
  * ProfileQuickActions: bottoni azioni rapide per operazioni sui profili

- Esteso CredentialManager con entità e servizi per DataCouplerProfile
  * Aggiunto modello DataCouplerProfile con configurazioni mapping e metadati
  * Implementata migrazione Entity Framework per memorizzazione profili
  * Creato DataCouplerProfileService per operazioni CRUD
  * Aggiunto CredentialDbContextFactory per operazioni database design-time

- Migliorato componente principale DataCoupler con integrazione profili
  * Integrata funzionalità caricamento/salvataggio profili
  * Aggiunto selettore profili nella parte superiore dell'interfaccia
  * Mantenuta retrocompatibilità con funzionalità esistenti
  * Migliorata esperienza utente con gestione configurazioni salvate

- Aggiornata struttura progetto e dipendenze
  * Aggiunto progetto Components alla soluzione
  * Aggiornati riferimenti progetti e import
  * Rimosso progetto obsoleto TestDatabaseFix

Questo aggiornamento migliora significativamente il flusso di lavoro permettendo agli utenti di salvare, caricare e gestire configurazioni complete di accoppiamento dati come
2025-07-02 00:00:05 +02:00

181 lines
9.3 KiB
Plaintext

@* Componente per la gestione completa dei profili *@
<!-- Modal per la gestione profili -->
@if (ShowModal)
{
<div class="modal fade show d-block" style="background-color: rgba(0,0,0,0.5);">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<i class="fas fa-cogs"></i> Gestione Profili
</h5>
<button type="button" class="btn-close" @onclick="CloseModal"></button>
</div>
<div class="modal-body">
@if (IsLoading)
{
<div class="text-center py-4">
<div class="spinner-border" role="status">
<span class="visually-hidden">Caricamento...</span>
</div>
<p class="mt-2">Caricamento profili...</p>
</div>
}
else if (Profiles == null || !Profiles.Any())
{
<div class="text-center py-4">
<i class="fas fa-folder-open fa-3x text-muted mb-3"></i>
<h6 class="text-muted">Nessun profilo salvato</h6>
<p class="text-muted">Configura una connessione e salva il tuo primo profilo!</p>
</div>
}
else
{
<!-- Filtro di ricerca -->
<div class="mb-3">
<div class="input-group">
<span class="input-group-text">
<i class="fas fa-search"></i>
</span>
<input type="text" class="form-control" @bind="SearchTerm" @oninput="FilterProfiles"
placeholder="Cerca profili per nome o descrizione..." />
</div>
</div>
<!-- Lista profili -->
<div class="table-responsive">
<table class="table table-hover">
<thead class="table-light">
<tr>
<th>Nome</th>
<th>Fonte → Destinazione</th>
<th>Creato</th>
<th>Ultimo Uso</th>
<th width="120">Azioni</th>
</tr>
</thead>
<tbody>
@foreach (var profile in GetFilteredProfiles())
{
<tr>
<td>
<strong>@profile.Name</strong>
@if (!string.IsNullOrEmpty(profile.Description))
{
<br><small class="text-muted">@profile.Description</small>
}
</td>
<td>
<span class="badge bg-primary me-1">@GetTypeLabel(profile.SourceType)</span>
<i class="fas fa-arrow-right text-muted"></i>
<span class="badge bg-success ms-1">@GetTypeLabel(profile.DestinationType)</span>
<br>
<small class="text-muted">
@GetProfileSummary(profile)
</small>
</td>
<td>
<small>
@profile.CreatedAt.ToString("dd/MM/yyyy HH:mm")
@if (!string.IsNullOrEmpty(profile.CreatedBy))
{
<br><span class="text-muted">da @profile.CreatedBy</span>
}
</small>
</td>
<td>
<small>
@(profile.LastUsedAt?.ToString("dd/MM/yyyy HH:mm") ?? "Mai utilizzato")
</small>
</td>
<td>
<div class="btn-group btn-group-sm">
<button type="button" class="btn btn-outline-primary"
@onclick="() => LoadProfile(profile)"
title="Carica questo profilo">
<i class="fas fa-download"></i>
</button>
<button type="button" class="btn btn-outline-danger"
@onclick="() => ConfirmDelete(profile)"
title="Elimina questo profilo">
<i class="fas fa-trash"></i>
</button>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
@if (!GetFilteredProfiles().Any())
{
<div class="text-center py-3">
<i class="fas fa-search text-muted"></i>
<p class="text-muted mb-0">Nessun profilo corrisponde alla ricerca</p>
</div>
}
}
@if (!string.IsNullOrEmpty(Message))
{
<div class="alert alert-@(MessageType) mt-3">
<i class="fas fa-@(MessageType == "success" ? "check-circle" : "exclamation-triangle")"></i>
@Message
</div>
}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @onclick="CloseModal">
<i class="fas fa-times"></i> Chiudi
</button>
</div>
</div>
</div>
</div>
}
<!-- Modal conferma eliminazione -->
@if (ShowDeleteConfirm && ProfileToDelete != null)
{
<div class="modal fade show d-block" style="background-color: rgba(0,0,0,0.7);">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title text-danger">
<i class="fas fa-exclamation-triangle"></i> Conferma Eliminazione
</h5>
</div>
<div class="modal-body">
<p>Sei sicuro di voler eliminare il profilo <strong>"@ProfileToDelete.Name"</strong>?</p>
@if (!string.IsNullOrEmpty(ProfileToDelete.Description))
{
<p class="text-muted">@ProfileToDelete.Description</p>
}
<div class="alert alert-warning">
<i class="fas fa-info-circle"></i>
<strong>Attenzione:</strong> Questa operazione non può essere annullata.
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @onclick="CancelDelete">
<i class="fas fa-times"></i> Annulla
</button>
<button type="button" class="btn btn-danger" @onclick="DeleteProfile" disabled="@IsDeleting">
@if (IsDeleting)
{
<span class="spinner-border spinner-border-sm" role="status"></span>
}
else
{
<i class="fas fa-trash"></i>
}
Elimina
</button>
</div>
</div>
</div>
</div>
}