1790 lines
85 KiB
PHP
1790 lines
85 KiB
PHP
<?php include('include/headscript.php');
|
|
|
|
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
|
die("Invalid template ID");
|
|
}
|
|
|
|
$id = intval($_GET['id']);
|
|
$db = DBHandlerSelect::getInstance();
|
|
$pdo = $db->getConnection();
|
|
$stmt = $pdo->prepare("SELECT name, header_row, start_column, target_table, sample_xlsx, idclient, clientname, idschema, schemaname, schemajson, xls_headers FROM excel_templates WHERE id = ?");
|
|
$stmt->execute([$id]);
|
|
$template = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if (!$template) {
|
|
die("Template not found");
|
|
}
|
|
|
|
$clientName = $template['clientname'] ?: '';
|
|
$schemaName = $template['schemaname'] ?: '';
|
|
$schemajson = $template['schemajson'] ? json_decode($template['schemajson'], true) : [];
|
|
$isSchemajsonEmpty = empty(trim($template['schemajson']));
|
|
|
|
// Recupera i campi dalla tabella template_mapping
|
|
$stmt = $pdo->prepare("SELECT id, field_id, excel_column, is_manual, manual_default, auto_value, data_type, is_required, default_value, has_list, length, decimals, min_value, max_value, default_curr_date, tablename, field_label, main_field, is_visible_import, is_visible_parts FROM template_mapping WHERE template_id = ?");
|
|
$stmt->execute([$id]);
|
|
$mappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
// Recupera i fixed fields dalla tabella template_fixed_mapping
|
|
$stmt = $pdo->prepare("
|
|
SELECT id, fixed_field_key, is_manual, data_type, is_required, default_value, is_visible_import
|
|
FROM template_fixed_mapping
|
|
WHERE template_id = ?
|
|
ORDER BY id ASC
|
|
");
|
|
$stmt->execute([$id]);
|
|
$fixedMappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
$hasFixedMappings = !empty($fixedMappings);
|
|
|
|
// Recupera le colonne già associate nel database
|
|
$usedColumnsFromDB = array_filter(array_column($mappings, 'excel_column'));
|
|
|
|
// Decodifica l'header XLS salvato, se presente
|
|
$xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], true) : [];
|
|
?>
|
|
|
|
<!doctype html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
|
|
<?php include('cssinclude.php'); ?>
|
|
<title>Configure Template <?= htmlspecialchars($template['name'], ENT_QUOTES, 'UTF-8'); ?></title>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
|
|
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
|
|
|
<style>
|
|
.dropdown-select {
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
border: 1px solid #ced4da;
|
|
border-radius: 4px;
|
|
padding: 5px;
|
|
font-size: 14px;
|
|
appearance: none;
|
|
background: white url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="%23333"><path d="M7.293 4.293a1 1 0 011.414 0L10 6.586l1.293-1.293a1 1 0 111.414 1.414l-2 2a1 1 0 01-1.414 0l-2-2a1 1 0 010-1.414z"/></svg>') no-repeat right 5px center;
|
|
}
|
|
|
|
.dropdown-select:focus {
|
|
outline: none;
|
|
border-color: #80bdff;
|
|
box-shadow: 0 0 5px rgba(0, 123, 255, 0.5);
|
|
}
|
|
|
|
#loading-overlay {
|
|
display: none;
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: rgba(0, 0, 0, 0.7);
|
|
z-index: 9999;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
#loading-overlay div {
|
|
color: white;
|
|
font-size: 24px;
|
|
padding: 20px;
|
|
background: #333;
|
|
border-radius: 10px;
|
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
|
|
}
|
|
|
|
/* Force predictable column sizing */
|
|
/* Stable sizing */
|
|
#schemaFieldsTable {
|
|
width: 100%;
|
|
table-layout: fixed;
|
|
}
|
|
|
|
/* Checkbox columns = minimal */
|
|
#schemaFieldsTable th:nth-child(1),
|
|
#schemaFieldsTable td:nth-child(1),
|
|
#schemaFieldsTable th:nth-child(2),
|
|
#schemaFieldsTable td:nth-child(2),
|
|
#schemaFieldsTable th:nth-child(3),
|
|
#schemaFieldsTable td:nth-child(3) {
|
|
width: 48px;
|
|
/* 🔥 più stretto */
|
|
text-align: center;
|
|
padding: 2px 4px;
|
|
/* 🔥 meno padding */
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* Make checkbox itself not oversized */
|
|
#schemaFieldsTable td:nth-child(1) input[type="checkbox"],
|
|
#schemaFieldsTable td:nth-child(2) input[type="checkbox"],
|
|
#schemaFieldsTable td:nth-child(3) input[type="checkbox"] {
|
|
transform: scale(0.95);
|
|
margin: 0;
|
|
}
|
|
|
|
/* Title */
|
|
#schemaFieldsTable th:nth-child(4),
|
|
#schemaFieldsTable td:nth-child(4) {
|
|
width: 300px;
|
|
}
|
|
|
|
/* Type */
|
|
#schemaFieldsTable th:nth-child(5),
|
|
#schemaFieldsTable td:nth-child(5) {
|
|
width: 120px;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* Mapping = wide but NOT insane */
|
|
#schemaFieldsTable th:nth-child(6),
|
|
#schemaFieldsTable td:nth-child(6) {
|
|
width: 380px;
|
|
}
|
|
|
|
/* Default Value = wider */
|
|
#schemaFieldsTable th:nth-child(7),
|
|
#schemaFieldsTable td:nth-child(7) {
|
|
width: 320px;
|
|
}
|
|
|
|
/* selects fill the cell */
|
|
#schemaFieldsTable td:nth-child(6) .form-select,
|
|
#schemaFieldsTable td:nth-child(7) .form-control,
|
|
#schemaFieldsTable td:nth-child(7) .form-select {
|
|
width: 100% !important;
|
|
}
|
|
|
|
/* fix bootstrap/select2 width */
|
|
.select2-container {
|
|
width: 100% !important;
|
|
}
|
|
|
|
/* Make Title column narrower + ellipsis */
|
|
#schemaFieldsTable td.title-col {
|
|
max-width: 320px;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div class="wrapper">
|
|
<?php include('include/navbar.php'); ?>
|
|
<?php include('include/topbar.php'); ?>
|
|
<div class="page-wrapper">
|
|
<div class="page-content">
|
|
<?php include('top_stat_widget.php'); ?>
|
|
|
|
<div class="card radius-10">
|
|
<div class="card-header">
|
|
<div class="d-flex align-items-center">
|
|
<div>
|
|
<h6 class="mb-0">Configure Template: <span id="templateName"><?php echo htmlspecialchars($template['name']); ?></span></h6>
|
|
<p>
|
|
Client: <span id="clientName"><?php echo htmlspecialchars($clientName); ?></span> |
|
|
Schema: <span id="schemaName"><?php echo htmlspecialchars($schemaName); ?></span> |
|
|
Header Row: <span id="headerRow"><?php echo $template['header_row']; ?></span> |
|
|
Start Column: <span id="startColumn"><?php echo htmlspecialchars($template['start_column']); ?></span>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="mb-4">
|
|
<label class="form-label">Upload XLS Example:</label>
|
|
<div class="d-flex align-items-center gap-3">
|
|
<input type="file" id="xlsUpload" class="form-control" style="flex:1;">
|
|
<label class="form-check-label" style="white-space:nowrap; cursor:pointer;">
|
|
<input type="checkbox" class="form-check-input" id="autoDetectHeader" checked>
|
|
Auto-detect header row
|
|
</label>
|
|
</div>
|
|
<small id="uploadStatus">
|
|
<?php if (!empty($template['sample_xlsx'])): ?>
|
|
✅ Current file: <a href="xlstemplates/<?php echo htmlspecialchars($template['sample_xlsx']); ?>" target="_blank"><?php echo htmlspecialchars($template['sample_xlsx']); ?></a>
|
|
<?php else: ?>
|
|
No file uploaded yet.
|
|
<?php endif; ?>
|
|
</small>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="d-flex align-items-center mb-2">
|
|
<h5>Schema Fields Configuration</h5>
|
|
<button id="updateSchemaButton" class="btn btn-primary ms-2" data-empty="<?php echo $isSchemajsonEmpty ? 'true' : 'false'; ?>">
|
|
<?php echo $isSchemajsonEmpty ? 'Load Schema Details' : 'Update Schema Details'; ?>
|
|
</button>
|
|
</div>
|
|
<table id="schemaFieldsTable" class="table table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th style="width:45px; text-align:center;">Main</th>
|
|
<th style="width:45px; text-align:center;">Import</th>
|
|
<th style="width:45px; text-align:center;">Parts</th>
|
|
<th style="width:320px;">Title</th>
|
|
<th style="width:120px;">Type</th>
|
|
<th>Mapping</th>
|
|
<th>Default Value</th>
|
|
</tr>
|
|
</thead>
|
|
|
|
|
|
|
|
|
|
|
|
<tbody id="schemaFieldsBody">
|
|
<?php foreach ($mappings as $mapping): ?>
|
|
<tr>
|
|
<td class="text-center">
|
|
<input type="checkbox"
|
|
class="main-field-checkbox"
|
|
data-mapping-id="<?php echo (int)$mapping['id']; ?>"
|
|
<?php echo ((int)$mapping['main_field'] === 1) ? 'checked' : ''; ?>>
|
|
</td>
|
|
|
|
<td class="text-center">
|
|
<input type="checkbox"
|
|
class="visible-import-checkbox"
|
|
data-mapping-id="<?php echo (int)$mapping['id']; ?>"
|
|
<?php echo ((int)($mapping['is_visible_import'] ?? 1) === 1) ? 'checked' : ''; ?>>
|
|
</td>
|
|
<td class="text-center">
|
|
<input type="checkbox"
|
|
class="visible-parts-checkbox"
|
|
data-mapping-id="<?php echo (int)$mapping['id']; ?>"
|
|
<?php echo ((int)($mapping['is_visible_parts'] ?? 0) === 1) ? 'checked' : ''; ?>>
|
|
</td>
|
|
|
|
|
|
<td class="title-col">
|
|
<?php echo htmlspecialchars($mapping['field_label'] ?? 'N/A'); ?>
|
|
<?php if ((int)$mapping['is_required'] === 1): ?>
|
|
<span class="badge bg-danger ms-2">Required</span>
|
|
<?php endif; ?>
|
|
</td>
|
|
|
|
|
|
<td><?php echo htmlspecialchars($mapping['data_type'] ?? 'N/A'); ?></td>
|
|
|
|
<td>
|
|
<?php
|
|
$isSceltaMultipla = ($mapping['data_type'] === 'SceltaMultipla');
|
|
|
|
$autoValue = $mapping['auto_value'] ?? 'none';
|
|
$hasAuto = ($autoValue && $autoValue !== 'none');
|
|
|
|
$mappingValue = $isSceltaMultipla
|
|
? 'manual'
|
|
: ($hasAuto ? 'auto' : ($mapping['excel_column'] ? 'xls' : ((int)$mapping['is_manual'] === 1 ? 'manual' : '')));
|
|
?>
|
|
|
|
<select class="form-select mapping-select"
|
|
data-id="<?php echo (int)$mapping['id']; ?>"
|
|
data-field-id="<?php echo (int)$mapping['field_id']; ?>"
|
|
<?php echo $isSceltaMultipla ? 'disabled' : ''; ?>>
|
|
|
|
<?php if (!$isSceltaMultipla): ?>
|
|
<option value="">Select Option</option>
|
|
<option value="xls" <?php echo ($mappingValue === 'xls') ? 'selected' : ''; ?>>Map to XLS Column</option>
|
|
<option value="auto" <?php echo ($mappingValue === 'auto') ? 'selected' : ''; ?>>Auto value</option>
|
|
<?php endif; ?>
|
|
|
|
<option value="manual" <?php echo ($mappingValue === 'manual') ? 'selected' : ''; ?>>Manual Entry</option>
|
|
</select>
|
|
|
|
<select class="form-select xls-columns"
|
|
style="display:<?php echo ($mappingValue === 'xls') ? 'block' : 'none'; ?>"
|
|
data-id="<?php echo (int)$mapping['id']; ?>"
|
|
<?php echo $mapping['excel_column'] ? 'data-current-xls="' . htmlspecialchars($mapping['excel_column']) . '"' : ''; ?>>
|
|
</select>
|
|
|
|
<select class="form-select auto-value-select"
|
|
style="display:<?php echo ($mappingValue === 'auto') ? 'block' : 'none'; ?>; margin-top:6px;"
|
|
data-id="<?php echo (int)$mapping['id']; ?>">
|
|
<option value="none">Select auto value</option>
|
|
<option value="import_date" <?php echo ($autoValue === 'import_date') ? 'selected' : ''; ?>>Current date (import)</option>
|
|
<option value="import_time" <?php echo ($autoValue === 'import_time') ? 'selected' : ''; ?>>Current time (import)</option>
|
|
<option value="export_date" <?php echo ($autoValue === 'export_date') ? 'selected' : ''; ?>>Current date (export)</option>
|
|
<option value="export_time" <?php echo ($autoValue === 'export_time') ? 'selected' : ''; ?>>Current time (export)</option>
|
|
</select>
|
|
|
|
<?php if (!empty($mapping['excel_column'])): ?>
|
|
<span class="mapped-column" style="margin-left:5px;">
|
|
(<?php echo htmlspecialchars($mapping['excel_column']); ?>)
|
|
</span>
|
|
<button class="btn btn-danger btn-sm remove-xls"
|
|
data-id="<?php echo (int)$mapping['id']; ?>"
|
|
style="margin-left:5px;">
|
|
X
|
|
</button>
|
|
<?php endif; ?>
|
|
</td>
|
|
|
|
<td>
|
|
<?php if ($mapping['data_type'] === 'SceltaMultipla'): ?>
|
|
<select class="form-select dropdown-select manual-default"
|
|
data-id="<?php echo (int)$mapping['id']; ?>"
|
|
data-field-id="<?php echo (int)$mapping['field_id']; ?>"
|
|
data-manual-default="<?php echo htmlspecialchars($mapping['manual_default'] ?? ''); ?>"
|
|
style="display:block;">
|
|
<option value="">Seleziona...</option>
|
|
</select>
|
|
<?php else: ?>
|
|
<input type="text"
|
|
class="form-control manual-default"
|
|
placeholder="Default value"
|
|
value="<?php echo htmlspecialchars($mapping['manual_default'] ?? ''); ?>"
|
|
style="display:<?php echo ($mappingValue === 'manual') ? 'block' : 'none'; ?>"
|
|
data-field-id="<?php echo (int)$mapping['field_id']; ?>">
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Defaults for fixed fields (not part of XLS mapping) -->
|
|
<!-- Fixed fields defaults (saved in DB, autosave) -->
|
|
<div class="row mt-4">
|
|
<div class="col-12 d-flex align-items-center justify-content-between">
|
|
<h5 class="mb-0">Fixed Fields Defaults</h5>
|
|
|
|
<?php if (!$hasFixedMappings): ?>
|
|
<button id="btnAddFixedFields" class="btn btn-outline-primary">
|
|
Add Fixed Fields
|
|
</button>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<div class="col-12">
|
|
<small class="text-muted">Defaults are saved automatically when changed</small>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mt-3" id="fixedFieldsSection" style="display: <?php echo $hasFixedMappings ? 'block' : 'none'; ?>;">
|
|
<div class="col-12">
|
|
<table class="table table-striped" id="fixedFieldsTable">
|
|
<thead>
|
|
<tr>
|
|
<th style="width:120px; text-align:center;">Visible on Import</th>
|
|
<th style="width:120px; text-align:center;">Required</th>
|
|
<th>Field</th>
|
|
<th style="width:140px;">Type</th>
|
|
<th>Default Value</th>
|
|
</tr>
|
|
</thead>
|
|
|
|
<tbody>
|
|
<?php foreach ($fixedMappings as $fm): ?>
|
|
<tr>
|
|
|
|
<td class="text-center">
|
|
<input type="checkbox"
|
|
class="fixed-visible-checkbox"
|
|
data-fixed-id="<?php echo (int)$fm['id']; ?>"
|
|
<?php echo ((int)$fm['is_visible_import'] === 1) ? 'checked' : ''; ?>>
|
|
</td>
|
|
|
|
<td class="text-center">
|
|
<input type="checkbox"
|
|
class="fixed-required-checkbox"
|
|
data-fixed-id="<?php echo (int)$fm['id']; ?>"
|
|
<?php echo ((int)$fm['is_required'] === 1) ? 'checked' : ''; ?>>
|
|
</td>
|
|
|
|
<td>
|
|
<strong><?php echo htmlspecialchars($fm['fixed_field_key']); ?></strong>
|
|
</td>
|
|
|
|
|
|
<td><?php echo htmlspecialchars($fm['data_type']); ?></td>
|
|
|
|
<td>
|
|
|
|
<?php if ($fm['data_type'] === 'DATE'): ?>
|
|
|
|
<input type="date"
|
|
class="form-control fixed-default-input"
|
|
data-fixed-id="<?php echo (int)$fm['id']; ?>"
|
|
value="<?php echo htmlspecialchars($fm['default_value'] ?? ''); ?>">
|
|
|
|
<?php elseif (in_array($fm['fixed_field_key'], [
|
|
'ClienteResponsabile',
|
|
'ClienteFornitore',
|
|
'ClienteAnalisi',
|
|
'MoltiplicatorePrezzo',
|
|
'AnagraficaCertestObject',
|
|
'AnagraficaCertestService'
|
|
])): ?>
|
|
|
|
<select class="form-select fixed-default-select"
|
|
data-fixed-id="<?php echo (int)$fm['id']; ?>"
|
|
data-fixed-key="<?php echo htmlspecialchars($fm['fixed_field_key']); ?>"
|
|
data-current-value="<?php echo htmlspecialchars($fm['default_value'] ?? ''); ?>">
|
|
<option value="">Loading...</option>
|
|
</select>
|
|
|
|
<?php else: ?>
|
|
|
|
<input type="text"
|
|
class="form-control fixed-default-input"
|
|
data-fixed-id="<?php echo (int)$fm['id']; ?>"
|
|
value="<?php echo htmlspecialchars($fm['default_value'] ?? ''); ?>">
|
|
|
|
<?php endif; ?>
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
<div id="fixedSaveStatus" class="small mt-2 text-muted"></div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div class="mt-4 text-end">
|
|
<a href="templates_dashboard.php" class="btn btn-primary">⬅ Back to Template Dashboard</a>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="overlay toggle-icon"></div>
|
|
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
|
<?php include('include/footer.php'); ?>
|
|
</div>
|
|
|
|
<?php include('jsinclude.php'); ?>
|
|
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
|
<script>
|
|
let availableXlsColumns = <?php echo json_encode($xlsHeaders); ?> || [];
|
|
let usedColumnsFromDB = <?php echo json_encode($usedColumnsFromDB); ?> || [];
|
|
|
|
document.getElementById('xlsUpload').addEventListener('change', function(event) {
|
|
let file = event.target.files[0];
|
|
if (!file) return;
|
|
|
|
let formData = new FormData();
|
|
formData.append("xls_file", file);
|
|
formData.append("template_id", <?php echo $id; ?>);
|
|
|
|
let statusText = document.getElementById('uploadStatus');
|
|
statusText.innerText = "Uploading...";
|
|
|
|
fetch('upload_xls_example.php', {
|
|
method: 'POST',
|
|
body: formData
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (!data.success) {
|
|
statusText.innerText = "❌ Upload failed: " + data.message;
|
|
return;
|
|
}
|
|
statusText.innerHTML = `✅ File uploaded: <a href="xlstemplates/${data.filename}" target="_blank">${data.filename}</a>`;
|
|
processXLSX(file);
|
|
})
|
|
.catch(error => {
|
|
statusText.innerText = "❌ Upload failed. Check console.";
|
|
console.error(error);
|
|
});
|
|
});
|
|
|
|
function processXLSX(file) {
|
|
let reader = new FileReader();
|
|
reader.onload = function(e) {
|
|
let data = new Uint8Array(e.target.result);
|
|
let workbook = XLSX.read(data, {
|
|
type: 'array'
|
|
});
|
|
let sheet = workbook.Sheets[workbook.SheetNames[0]];
|
|
|
|
// Read sheet range to determine column offset
|
|
const sheetRange = XLSX.utils.decode_range(sheet['!ref'] || 'A1');
|
|
const colOffset = sheetRange.s.c; // first column index in sheet (0-based)
|
|
|
|
let sheetData = XLSX.utils.sheet_to_json(sheet, {
|
|
header: 1,
|
|
defval: "",
|
|
raw: false,
|
|
range: 0
|
|
});
|
|
|
|
// Track merged cell ranges — adjust for column offset
|
|
const merges = (sheet['!merges'] || []).map(m => ({
|
|
s: { r: m.s.r, c: m.s.c - colOffset },
|
|
e: { r: m.e.r, c: m.e.c - colOffset }
|
|
}));
|
|
console.log('Sheet column offset:', colOffset, '(first col:', String.fromCharCode(65 + colOffset) + ')');
|
|
|
|
const useAutoDetect = document.getElementById('autoDetectHeader').checked;
|
|
|
|
if (!useAutoDetect) {
|
|
// Use values from template settings
|
|
let rowIndex = parseInt(document.getElementById('headerRow').textContent) || 1;
|
|
let startColumn = parseInt(document.getElementById('startColumn').textContent) || 1;
|
|
console.log(`Manual mode: row ${rowIndex}, startCol ${startColumn}`);
|
|
|
|
if (!sheetData[rowIndex - 1]) {
|
|
document.getElementById('schemaFieldsBody').querySelectorAll('select.xls-columns').forEach(select => {
|
|
select.innerHTML = '<option value="">Nessuna intestazione trovata</option>';
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Build logical headers, collapsing merged cells
|
|
const mergeStartMapManual = {};
|
|
merges.forEach(m => {
|
|
if ((rowIndex - 1) >= m.s.r && (rowIndex - 1) <= m.e.r) {
|
|
for (let c = m.s.c; c <= m.e.c; c++) {
|
|
mergeStartMapManual[c] = m.s.c;
|
|
}
|
|
}
|
|
});
|
|
let headers = [];
|
|
const rawRowManual = sheetData[rowIndex - 1] || [];
|
|
const seenManual = new Set();
|
|
for (let c = startColumn - 1; c < rawRowManual.length; c++) {
|
|
const ms = mergeStartMapManual[c];
|
|
if (ms !== undefined) {
|
|
if (seenManual.has(ms)) continue;
|
|
seenManual.add(ms);
|
|
const v = rawRowManual[ms];
|
|
headers.push(v === undefined ? "" : String(v).replace(/[\r\n\t]+/g, ' ').trim());
|
|
} else {
|
|
const v = rawRowManual[c];
|
|
headers.push(v === undefined ? "" : String(v).replace(/[\r\n\t]+/g, ' ').trim());
|
|
}
|
|
}
|
|
while (headers.length > 0 && headers[headers.length - 1] === '') {
|
|
headers.pop();
|
|
}
|
|
headers = headers.map((h, i) => {
|
|
h = h.replace(/[\r\n\t]+/g, ' ').trim();
|
|
return h === '' ? `__empty_${i + 1}__` : h;
|
|
});
|
|
console.log("Intestazioni estratte (manual):", headers);
|
|
availableXlsColumns = [...headers];
|
|
usedColumnsFromDB = [];
|
|
saveXlsHeaders(headers, rowIndex, startColumn);
|
|
updateXlsDropdowns();
|
|
return;
|
|
}
|
|
|
|
// Collect field_label titles from the schema table
|
|
const knownLabels = [];
|
|
document.querySelectorAll('.title-col').forEach(td => {
|
|
const v = (td.textContent || '').trim().toLowerCase().replace(/\s*required\s*$/i, '').trim();
|
|
if (v && v !== 'n/a') knownLabels.push(v);
|
|
});
|
|
const uniqueLabels = [...new Set(knownLabels)];
|
|
|
|
console.group('🔍 Auto-detect header row');
|
|
console.log('Sheet name:', workbook.SheetNames[0]);
|
|
console.log('Total rows in sheet:', sheetData.length);
|
|
console.log('Labels from schema field titles:', knownLabels);
|
|
console.log('Unique labels to match against:', uniqueLabels);
|
|
|
|
// Auto-detect: find the row with most matches, fallback to most non-empty unique text cells
|
|
let bestRow = 0;
|
|
let bestScore = 0;
|
|
let bestStartCol = 0;
|
|
let fallbackRow = 0;
|
|
let fallbackScore = 0;
|
|
let fallbackStartCol = 0;
|
|
const scanLimit = Math.min(sheetData.length, 50);
|
|
const rowScores = [];
|
|
|
|
for (let r = 0; r < scanLimit; r++) {
|
|
const row = sheetData[r] || [];
|
|
let score = 0;
|
|
let firstNonEmpty = -1;
|
|
let nonEmptyCount = 0;
|
|
const matched = [];
|
|
const cellValues = [];
|
|
const seen = new Set();
|
|
for (let c = 0; c < row.length; c++) {
|
|
const cellVal = String(row[c] || '').trim();
|
|
const cellLower = cellVal.toLowerCase();
|
|
if (cellVal) {
|
|
cellValues.push(`[${c}]="${cellVal}"`);
|
|
if (firstNonEmpty < 0) firstNonEmpty = c;
|
|
// Count unique short text cells (likely headers, not data/descriptions)
|
|
if (cellVal.length < 80 && !seen.has(cellLower)) {
|
|
nonEmptyCount++;
|
|
seen.add(cellLower);
|
|
}
|
|
}
|
|
if (cellLower && uniqueLabels.includes(cellLower)) {
|
|
score++;
|
|
matched.push(cellVal);
|
|
}
|
|
}
|
|
rowScores.push({ row: r + 1, score, nonEmpty: nonEmptyCount, matched, firstNonEmpty: firstNonEmpty + 1, cells: cellValues });
|
|
if (score > bestScore) {
|
|
bestScore = score;
|
|
bestRow = r;
|
|
bestStartCol = firstNonEmpty >= 0 ? firstNonEmpty : 0;
|
|
}
|
|
// Fallback: row with most unique short text cells (likely header row)
|
|
if (nonEmptyCount > fallbackScore) {
|
|
fallbackScore = nonEmptyCount;
|
|
fallbackRow = r;
|
|
fallbackStartCol = firstNonEmpty >= 0 ? firstNonEmpty : 0;
|
|
}
|
|
}
|
|
|
|
// If no label matches, use fallback (most populated row)
|
|
if (bestScore === 0 && fallbackScore > 0) {
|
|
bestRow = fallbackRow;
|
|
bestStartCol = fallbackStartCol;
|
|
console.log(`⚠️ No label matches found. Using fallback: row with most headers (${fallbackScore} unique cells)`);
|
|
}
|
|
|
|
console.log('All rows scanned:');
|
|
console.table(rowScores.filter(r => r.nonEmpty > 0).map(r => ({
|
|
row: r.row,
|
|
labelMatches: r.score,
|
|
uniqueCells: r.nonEmpty,
|
|
matched: r.matched.join(', '),
|
|
firstCol: r.firstNonEmpty
|
|
})));
|
|
console.log(`✅ Result: row ${bestRow + 1}, startCol ${bestStartCol + 1}, labelScore ${bestScore}/${uniqueLabels.length}, fallbackScore ${fallbackScore}`);
|
|
console.log('Selected row raw data:', sheetData[bestRow]);
|
|
console.groupEnd();
|
|
|
|
// Update display
|
|
document.getElementById('headerRow').textContent = bestRow + 1;
|
|
document.getElementById('startColumn').textContent = bestStartCol + 1;
|
|
|
|
if (!sheetData[bestRow]) {
|
|
document.getElementById('schemaFieldsBody').querySelectorAll('select.xls-columns').forEach(select => {
|
|
select.innerHTML = '<option value="">Nessuna intestazione trovata</option>';
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Build logical columns: each merge = one column, each non-merged cell = one column
|
|
let headers = [];
|
|
const rawRow = sheetData[bestRow] || [];
|
|
|
|
// Map each physical column to its merge start (or itself if not merged)
|
|
const mergeStartMap = {}; // physCol -> startCol of its merge
|
|
merges.forEach(m => {
|
|
if (bestRow >= m.s.r && bestRow <= m.e.r) {
|
|
for (let c = m.s.c; c <= m.e.c; c++) {
|
|
mergeStartMap[c] = m.s.c;
|
|
}
|
|
}
|
|
});
|
|
|
|
const seen = new Set();
|
|
for (let c = bestStartCol; c < rawRow.length; c++) {
|
|
const mergeStart = mergeStartMap[c];
|
|
const cleanVal = (v) => (v === undefined ? "" : String(v).replace(/[\r\n\t]+/g, ' ').trim());
|
|
if (mergeStart !== undefined) {
|
|
// Part of a merge — only take the first occurrence
|
|
if (seen.has(mergeStart)) continue;
|
|
seen.add(mergeStart);
|
|
headers.push(cleanVal(rawRow[mergeStart]));
|
|
} else {
|
|
headers.push(cleanVal(rawRow[c]));
|
|
}
|
|
}
|
|
// Trim trailing empty columns
|
|
while (headers.length > 0 && headers[headers.length - 1] === '') {
|
|
headers.pop();
|
|
}
|
|
// Final clean + name empty columns to match __empty_N__ convention
|
|
headers = headers.map((h, i) => {
|
|
h = h.replace(/[\r\n\t]+/g, ' ').trim();
|
|
return h === '' ? `__empty_${i + 1}__` : h;
|
|
});
|
|
console.log("Logical headers:", headers, `(${headers.length} columns from ${rawRow.length} physical)`);
|
|
availableXlsColumns = [...headers];
|
|
usedColumnsFromDB = [];
|
|
saveXlsHeaders(headers, bestRow + 1, bestStartCol + 1);
|
|
updateXlsDropdowns();
|
|
};
|
|
reader.readAsArrayBuffer(file);
|
|
}
|
|
|
|
function saveXlsHeaders(headers, headerRow, startColumn) {
|
|
const payload = {
|
|
template_id: <?php echo $id; ?>,
|
|
xls_headers: JSON.stringify(headers)
|
|
};
|
|
if (headerRow !== undefined) payload.header_row = headerRow;
|
|
if (startColumn !== undefined) payload.start_column = startColumn;
|
|
|
|
fetch('update_xls_headers.php', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(payload)
|
|
}).then(response => response.json())
|
|
.then(data => {
|
|
if (!data.success) console.error("❌ Error saving XLS headers:", data.message);
|
|
else console.log("✅ Saved headers, header_row:", headerRow, "start_column:", startColumn);
|
|
})
|
|
.catch(error => console.error("❌ Fetch error:", error));
|
|
}
|
|
|
|
function updateXlsDropdowns() {
|
|
let usedColumns = Array.from(document.querySelectorAll('select.xls-columns'))
|
|
.filter(select => select.style.display === 'block' && select.value)
|
|
.map(select => select.value)
|
|
.concat(usedColumnsFromDB);
|
|
|
|
document.querySelectorAll('select.xls-columns').forEach(select => {
|
|
let currentValue = select.value || select.dataset.currentXls || '';
|
|
let options = availableXlsColumns
|
|
.map((col, origIdx) => ({ col, origIdx }))
|
|
.filter(({ col }) => !usedColumns.includes(col) || col === currentValue)
|
|
.map(({ col, origIdx }) => {
|
|
const clean = col.replace(/[\r\n\t]+/g, ' ').trim();
|
|
const isEmpty = clean === '';
|
|
const colNum = origIdx + 1;
|
|
const val = isEmpty ? `__empty_${colNum}__` : clean;
|
|
const label = isEmpty ? `(empty column ${colNum})` : clean;
|
|
const isSelected = (isEmpty ? val === currentValue : col === currentValue) ? 'selected' : '';
|
|
return `<option value="${val}" ${isSelected}>${label}</option>`;
|
|
})
|
|
.join('');
|
|
select.innerHTML = '<option value="">Select XLS Column</option>' + options;
|
|
select.dataset.currentXls = currentValue;
|
|
if (currentValue && !options.includes(currentValue)) {
|
|
select.value = '';
|
|
}
|
|
});
|
|
}
|
|
|
|
function initSelect2ForSchemaDropdowns() {
|
|
// dropdown-select = SceltaMultipla (manual default)
|
|
if (window.jQuery && $.fn.select2) {
|
|
$('.dropdown-select').each(function() {
|
|
if ($(this).hasClass('select2-hidden-accessible')) return; // già inizializzato
|
|
$(this).select2({
|
|
width: '100%',
|
|
placeholder: 'Seleziona...',
|
|
allowClear: true
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
function initSelect2ForFixedDropdowns() {
|
|
if (!(window.jQuery && $.fn.select2)) return;
|
|
|
|
$('.fixed-default-select').each(function() {
|
|
const $el = $(this);
|
|
|
|
// se era già Select2, lo resettiamo (utile quando renderizzi righe nuove)
|
|
if ($el.hasClass('select2-hidden-accessible')) {
|
|
$el.select2('destroy');
|
|
}
|
|
|
|
$el.select2({
|
|
width: '100%',
|
|
placeholder: 'Seleziona...',
|
|
allowClear: true
|
|
});
|
|
});
|
|
}
|
|
|
|
function saveFixedDefaultSelect(selectEl) {
|
|
const id = selectEl.dataset.fixedId;
|
|
const value = selectEl.value;
|
|
|
|
fetch('update_fixed_field.php', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
id,
|
|
field: 'default_value',
|
|
value
|
|
})
|
|
})
|
|
.then(r => r.json())
|
|
.then(d => {
|
|
if (!d.success) throw new Error(d.message || 'Update failed');
|
|
// aggiorno anche il dataset così rimane coerente
|
|
selectEl.dataset.currentValue = value;
|
|
fixedStatus('✅ Saved');
|
|
})
|
|
.catch(err => {
|
|
console.error(err);
|
|
fixedStatus('❌ Save error', true);
|
|
});
|
|
}
|
|
|
|
// Eventi Select2 (jQuery)
|
|
if (window.jQuery) {
|
|
$(document).on('select2:select select2:clear', '.fixed-default-select', function() {
|
|
saveFixedDefaultSelect(this);
|
|
});
|
|
|
|
// fallback (nel caso cambi senza select2)
|
|
$(document).on('change', '.fixed-default-select', function() {
|
|
saveFixedDefaultSelect(this);
|
|
});
|
|
}
|
|
|
|
|
|
document.addEventListener("DOMContentLoaded", function() {
|
|
let templateId = <?php echo $id; ?>;
|
|
let schemaId = <?php echo json_encode($template['idschema'] ?? 0); ?>;
|
|
let isSchemajsonEmpty = <?php echo json_encode($isSchemajsonEmpty); ?>;
|
|
|
|
// Crea l'overlay di caricamento
|
|
const loadingOverlay = document.createElement('div');
|
|
loadingOverlay.id = 'loading-overlay';
|
|
loadingOverlay.innerHTML = '<div>Loading Dropdown Options...</div>';
|
|
document.body.appendChild(loadingOverlay);
|
|
|
|
async function populateDropdowns() {
|
|
const dropdowns = document.querySelectorAll('.dropdown-select');
|
|
if (dropdowns.length === 0) {
|
|
console.warn('Nessun dropdown di tipo SceltaMultipla trovato.');
|
|
return;
|
|
}
|
|
|
|
console.log(`Trovati ${dropdowns.length} dropdown da popolare.`);
|
|
|
|
const uniqueFieldIds = [...new Set(Array.from(dropdowns).map(d => d.getAttribute('data-field-id')))].filter(fieldId => fieldId);
|
|
|
|
if (uniqueFieldIds.length === 0) {
|
|
console.warn('Nessun field_id valido trovato per i dropdown.');
|
|
dropdowns.forEach(dropdown => {
|
|
dropdown.innerHTML = '<option value="">Nessun field_id valido</option>';
|
|
dropdown.disabled = true;
|
|
});
|
|
return;
|
|
}
|
|
|
|
console.log('Field IDs univoci:', uniqueFieldIds);
|
|
|
|
// Mostra l'overlay di caricamento
|
|
const loadingOverlay = document.getElementById('loading-overlay');
|
|
loadingOverlay.style.display = 'flex';
|
|
|
|
let dropdownData = {};
|
|
|
|
try {
|
|
// Usa field_ids come previsto dal backend
|
|
const fieldIdsQuery = uniqueFieldIds.join(',');
|
|
console.log(`Recupero dati per field_ids: ${fieldIdsQuery}`);
|
|
const response = await fetch(`get_customfield_values.php?field_ids=${fieldIdsQuery}`);
|
|
if (!response.ok) {
|
|
throw new Error(`Errore HTTP: ${response.status} ${response.statusText}`);
|
|
}
|
|
const data = await response.json();
|
|
if (data.error) {
|
|
throw new Error(`Errore API: ${data.error}`);
|
|
}
|
|
dropdownData = data; // data è un oggetto come { "146": [], "156": [...] }
|
|
console.log('Dati totali restituiti:', dropdownData);
|
|
|
|
// Popola i dropdown
|
|
dropdowns.forEach(dropdown => {
|
|
const fieldId = dropdown.getAttribute('data-field-id');
|
|
const manualDefault = dropdown.getAttribute('data-manual-default') || '';
|
|
console.log(`Popolamento dropdown per field_id: ${fieldId}, manual_default: ${manualDefault}`);
|
|
|
|
dropdown.innerHTML = '<option value="">Seleziona...</option>';
|
|
|
|
if (!fieldId || !dropdownData[fieldId] || dropdownData[fieldId].length === 0) {
|
|
console.warn(`Nessun dato disponibile per field_id ${fieldId}`);
|
|
dropdown.innerHTML = `<option value="">Nessun valore (field_id ${fieldId} vuoto)</option>`;
|
|
dropdown.disabled = true; // Disabilita per evitare selezioni inutili
|
|
return;
|
|
}
|
|
|
|
dropdownData[fieldId].forEach(value => {
|
|
const option = document.createElement('option');
|
|
option.value = value.IdCustomFieldsValue;
|
|
option.textContent = value.Valore;
|
|
if (manualDefault && String(value.IdCustomFieldsValue) === String(manualDefault)) {
|
|
option.selected = true;
|
|
}
|
|
dropdown.appendChild(option);
|
|
});
|
|
dropdown.disabled = false;
|
|
});
|
|
} catch (error) {
|
|
console.error('Errore generale nel caricamento dei dropdown:', error);
|
|
dropdowns.forEach(dropdown => {
|
|
dropdown.innerHTML = '<option value="">Errore nel caricamento</option>';
|
|
dropdown.disabled = true;
|
|
});
|
|
} finally {
|
|
// attiva Select2 dopo aver messo le options
|
|
initSelect2ForSchemaDropdowns();
|
|
|
|
console.log('Nascondo overlay di caricamento.');
|
|
loadingOverlay.style.display = 'none';
|
|
}
|
|
|
|
}
|
|
|
|
// Carica i dropdown con overlay
|
|
async function loadDropdownsWithOverlay() {
|
|
console.log('Inizio caricamento tendine');
|
|
loadingOverlay.style.display = 'flex';
|
|
await new Promise(resolve => setTimeout(resolve, 500));
|
|
await populateDropdowns();
|
|
console.log('Caricamento tendine completato');
|
|
loadingOverlay.style.display = 'none';
|
|
}
|
|
|
|
loadDropdownsWithOverlay();
|
|
|
|
// =======================
|
|
// SAVE: SceltaMultipla dropdown (Select2-safe)
|
|
// =======================
|
|
|
|
// 1) listener nativo a livello document (più robusto del delegation su tbody)
|
|
document.addEventListener('change', function(event) {
|
|
const el = event.target;
|
|
if (!(el && el.matches && el.matches('select.manual-default.dropdown-select'))) return;
|
|
|
|
const tr = el.closest('tr');
|
|
const mappingId =
|
|
el.getAttribute('data-id') ||
|
|
tr?.querySelector('.mapping-select')?.getAttribute('data-id');
|
|
|
|
const xlsSelect = tr?.querySelector('.xls-columns');
|
|
|
|
console.log('[SceltaMultipla change] saving', {
|
|
mappingId,
|
|
value: el.value
|
|
});
|
|
|
|
if (!mappingId) {
|
|
console.error('❌ Missing mappingId for SceltaMultipla dropdown');
|
|
return;
|
|
}
|
|
|
|
saveMapping(mappingId, 'manual', el.value, xlsSelect ? xlsSelect.value : null);
|
|
});
|
|
|
|
// 2) eventi Select2 (quando Select2 “mangia” il change)
|
|
if (window.jQuery) {
|
|
$(document).on('select2:select select2:clear', 'select.manual-default.dropdown-select', function() {
|
|
const el = this;
|
|
const tr = el.closest('tr');
|
|
const mappingId =
|
|
el.getAttribute('data-id') ||
|
|
tr?.querySelector('.mapping-select')?.getAttribute('data-id');
|
|
|
|
const xlsSelect = tr?.querySelector('.xls-columns');
|
|
|
|
console.log('[SceltaMultipla select2] saving', {
|
|
mappingId,
|
|
value: el.value
|
|
});
|
|
|
|
if (!mappingId) {
|
|
console.error('❌ Missing mappingId for SceltaMultipla dropdown (select2)');
|
|
return;
|
|
}
|
|
|
|
saveMapping(mappingId, 'manual', el.value, xlsSelect ? xlsSelect.value : null);
|
|
});
|
|
}
|
|
|
|
|
|
async function loadClientAndSchemaNames() {
|
|
if (<?php echo json_encode($template['idclient'] ?? 0); ?> > 0) {
|
|
let response = await fetch(`get_clienti.php?id=<?php echo $template['idclient']; ?>`);
|
|
let data = await response.json();
|
|
if (response.ok && data.value?.length) document.getElementById('clientName').textContent = data.value[0].Nominativo || 'N/A';
|
|
}
|
|
if (schemaId > 0) {
|
|
let response = await fetch(`get_schemi.php?id=${schemaId}`);
|
|
let data = await response.json();
|
|
if (response.ok && data.value?.length) document.getElementById('schemaName').textContent = data.value[0].Nome || 'N/A';
|
|
}
|
|
}
|
|
|
|
loadClientAndSchemaNames();
|
|
|
|
async function updateSchemaDetails() {
|
|
if (!schemaId) {
|
|
document.getElementById('schemaFieldsBody').innerHTML = '<tr><td colspan="6" class="text-warning">No schema associated.</td></tr>';
|
|
return;
|
|
}
|
|
|
|
let updateSchemaButton = document.getElementById('updateSchemaButton');
|
|
updateSchemaButton.disabled = true;
|
|
updateSchemaButton.textContent = 'Loading...';
|
|
|
|
try {
|
|
let response = await fetch(`get_schema_details.php?id=${schemaId}`);
|
|
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
|
|
let data = JSON.parse(await response.text());
|
|
if (!data.SchemiCustomFieldsDettagli) throw new Error('Missing "SchemiCustomFieldsDettagli"');
|
|
|
|
await saveSchemaJson(templateId, JSON.stringify(data));
|
|
alert('Schema updated successfully. Refresh the page to see changes.');
|
|
} catch (error) {
|
|
document.getElementById('schemaFieldsBody').innerHTML = '<tr><td colspan="6" class="text-danger">Error: ' + error.message + '</td></tr>';
|
|
} finally {
|
|
updateSchemaButton.disabled = false;
|
|
updateSchemaButton.textContent = 'Update Schema Details';
|
|
}
|
|
}
|
|
|
|
async function saveSchemaJson(templateId, schemaJson) {
|
|
let response = await fetch('update_schemajson.php', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
template_id: templateId,
|
|
schemajson: schemaJson
|
|
})
|
|
});
|
|
let data = await response.json();
|
|
if (!data.success) throw new Error(data.message || 'Failed to save schema JSON');
|
|
}
|
|
|
|
document.getElementById('updateSchemaButton').addEventListener('click', updateSchemaDetails);
|
|
|
|
document.getElementById('schemaFieldsBody').addEventListener('change', function(event) {
|
|
if (event.target.classList.contains('mapping-select')) {
|
|
let tr = event.target.closest('tr');
|
|
let mappingId = event.target.getAttribute('data-id');
|
|
let xlsSelect = tr.querySelector('.xls-columns');
|
|
let manualInput = tr.querySelector('.manual-default');
|
|
let autoSelect = tr.querySelector('.auto-value-select');
|
|
let mappedColumn = tr.querySelector('.mapped-column');
|
|
let removeBtn = tr.querySelector('.remove-xls');
|
|
if (event.target.value === 'xls') {
|
|
xlsSelect.style.display = 'block';
|
|
if (autoSelect) autoSelect.style.display = 'none';
|
|
|
|
manualInput.style.display = 'none';
|
|
manualInput.value = '';
|
|
|
|
if (mappedColumn) mappedColumn.style.display = 'none';
|
|
if (removeBtn) removeBtn.style.display = xlsSelect.value ? 'inline-block' : 'none';
|
|
|
|
} else if (event.target.value === 'manual') {
|
|
xlsSelect.style.display = 'none';
|
|
if (autoSelect) autoSelect.style.display = 'none';
|
|
|
|
manualInput.style.display = 'block';
|
|
|
|
if (mappedColumn) mappedColumn.style.display = 'none';
|
|
if (removeBtn) removeBtn.style.display = 'none';
|
|
|
|
} else if (event.target.value === 'auto') {
|
|
xlsSelect.style.display = 'none';
|
|
xlsSelect.value = ''; // libera UI
|
|
|
|
manualInput.style.display = 'none';
|
|
manualInput.value = '';
|
|
|
|
if (mappedColumn) mappedColumn.style.display = 'none';
|
|
if (removeBtn) removeBtn.style.display = 'none';
|
|
|
|
if (autoSelect) autoSelect.style.display = 'block';
|
|
|
|
} else {
|
|
xlsSelect.style.display = 'none';
|
|
if (autoSelect) autoSelect.style.display = 'none';
|
|
|
|
manualInput.style.display = 'none';
|
|
manualInput.value = '';
|
|
|
|
if (mappedColumn) mappedColumn.style.display = 'none';
|
|
if (removeBtn) removeBtn.style.display = 'none';
|
|
}
|
|
saveMapping(
|
|
mappingId,
|
|
event.target.value,
|
|
manualInput.value,
|
|
xlsSelect.value,
|
|
autoSelect ? autoSelect.value : null
|
|
);
|
|
updateXlsDropdowns();
|
|
} else if (event.target.classList.contains('main-field-checkbox')) {
|
|
const checkbox = event.target;
|
|
const mappingId = checkbox.dataset.mappingId;
|
|
const value = checkbox.checked ? 1 : 0;
|
|
|
|
// Se checked, deseleziona tutti gli altri visivamente
|
|
if (checkbox.checked) {
|
|
document.querySelectorAll('.main-field-checkbox').forEach(cb => {
|
|
if (cb !== checkbox) cb.checked = false;
|
|
});
|
|
}
|
|
|
|
// Salva l'aggiornamento
|
|
fetch('update_main_field.php', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
template_id: <?php echo $id; ?>,
|
|
mapping_id: mappingId,
|
|
value: value
|
|
})
|
|
}).then(response => response.json())
|
|
.then(data => {
|
|
if (!data.success) {
|
|
console.error("❌ Error updating main_field:", data.message);
|
|
checkbox.checked = !checkbox.checked;
|
|
document.querySelectorAll('.main-field-checkbox').forEach(cb => {
|
|
cb.checked = cb.dataset.originalChecked === 'true';
|
|
});
|
|
} else {
|
|
document.querySelectorAll('.main-field-checkbox').forEach(cb => {
|
|
cb.dataset.originalChecked = cb.checked;
|
|
});
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error("❌ Fetch error:", error);
|
|
checkbox.checked = !checkbox.checked;
|
|
document.querySelectorAll('.main-field-checkbox').forEach(cb => {
|
|
cb.checked = cb.dataset.originalChecked === 'true';
|
|
});
|
|
});
|
|
} else if (event.target.classList.contains('visible-parts-checkbox')) {
|
|
const checkbox = event.target;
|
|
const mappingId = checkbox.dataset.mappingId;
|
|
const value = checkbox.checked ? 1 : 0;
|
|
|
|
// salva stato per rollback
|
|
const prevChecked = checkbox.checked;
|
|
|
|
// ✅ UI: se sto mettendo a 1, tolgo la spunta a tutti gli altri SUBITO
|
|
if (value === 1) {
|
|
document.querySelectorAll('.visible-parts-checkbox').forEach(cb => {
|
|
if (cb !== checkbox) cb.checked = false;
|
|
});
|
|
}
|
|
|
|
fetch('update_visible_parts.php', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
template_id: <?php echo $id; ?>,
|
|
mapping_id: mappingId,
|
|
value: value
|
|
})
|
|
})
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (!data.success) {
|
|
console.error("❌ Error updating is_visible_parts:", data.message);
|
|
|
|
// rollback UI
|
|
checkbox.checked = !prevChecked;
|
|
|
|
// se avevo tolto le spunte agli altri, ricarico per riallineare la UI al DB
|
|
// (semplice e safe)
|
|
location.reload();
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error("❌ Fetch error:", error);
|
|
|
|
// rollback UI
|
|
checkbox.checked = !prevChecked;
|
|
location.reload();
|
|
});
|
|
} else if (event.target.classList.contains('visible-parts-checkbox')) {
|
|
const checkbox = event.target;
|
|
const mappingId = checkbox.dataset.mappingId;
|
|
const value = checkbox.checked ? 1 : 0;
|
|
|
|
fetch('update_visible_parts.php', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
template_id: <?php echo $id; ?>,
|
|
mapping_id: mappingId,
|
|
value: value
|
|
})
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (!data.success) {
|
|
console.error("❌ Error updating is_visible_parts:", data.message);
|
|
checkbox.checked = !checkbox.checked;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error("❌ Fetch error:", error);
|
|
checkbox.checked = !checkbox.checked;
|
|
});
|
|
}
|
|
|
|
});
|
|
|
|
// Salva lo stato originale dei checkbox al caricamento
|
|
document.querySelectorAll('.main-field-checkbox').forEach(cb => {
|
|
cb.dataset.originalChecked = cb.checked;
|
|
});
|
|
// AUTO VALUE select change -> save auto_value
|
|
document.getElementById('schemaFieldsBody').addEventListener('change', function(event) {
|
|
if (!event.target.classList.contains('auto-value-select')) return;
|
|
|
|
const tr = event.target.closest('tr');
|
|
const mappingId = event.target.getAttribute('data-id');
|
|
|
|
const xlsSelect = tr.querySelector('.xls-columns');
|
|
const manualInput = tr.querySelector('.manual-default');
|
|
|
|
saveMapping(
|
|
mappingId,
|
|
'auto',
|
|
manualInput ? manualInput.value : '',
|
|
xlsSelect ? xlsSelect.value : null,
|
|
event.target.value
|
|
);
|
|
});
|
|
document.getElementById('schemaFieldsBody').addEventListener('change', function(event) {
|
|
if (event.target.classList.contains('xls-columns')) {
|
|
let tr = event.target.closest('tr');
|
|
let mappingId = event.target.getAttribute('data-id');
|
|
let manualInput = tr.querySelector('.manual-default');
|
|
let autoSelect = tr.querySelector('.auto-value-select');
|
|
let mappedColumn = tr.querySelector('.mapped-column');
|
|
let removeBtn = tr.querySelector('.remove-xls');
|
|
|
|
if (!mappedColumn) {
|
|
mappedColumn = document.createElement('span');
|
|
mappedColumn.className = 'mapped-column';
|
|
mappedColumn.style.marginLeft = '5px';
|
|
tr.querySelector('td:nth-child(5)').appendChild(mappedColumn);
|
|
}
|
|
if (!removeBtn) {
|
|
removeBtn = document.createElement('button');
|
|
removeBtn.className = 'btn btn-danger btn-sm remove-xls';
|
|
removeBtn.textContent = 'X';
|
|
removeBtn.style.marginLeft = '5px';
|
|
removeBtn.setAttribute('data-id', mappingId);
|
|
tr.querySelector('td:nth-child(5)').appendChild(removeBtn);
|
|
|
|
removeBtn.addEventListener('click', function(e) {
|
|
let tr = e.target.closest('tr');
|
|
let xlsSelect = tr.querySelector('.xls-columns');
|
|
let mappingSelect = tr.querySelector('.mapping-select');
|
|
xlsSelect.value = '';
|
|
xlsSelect.style.display = 'none';
|
|
mappingSelect.value = '';
|
|
if (mappedColumn) mappedColumn.style.display = 'none';
|
|
e.target.style.display = 'none';
|
|
saveMapping(mappingId, '', '', null);
|
|
updateXlsDropdowns();
|
|
});
|
|
}
|
|
|
|
console.log("XLS Column changed:", {
|
|
id: mappingId,
|
|
value: event.target.value
|
|
});
|
|
if (mappedColumn) {
|
|
mappedColumn.textContent = event.target.value ? `(${event.target.value})` : '';
|
|
mappedColumn.style.display = event.target.value ? 'inline' : 'none';
|
|
}
|
|
if (removeBtn) removeBtn.style.display = event.target.value ? 'inline-block' : 'none';
|
|
const mappingSelect = tr.querySelector('.mapping-select');
|
|
if (mappingSelect && mappingSelect.value !== 'xls') {
|
|
return; // non salvare XLS se non siamo in modalità XLS
|
|
}
|
|
saveMapping(mappingId, 'xls', manualInput.value, event.target.value);
|
|
updateXlsDropdowns();
|
|
}
|
|
});
|
|
|
|
document.getElementById('schemaFieldsBody').addEventListener('change', function(event) {
|
|
if (event.target.classList.contains('manual-default') && event.target.tagName === 'SELECT') {
|
|
let tr = event.target.closest('tr');
|
|
let mappingId = event.target.getAttribute('data-id');
|
|
let xlsSelect = tr.querySelector('.xls-columns');
|
|
console.log("Manual default dropdown changed:", {
|
|
id: mappingId,
|
|
value: event.target.value
|
|
});
|
|
saveMapping(mappingId, 'manual', event.target.value, xlsSelect.value);
|
|
}
|
|
});
|
|
|
|
document.getElementById('schemaFieldsBody').addEventListener('input', function(event) {
|
|
if (event.target.classList.contains('manual-default') && event.target.tagName === 'INPUT') {
|
|
let tr = event.target.closest('tr');
|
|
let mappingId = tr.querySelector('.mapping-select').getAttribute('data-id');
|
|
let xlsSelect = tr.querySelector('.xls-columns');
|
|
console.log("Manual default input changed:", {
|
|
id: mappingId,
|
|
value: event.target.value
|
|
});
|
|
saveMapping(mappingId, 'manual', event.target.value, xlsSelect.value);
|
|
}
|
|
});
|
|
|
|
document.getElementById('schemaFieldsBody').addEventListener('click', function(event) {
|
|
if (event.target.classList.contains('remove-xls')) {
|
|
let mappingId = event.target.getAttribute('data-id');
|
|
let tr = event.target.closest('tr');
|
|
let xlsSelect = tr.querySelector('.xls-columns');
|
|
let mappingSelect = tr.querySelector('.mapping-select');
|
|
let mappedColumn = tr.querySelector('.mapped-column');
|
|
xlsSelect.value = '';
|
|
xlsSelect.style.display = 'none';
|
|
mappingSelect.value = '';
|
|
if (mappedColumn) mappedColumn.style.display = 'none';
|
|
event.target.style.display = 'none';
|
|
console.log("Removing XLS mapping:", {
|
|
id: mappingId
|
|
});
|
|
saveMapping(mappingId, '', '', null);
|
|
updateXlsDropdowns();
|
|
}
|
|
});
|
|
|
|
function saveMapping(mappingId, mappingType, defaultValue, excelColumn, autoValue) {
|
|
console.log("Saving mapping:", {
|
|
id: mappingId,
|
|
mapping_type: mappingType,
|
|
excel_column: excelColumn,
|
|
manual_default: defaultValue
|
|
});
|
|
fetch('save_mapping_json.php', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
id: mappingId,
|
|
mapping_type: mappingType,
|
|
excel_column: mappingType === 'xls' ? excelColumn : null,
|
|
manual_default: mappingType === 'manual' ? defaultValue : null,
|
|
auto_value: mappingType === 'auto' ? (autoValue || 'none') : 'none',
|
|
tablename: "<?php echo $template['target_table']; ?>"
|
|
})
|
|
}).then(response => response.json())
|
|
.then(data => {
|
|
console.log("Save response:", data);
|
|
if (!data.success) console.error("❌ Error saving mapping:", data.message);
|
|
if (data.success && excelColumn) {
|
|
usedColumnsFromDB = usedColumnsFromDB.filter(col => col !== excelColumn);
|
|
usedColumnsFromDB.push(excelColumn);
|
|
updateXlsDropdowns();
|
|
}
|
|
})
|
|
.catch(error => console.error("❌ Fetch error:", error));
|
|
}
|
|
|
|
// =======================
|
|
// FIXED FIELDS (DB autosave)
|
|
// =======================
|
|
|
|
function escapeHtml(str) {
|
|
return String(str ?? '')
|
|
.replaceAll('&', '&')
|
|
.replaceAll('<', '<')
|
|
.replaceAll('>', '>')
|
|
.replaceAll('"', '"')
|
|
.replaceAll("'", ''');
|
|
}
|
|
|
|
function fixedStatus(msg, isError = false) {
|
|
const el = document.getElementById('fixedSaveStatus');
|
|
if (!el) return;
|
|
el.textContent = msg;
|
|
el.classList.toggle('text-danger', isError);
|
|
el.classList.toggle('text-muted', !isError);
|
|
setTimeout(() => {
|
|
el.textContent = '';
|
|
}, 2000);
|
|
}
|
|
|
|
function renderFixedRows(rows) {
|
|
const tbody = document.querySelector('#fixedFieldsTable tbody');
|
|
if (!tbody) return;
|
|
|
|
const keysWithDropdown = [
|
|
'ClienteResponsabile',
|
|
'ClienteFornitore',
|
|
'ClienteAnalisi',
|
|
'MoltiplicatorePrezzo',
|
|
'AnagraficaCertestObject',
|
|
'AnagraficaCertestService'
|
|
];
|
|
|
|
tbody.innerHTML = rows.map(r => {
|
|
const type = String(r.data_type || '').toUpperCase();
|
|
const dv = r.default_value ?? '';
|
|
const isDate = type === 'DATE';
|
|
const isDropdown = keysWithDropdown.includes(String(r.fixed_field_key || ''));
|
|
|
|
return `
|
|
<tr>
|
|
<td class="text-center">
|
|
<input type="checkbox"
|
|
class="fixed-visible-checkbox"
|
|
data-fixed-id="${r.id}"
|
|
${parseInt(r.is_visible_import) === 1 ? 'checked' : ''}>
|
|
</td>
|
|
|
|
<td class="text-center">
|
|
<input type="checkbox"
|
|
class="fixed-required-checkbox"
|
|
data-fixed-id="${r.id}"
|
|
${parseInt(r.is_required) === 1 ? 'checked' : ''}>
|
|
</td>
|
|
|
|
<td><strong>${escapeHtml(r.fixed_field_key)}</strong></td>
|
|
|
|
|
|
<td>${escapeHtml(r.data_type || '')}</td>
|
|
|
|
<td>
|
|
${
|
|
isDate
|
|
? `<input type="date"
|
|
class="form-control fixed-default-input"
|
|
data-fixed-id="${r.id}"
|
|
value="${escapeHtml(dv)}">`
|
|
: isDropdown
|
|
? `<select class="form-select fixed-default-select"
|
|
data-fixed-id="${r.id}"
|
|
data-fixed-key="${escapeHtml(r.fixed_field_key)}"
|
|
data-current-value="${escapeHtml(dv)}">
|
|
<option value="">Loading...</option>
|
|
</select>`
|
|
: `<input type="text"
|
|
class="form-control fixed-default-input"
|
|
data-fixed-id="${r.id}"
|
|
value="${escapeHtml(dv)}"
|
|
placeholder="Default value">`
|
|
}
|
|
</td>
|
|
</tr>
|
|
`;
|
|
}).join('');
|
|
}
|
|
|
|
|
|
|
|
// Button: Add Fixed Fields
|
|
const btnAddFixedFields = document.getElementById('btnAddFixedFields');
|
|
if (btnAddFixedFields) {
|
|
btnAddFixedFields.addEventListener('click', async () => {
|
|
btnAddFixedFields.disabled = true;
|
|
btnAddFixedFields.textContent = 'Creating...';
|
|
|
|
try {
|
|
const res = await fetch('create_fixed_fields.php', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
template_id: templateId
|
|
})
|
|
});
|
|
const data = await res.json();
|
|
|
|
if (!data.success) throw new Error(data.message || 'Create failed');
|
|
|
|
document.getElementById('fixedFieldsSection').style.display = 'block';
|
|
btnAddFixedFields.style.display = 'none';
|
|
|
|
if (Array.isArray(data.rows)) {
|
|
renderFixedRows(data.rows);
|
|
await fillFixedDropdowns();
|
|
initSelect2ForFixedDropdowns();
|
|
|
|
|
|
}
|
|
|
|
fixedStatus('✅ Fixed fields created');
|
|
} catch (e) {
|
|
console.error(e);
|
|
fixedStatus('❌ ' + e.message, true);
|
|
btnAddFixedFields.disabled = false;
|
|
btnAddFixedFields.textContent = 'Add Fixed Fields';
|
|
}
|
|
});
|
|
}
|
|
|
|
// Autosave: Visible on import
|
|
document.addEventListener('change', (e) => {
|
|
if (!e.target.classList.contains('fixed-visible-checkbox')) return;
|
|
|
|
const id = e.target.dataset.fixedId;
|
|
const value = e.target.checked ? 1 : 0;
|
|
|
|
fetch('update_fixed_field.php', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
id,
|
|
field: 'is_visible_import',
|
|
value
|
|
})
|
|
})
|
|
.then(r => r.json())
|
|
.then(d => {
|
|
if (!d.success) throw new Error(d.message || 'Update failed');
|
|
fixedStatus('✅ Saved');
|
|
})
|
|
.catch(err => {
|
|
console.error(err);
|
|
e.target.checked = !e.target.checked;
|
|
fixedStatus('❌ Save error', true);
|
|
});
|
|
});
|
|
|
|
document.addEventListener('change', (e) => {
|
|
if (!e.target.classList.contains('fixed-required-checkbox')) return;
|
|
|
|
const id = e.target.dataset.fixedId;
|
|
const value = e.target.checked ? 1 : 0;
|
|
|
|
fetch('update_fixed_field.php', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
id,
|
|
field: 'is_required',
|
|
value
|
|
})
|
|
})
|
|
.then(r => r.json())
|
|
.then(d => {
|
|
if (!d.success) throw new Error(d.message || 'Update failed');
|
|
fixedStatus('✅ Saved');
|
|
})
|
|
.catch(err => {
|
|
console.error(err);
|
|
e.target.checked = !e.target.checked;
|
|
fixedStatus('❌ Save error', true);
|
|
});
|
|
});
|
|
|
|
|
|
// Autosave: manual_default (debounce)
|
|
let fixedTimer = null;
|
|
document.addEventListener('input', (e) => {
|
|
if (!e.target.classList.contains('fixed-default-input')) return;
|
|
|
|
clearTimeout(fixedTimer);
|
|
fixedTimer = setTimeout(() => {
|
|
const id = e.target.dataset.fixedId;
|
|
const value = e.target.value;
|
|
|
|
fetch('update_fixed_field.php', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
id,
|
|
field: 'default_value',
|
|
value
|
|
})
|
|
|
|
})
|
|
.then(r => r.json())
|
|
.then(d => {
|
|
if (!d.success) throw new Error(d.message || 'Update failed');
|
|
fixedStatus('✅ Saved');
|
|
})
|
|
.catch(err => {
|
|
console.error(err);
|
|
fixedStatus('❌ Save error', true);
|
|
});
|
|
}, 350);
|
|
});
|
|
|
|
|
|
if (availableXlsColumns.length) updateXlsDropdowns();
|
|
|
|
fillFixedDropdowns().then(() => {
|
|
initSelect2ForFixedDropdowns();
|
|
});
|
|
|
|
});
|
|
|
|
async function fillFixedDropdowns() {
|
|
const selects = document.querySelectorAll('.fixed-default-select');
|
|
console.log('[fillFixedDropdowns] found selects:', selects.length);
|
|
|
|
if (!selects.length) return;
|
|
|
|
const clientId = <?php echo (int)($template['idclient'] ?? 0); ?>;
|
|
|
|
const requestCache = {};
|
|
|
|
async function fetchJson(url) {
|
|
if (requestCache[url]) {
|
|
return requestCache[url];
|
|
}
|
|
|
|
requestCache[url] = (async () => {
|
|
const r = await fetch(url);
|
|
const text = await r.text();
|
|
if (!r.ok) throw new Error(`HTTP ${r.status} on ${url}: ${text.slice(0, 200)}`);
|
|
try {
|
|
return JSON.parse(text);
|
|
} catch (e) {
|
|
throw new Error(`Invalid JSON from ${url}: ${text.slice(0, 200)}`);
|
|
}
|
|
})();
|
|
|
|
return requestCache[url];
|
|
}
|
|
|
|
function normalizeList(j, key) {
|
|
// Accetta vari formati: {value:[]}, {Responsabili:[]}, {results:[]}, array diretto
|
|
if (Array.isArray(j)) return j;
|
|
if (j && Array.isArray(j.value)) return j.value;
|
|
if (j && Array.isArray(j.Responsabili)) return j.Responsabili;
|
|
if (j && Array.isArray(j.results)) return j.results;
|
|
|
|
console.warn('[fillFixedDropdowns] unknown JSON shape for', key, j);
|
|
return [];
|
|
}
|
|
|
|
function formatClientLabel(x) {
|
|
const nome = x.Nominativo ?? x.Nome ?? x.name ?? 'Nome non disponibile';
|
|
const id = x.IdCliente ?? x.Id ?? x.id ?? '';
|
|
const codiceCliente = (x.CodiceCliente ?? x.codiceCliente ?? '').toString().trim();
|
|
const suffix = (codiceCliente.split('_')[1] || '').trim();
|
|
const shortCode = suffix || (codiceCliente ? codiceCliente.charAt(0) : '--');
|
|
|
|
return `${nome.trim()} - ${shortCode} (ID: ${id})`;
|
|
}
|
|
|
|
async function loadData(key) {
|
|
if (key === 'MoltiplicatorePrezzo') {
|
|
const j = await fetchJson('get_moltiplicatoreprezzo.php');
|
|
const list = normalizeList(j, key);
|
|
return list.map(x => ({
|
|
id: x.IdMoltiplicatorePrezzo,
|
|
label: `${x.Descrizione} (x${x.Fattore})`
|
|
}));
|
|
}
|
|
|
|
if (key === 'AnagraficaCertestObject') {
|
|
const j = await fetchJson('get_anagrafica_certest_object.php');
|
|
const list = normalizeList(j, key);
|
|
return list.map(x => ({
|
|
id: x.IdAnagrafica,
|
|
label: `${x.Codice} - ${x.NomeAnagrafica}`
|
|
}));
|
|
}
|
|
|
|
if (key === 'AnagraficaCertestService') {
|
|
const j = await fetchJson('get_anagrafica_certest_service.php');
|
|
const list = normalizeList(j, key);
|
|
return list.map(x => ({
|
|
id: x.IdAnagrafica,
|
|
label: `${x.Codice} - ${x.NomeAnagrafica}`
|
|
}));
|
|
}
|
|
|
|
if (key === 'ClienteResponsabile') {
|
|
if (!clientId) return [];
|
|
const j = await fetchJson('get_cliente_responsabile.php?id_cliente=' + clientId);
|
|
const list = normalizeList(j, key);
|
|
|
|
// supporto due possibili campi:
|
|
// - {IdClienteResponsabile, Nominativo}
|
|
// - {Id, Nome} o simili
|
|
return list.map(x => ({
|
|
id: x.IdClienteResponsabile ?? x.Id ?? x.id,
|
|
label: x.Nominativo ?? x.Nome ?? x.name ?? ''
|
|
})).filter(x => x.id != null && x.label);
|
|
}
|
|
|
|
if (key === 'ClienteFornitore' || key === 'ClienteAnalisi') {
|
|
const j = await fetchJson('get_clienti.php');
|
|
const list = normalizeList(j, key);
|
|
|
|
return list.map(x => ({
|
|
id: x.IdCliente ?? x.Id ?? x.id,
|
|
label: formatClientLabel(x)
|
|
})).filter(x => x.id != null && x.label);
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
for (const select of selects) {
|
|
const key = select.dataset.fixedKey;
|
|
const current = select.dataset.currentValue;
|
|
|
|
// IMPORTANT: togli "Loading..." subito (così se crasha non resta bloccato)
|
|
select.innerHTML = '<option value="">Loading...</option>';
|
|
|
|
try {
|
|
console.log('[fillFixedDropdowns] loading', key);
|
|
const items = await loadData(key);
|
|
|
|
if (!items.length) {
|
|
select.innerHTML = '<option value="">No data</option>';
|
|
continue;
|
|
}
|
|
|
|
select.innerHTML = '<option value=""></option>';
|
|
|
|
for (const it of items) {
|
|
const opt = document.createElement('option');
|
|
opt.value = it.id;
|
|
opt.textContent = it.label;
|
|
select.appendChild(opt);
|
|
}
|
|
|
|
if (current) select.value = current;
|
|
|
|
} catch (err) {
|
|
console.error('[fillFixedDropdowns] error for', key, err);
|
|
select.innerHTML = '<option value="">Error loading data</option>';
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
|
|
</body>
|
|
|
|
</html>
|