[Feature] Implementato sistema di valori default per campi mapping

- Creato modello FieldMappingEntry per gestione unificata di field mapping e default values

- Aggiunta colonna DefaultValuesJson alla tabella DataCouplerProfile (max 4000 caratteri)

- Implementata UI con toggle per selezionare modalità Mapping o Default

- Supporto per 9 tipi di dati: string, int, long, decimal, double, float, boolean, datetime, datetimeoffset

- Aggiornata logica TransformRecordToRestEntity per applicare valori default dopo field mapping

- Implementata serializzazione/deserializzazione DefaultValues in DataCouplerProfileService

- Sistema completo di salvataggio/caricamento valori default nei profili

- Migrazione database AddDefaultValuesJsonToProfile creata e applicata
This commit is contained in:
Alessio Dal Santo
2026-02-16 14:42:03 +01:00
parent 483eb7b407
commit b9670ae426
10 changed files with 1249 additions and 65 deletions
+174 -55
View File
@@ -920,23 +920,80 @@
<!-- Colonna Centrale: Controlli Mapping -->
<div class="col-2 text-center">
<div class="d-flex flex-column justify-content-center h-100">
<button class="btn btn-success mb-2" @onclick="CreateMapping"
disabled="@(string.IsNullOrEmpty(selectedDbColumn) || string.IsNullOrEmpty(selectedRestProperty))">
<i class="fas fa-arrow-right"></i>
<small class="d-block">Map</small>
</button>
<button class="btn btn-danger mb-2" @onclick="RemoveMapping"
disabled="@(string.IsNullOrEmpty(selectedDbColumn) || !fieldMappings.ContainsKey(selectedDbColumn))">
<i class="fas fa-times"></i>
<small class="d-block">Remove</small>
</button>
<button class="btn btn-warning mb-2" @onclick="AutoMapFields">
<i class="fas fa-magic"></i>
<small class="d-block">Auto</small>
</button>
<!-- Toggle tra Mapping e Default Value -->
<div class="btn-group mb-3" role="group">
<button type="button"
class="btn btn-sm @(isAddingDefaultValue ? "btn-outline-primary" : "btn-primary")"
@onclick="@(() => isAddingDefaultValue = false)">
<i class="fas fa-arrows-alt-h"></i>
<small class="d-block">Mapping</small>
</button>
<button type="button"
class="btn btn-sm @(isAddingDefaultValue ? "btn-warning" : "btn-outline-warning")"
@onclick="@(() => isAddingDefaultValue = true)">
<i class="fas fa-file-alt"></i>
<small class="d-block">Default</small>
</button>
</div>
<!-- Controlli per Mapping Normale -->
@if (!isAddingDefaultValue)
{
<button class="btn btn-success mb-2" @onclick="CreateMapping"
disabled="@(string.IsNullOrEmpty(selectedDbColumn) || string.IsNullOrEmpty(selectedRestProperty))">
<i class="fas fa-arrow-right"></i>
<small class="d-block">Map</small>
</button>
<button class="btn btn-danger mb-2" @onclick="RemoveMapping"
disabled="@(string.IsNullOrEmpty(selectedDbColumn) || !fieldMappings.ContainsKey(selectedDbColumn))">
<i class="fas fa-times"></i>
<small class="d-block">Remove</small>
</button>
<button class="btn btn-warning mb-2" @onclick="AutoMapFields">
<i class="fas fa-magic"></i>
<small class="d-block">Auto</small>
</button>
}
else
{
<!-- Controlli per Default Value -->
<div class="mb-2">
<small class="text-muted d-block mb-1">Tipo Valore:</small>
<select class="form-select form-select-sm mb-2" @bind="defaultValueType">
<option value="string">String</option>
<option value="int">Integer</option>
<option value="decimal">Decimal</option>
<option value="boolean">Boolean</option>
<option value="datetime">DateTime</option>
</select>
<input type="text" class="form-control form-control-sm mb-2"
placeholder="Valore default..."
@bind="defaultValueInput" />
<small class="text-muted d-block mb-2">
@if (defaultValueType == "datetime")
{
<span>Es: @DateTime.Now.ToString("yyyy-MM-dd")</span>
}
else if (defaultValueType == "boolean")
{
<span>Es: true o false</span>
}
else if (defaultValueType == "decimal")
{
<span>Es: 100.50</span>
}
</small>
</div>
<button class="btn btn-warning mb-2" @onclick="CreateDefaultValue"
disabled="@(string.IsNullOrEmpty(selectedRestProperty) || string.IsNullOrEmpty(defaultValueInput))">
<i class="fas fa-check"></i>
<small class="d-block">Set Default</small>
</button>
}
<button class="btn btn-secondary" @onclick="ClearAllMappings">
<i class="fas fa-trash"></i>
<small class="d-block">Clear</small>
<small class="d-block">Clear All</small>
</button>
</div>
</div>
@@ -965,6 +1022,10 @@
{
<span class="badge bg-success">Mapped</span>
}
@if (defaultValues.ContainsKey(property.Name))
{
<span class="badge bg-warning text-dark">Default</span>
}
</div>
</div>
</a>
@@ -1087,11 +1148,11 @@
</div>
}
<!-- Sezione Mappature Correnti --> @if (fieldMappings.Any())
<!-- Sezione Mappature Correnti --> @if (fieldMappings.Any() || defaultValues.Any())
{
<div class="mt-4">
<div class="d-flex justify-content-between align-items-center">
<h6>Mappature Correnti (@fieldMappings.Count)</h6>
<h6>Configurazione Mapping (@(fieldMappings.Count + defaultValues.Count) totali)</h6>
@if (keyFields.Any())
{
<small class="text-info">
@@ -1099,44 +1160,101 @@
</small>
}
</div>
<div class="table-responsive">
<table class="table table-sm table-striped">
<thead>
<tr>
<th>Campo Database</th>
<th>Tipo DB</th>
<th>→</th>
<th>Proprietà REST</th>
<th>Tipo REST</th>
<th>Azioni</th>
</tr>
</thead>
<tbody>
@foreach (var mapping in fieldMappings)
{
DbColumnInfo? dbColumn = null;
if (selectedSourceType == "database" && !string.IsNullOrEmpty(selectedTable))
{
dbColumn = databaseTables.ContainsKey(selectedTable) ?
databaseTables[selectedTable].FirstOrDefault(c => c.Name == mapping.Key) : null;
}
var restProperty = restEntityDetails?.Properties.FirstOrDefault(p => p.Name == mapping.Value);
<tr>
<td><strong>@mapping.Key</strong></td>
<td><small class="text-muted">@(dbColumn?.DataType ?? (selectedSourceType == "file" ? "Text" : "Unknown"))</small></td>
<td><i class="fas fa-arrow-right text-success"></i></td>
<td><strong>@mapping.Value</strong></td>
<td><small class="text-muted">@(restProperty?.Type ?? "Unknown")</small></td>
<td>
<button class="btn btn-sm btn-danger" @onclick="@(() => RemoveSpecificMapping(mapping.Key))">
<i class="fas fa-trash"></i>
</button>
</td>
</tr>
}
</tbody>
</table>
</div>
<!-- Tabella Mapping Campi -->
@if (fieldMappings.Any())
{
<div class="card mb-3">
<div class="card-header bg-light">
<i class="fas fa-arrows-alt-h"></i> <strong>Field Mappings</strong> (@fieldMappings.Count)
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-sm table-striped mb-0">
<thead>
<tr>
<th>Campo Sorgente</th>
<th>Tipo Sorgente</th>
<th>→</th>
<th>Campo Destinazione</th>
<th>Tipo Destinazione</th>
<th>Azioni</th>
</tr>
</thead>
<tbody>
@foreach (var mapping in fieldMappings)
{
DbColumnInfo? dbColumn = null;
if (selectedSourceType == "database" && !string.IsNullOrEmpty(selectedTable))
{
dbColumn = databaseTables.ContainsKey(selectedTable) ?
databaseTables[selectedTable].FirstOrDefault(c => c.Name == mapping.Key) : null;
}
var restProperty = restEntityDetails?.Properties.FirstOrDefault(p => p.Name == mapping.Value);
<tr>
<td><strong>@mapping.Key</strong></td>
<td><small class="text-muted">@(dbColumn?.DataType ?? (selectedSourceType == "file" ? "Text" : "Unknown"))</small></td>
<td><i class="fas fa-arrow-right text-success"></i></td>
<td><strong>@mapping.Value</strong></td>
<td><small class="text-muted">@(restProperty?.Type ?? "Unknown")</small></td>
<td>
<button class="btn btn-sm btn-danger" @onclick="@(() => RemoveSpecificMapping(mapping.Key))">
<i class="fas fa-trash"></i>
</button>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
}
<!-- Tabella Default Values -->
@if (defaultValues.Any())
{
<div class="card mb-3">
<div class="card-header bg-warning text-dark">
<i class="fas fa-file-alt"></i> <strong>Default Values</strong> (@defaultValues.Count)
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-sm table-striped mb-0">
<thead>
<tr>
<th>Campo Destinazione</th>
<th>Valore Default</th>
<th>Tipo Valore</th>
<th>Tipo Campo REST</th>
<th>Azioni</th>
</tr>
</thead>
<tbody>
@foreach (var defaultValue in defaultValues)
{
var restProperty = restEntityDetails?.Properties.FirstOrDefault(p => p.Name == defaultValue.Key);
var (value, valueType) = defaultValue.Value;
<tr>
<td><strong>@defaultValue.Key</strong></td>
<td><code>@(value?.ToString() ?? "null")</code></td>
<td>
<span class="badge bg-info">@valueType</span>
</td>
<td><small class="text-muted">@(restProperty?.Type ?? "Unknown")</small></td>
<td>
<button class="btn btn-sm btn-danger" @onclick="@(() => RemoveDefaultValue(defaultValue.Key))">
<i class="fas fa-trash"></i>
</button>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
}
</div>
}
@@ -1314,6 +1432,7 @@
DestinationCredentialName="@selectedRestCredential"
DestinationEndpoint="@selectedRestEntity?.Name"
FieldMappings="@GetCurrentFieldMappings()"
DefaultValues="@defaultValues"
ExternalIdRelationships="@externalIdRelationships"
SourceKeyField="@sourceKeyField"
UseRecordAssociations="@useRecordAssociations"