386 lines
19 KiB
PHP
386 lines
19 KiB
PHP
<?php
|
|
include('include/headscript.php');
|
|
|
|
// Binding preparati da import_insert.php (da risolvere + collegati in automatico).
|
|
$pending = $_SESSION['pending_bindings'] ?? null;
|
|
|
|
if (empty($pending) || (empty($pending['items']) && empty($pending['auto']) && empty($pending['saved']))) {
|
|
// Niente da mostrare: vado alla griglia.
|
|
$tid = $pending['template_id'] ?? ($_SESSION['template_id'] ?? null);
|
|
$ref = $pending['importref'] ?? '';
|
|
unset($_SESSION['pending_bindings']);
|
|
|
|
if ($tid) {
|
|
header("Location: imported.php?id=" . urlencode($tid) . "&importref=" . urlencode($ref));
|
|
} else {
|
|
header("Location: xlstemplates_grid.php");
|
|
}
|
|
exit;
|
|
}
|
|
|
|
$templateId = (int) $pending['template_id'];
|
|
$importRef = (string) $pending['importref'];
|
|
$items = $pending['items'] ?? [];
|
|
$autoItems = $pending['auto'] ?? [];
|
|
$savedItems = $pending['saved'] ?? [];
|
|
|
|
// Righe gia' risolte (modificabili): auto-collegate + binding gia' salvati.
|
|
$resolvedItems = [];
|
|
foreach ($autoItems as $a) {
|
|
$a['badge'] = 'auto';
|
|
$a['badge_class'] = 'bg-success';
|
|
$resolvedItems[] = $a;
|
|
}
|
|
foreach ($savedItems as $s) {
|
|
$s['badge'] = 'salvato';
|
|
$s['badge_class'] = 'bg-secondary';
|
|
$resolvedItems[] = $s;
|
|
}
|
|
|
|
// Raggruppa le righe per campo (custom: mapping_id, fixed: fixed_field_key),
|
|
// mantenendo l'ordine di prima apparizione. Ogni gruppo elenca prima i pending.
|
|
$groups = [];
|
|
$groupOrder = [];
|
|
$pushToGroup = function (array $row, string $type, $idx = null) use (&$groups, &$groupOrder) {
|
|
$kind = $row['kind'] ?? 'custom';
|
|
$gkey = $kind === 'fixed'
|
|
? 'fx:' . ($row['fixed_field_key'] ?? '')
|
|
: 'cf:' . (int) ($row['mapping_id'] ?? 0);
|
|
if (!isset($groups[$gkey])) {
|
|
$groups[$gkey] = [
|
|
'label' => $row['field_label'] ?? $gkey,
|
|
'kind' => $kind,
|
|
'pending' => [],
|
|
'resolved' => [],
|
|
];
|
|
$groupOrder[] = $gkey;
|
|
}
|
|
if ($type === 'pending') {
|
|
$groups[$gkey]['pending'][] = ['idx' => $idx, 'data' => $row];
|
|
} else {
|
|
$groups[$gkey]['resolved'][] = ['data' => $row];
|
|
}
|
|
};
|
|
foreach ($items as $idx => $item) {
|
|
$pushToGroup($item, 'pending', $idx);
|
|
}
|
|
foreach ($resolvedItems as $res) {
|
|
$pushToGroup($res, 'resolved');
|
|
}
|
|
|
|
$db = DBHandlerSelect::getInstance();
|
|
$pdo = $db->getConnection();
|
|
$stmt = $pdo->prepare("SELECT name FROM excel_templates WHERE id = ?");
|
|
$stmt->execute([$templateId]);
|
|
$templateName = $stmt->fetchColumn() ?: ('Template ' . $templateId);
|
|
?>
|
|
<!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'); ?>
|
|
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet">
|
|
<style>
|
|
.json-value {
|
|
font-family: Consolas, Monaco, monospace;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.binding-status {
|
|
font-size: 12px;
|
|
}
|
|
|
|
td .select2-container {
|
|
min-width: 240px;
|
|
}
|
|
|
|
/* Allinea l'altezza di select2 a form-select/btn di Bootstrap */
|
|
.select2-container--default .select2-selection--single {
|
|
height: 38px;
|
|
border-color: #ced4da;
|
|
}
|
|
|
|
.select2-container--default .select2-selection--single .select2-selection__rendered {
|
|
line-height: 36px;
|
|
}
|
|
|
|
.select2-container--default .select2-selection--single .select2-selection__arrow {
|
|
height: 36px;
|
|
}
|
|
</style>
|
|
<title>Binding JSON → LIMS - <?= htmlspecialchars($titlewebsite ?? '', ENT_QUOTES, 'UTF-8'); ?></title>
|
|
</head>
|
|
|
|
<body>
|
|
<div class="wrapper">
|
|
<?php include('include/navbar.php'); ?>
|
|
<?php include('include/topbar.php'); ?>
|
|
<div class="page-wrapper">
|
|
<div class="page-content">
|
|
|
|
<div class="card radius-10">
|
|
<div class="card-header">
|
|
<div class="d-flex align-items-center justify-content-between flex-wrap gap-2">
|
|
<div>
|
|
<h6 class="mb-0">Binding JSON → LIMS</h6>
|
|
<small><?= htmlspecialchars($templateName) ?> · Template ID: <?= $templateId ?></small>
|
|
</div>
|
|
<a href="bindings_manage.php?template_id=<?= $templateId ?>" class="btn btn-sm btn-outline-secondary">
|
|
<i class="fas fa-cog"></i> Gestione binding
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card-body">
|
|
<?php if (!empty($items)): ?>
|
|
<div class="alert alert-info">
|
|
Alcuni valori importati dal JSON non hanno ancora una corrispondenza con i valori del LIMS.
|
|
Seleziona il valore LIMS corretto per ciascuno e conferma. I binding verranno salvati e
|
|
riutilizzati nelle importazioni successive.
|
|
</div>
|
|
<?php endif; ?>
|
|
<?php if (!empty($autoItems)): ?>
|
|
<div class="alert alert-success">
|
|
<?= count($autoItems) ?> valore/i collegato/i automaticamente (corrispondenza esatta con il LIMS).
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div id="bindingError" class="alert alert-danger" style="display:none;"></div>
|
|
|
|
<form id="bindingForm">
|
|
<div class="table-responsive">
|
|
<table class="table table-bordered align-middle">
|
|
<thead>
|
|
<tr>
|
|
<th style="width:32px;"></th>
|
|
<th>Valore JSON</th>
|
|
<th>Valore LIMS</th>
|
|
<th style="width:210px;">Azioni</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($groupOrder as $gkey): $g = $groups[$gkey];
|
|
$pendCount = count($g['pending']);
|
|
$totalCount = $pendCount + count($g['resolved']); ?>
|
|
<tr class="binding-group-header table-light">
|
|
<td colspan="4">
|
|
<strong><?= htmlspecialchars($g['label']) ?></strong>
|
|
<?php if ($g['kind'] === 'fixed'): ?><span class="badge bg-light text-dark border">fixed</span><?php endif; ?>
|
|
<span class="text-muted">· <?= $totalCount ?> valore/i<?php if ($pendCount): ?>, <?= $pendCount ?> da risolvere<?php endif; ?></span>
|
|
</td>
|
|
</tr>
|
|
|
|
<?php foreach ($g['pending'] as $p): $idx = $p['idx']; $item = $p['data']; $kind = $item['kind'] ?? 'custom'; ?>
|
|
<tr class="binding-row"
|
|
data-index="<?= $idx ?>"
|
|
data-kind="<?= $kind ?>"
|
|
<?php if ($kind === 'fixed'): ?>
|
|
data-fixed-key="<?= htmlspecialchars($item['fixed_field_key'], ENT_QUOTES) ?>"
|
|
<?php else: ?>
|
|
data-mapping-id="<?= (int) ($item['mapping_id'] ?? 0) ?>"
|
|
data-field-id="<?= (int) ($item['field_id'] ?? 0) ?>"
|
|
<?php endif; ?>
|
|
data-json-value="<?= htmlspecialchars($item['json_value'], ENT_QUOTES) ?>"
|
|
data-datadb-ids="<?= htmlspecialchars(json_encode($item['datadb_ids']), ENT_QUOTES) ?>">
|
|
<td class="text-muted ps-4">›</td>
|
|
<td class="json-value"><?= htmlspecialchars($item['json_value']) ?></td>
|
|
<td>
|
|
<select class="form-select binding-select">
|
|
<option value="">Seleziona valore LIMS...</option>
|
|
</select>
|
|
</td>
|
|
<td>
|
|
<button type="button" class="btn btn-sm btn-outline-secondary skip-binding-btn">Nessuna corrispondenza</button>
|
|
<div class="binding-status text-muted mt-1">In attesa</div>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
|
|
<?php foreach ($g['resolved'] as $r): $res = $r['data']; $kind = $res['kind'] ?? 'custom'; ?>
|
|
<tr class="binding-row"
|
|
data-kind="<?= $kind ?>"
|
|
<?php if ($kind === 'fixed'): ?>
|
|
data-fixed-key="<?= htmlspecialchars($res['fixed_field_key'], ENT_QUOTES) ?>"
|
|
<?php else: ?>
|
|
data-mapping-id="<?= (int) ($res['mapping_id'] ?? 0) ?>"
|
|
data-field-id="<?= (int) ($res['field_id'] ?? 0) ?>"
|
|
<?php endif; ?>
|
|
data-json-value="<?= htmlspecialchars($res['json_value'], ENT_QUOTES) ?>"
|
|
data-datadb-ids="<?= htmlspecialchars(json_encode($res['datadb_ids']), ENT_QUOTES) ?>">
|
|
<td class="text-muted ps-4">›</td>
|
|
<td class="json-value"><?= htmlspecialchars($res['json_value']) ?></td>
|
|
<td>
|
|
<select class="form-select binding-select">
|
|
<option value="<?= (int) $res['lims_value_id'] ?>" selected><?= htmlspecialchars($res['lims_value']) ?></option>
|
|
</select>
|
|
</td>
|
|
<td>
|
|
<button type="button" class="btn btn-sm btn-outline-secondary skip-binding-btn">Nessuna corrispondenza</button>
|
|
<div class="binding-status mt-1"><span class="badge <?= $res['badge_class'] ?>"><?= $res['badge'] ?></span></div>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="d-flex justify-content-end gap-2 mt-3">
|
|
<a href="imported.php?id=<?= $templateId ?>&importref=<?= urlencode($importRef) ?>"
|
|
class="btn btn-outline-secondary">Salta per ora</a>
|
|
<button type="submit" class="btn btn-primary" id="confirmBindingsBtn" disabled>
|
|
Conferma e prosegui
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</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>
|
|
$(function() {
|
|
const TEMPLATE_ID = <?= $templateId ?>;
|
|
const IMPORT_REF = <?= json_encode($importRef) ?>;
|
|
const CONTINUE_URL = 'imported.php?id=' + TEMPLATE_ID + '&importref=' + encodeURIComponent(IMPORT_REF);
|
|
|
|
const $form = $('#bindingForm');
|
|
const $btn = $('#confirmBindingsBtn');
|
|
const $error = $('#bindingError');
|
|
|
|
// Dropdown valori LIMS per riga: sorgente custom vs fixed.
|
|
$('.binding-select').each(function() {
|
|
const $row = $(this).closest('.binding-row');
|
|
const kind = $row.data('kind');
|
|
const isFixed = kind === 'fixed';
|
|
|
|
$(this).select2({
|
|
placeholder: 'Seleziona valore LIMS...',
|
|
width: '100%',
|
|
ajax: {
|
|
url: isFixed ? 'search_fixed_field_values.php' : 'search_customfield_values.php',
|
|
dataType: 'json',
|
|
delay: 200,
|
|
data: params => isFixed ? {
|
|
field_key: $row.data('fixed-key'),
|
|
template_id: TEMPLATE_ID,
|
|
q: params.term || '',
|
|
limit: 50
|
|
} : {
|
|
field_id: $row.data('field-id'),
|
|
q: params.term || '',
|
|
limit: 50
|
|
},
|
|
processResults: data => ({
|
|
results: data.results || []
|
|
})
|
|
},
|
|
minimumInputLength: 0
|
|
});
|
|
});
|
|
|
|
// Una riga e' pronta se ha un valore scelto oppure e' marcata "nessuna corrispondenza".
|
|
function refreshButton() {
|
|
const allReady = $('.binding-row').toArray().every(row => {
|
|
const $row = $(row);
|
|
return $row.hasClass('is-skipped') || $row.find('.binding-select').val();
|
|
});
|
|
$btn.prop('disabled', !allReady);
|
|
}
|
|
|
|
$('.binding-select').on('change', refreshButton);
|
|
refreshButton();
|
|
|
|
// "Nessuna corrispondenza": azzera il valore importato, nessun binding salvato.
|
|
$('.skip-binding-btn').on('click', function() {
|
|
const $row = $(this).closest('.binding-row');
|
|
const $select = $row.find('.binding-select');
|
|
const $status = $row.find('.binding-status');
|
|
const skipped = $row.toggleClass('is-skipped').hasClass('is-skipped');
|
|
|
|
$(this).toggleClass('btn-outline-secondary', !skipped).toggleClass('btn-secondary', skipped);
|
|
$select.val(null).trigger('change').prop('disabled', skipped);
|
|
$status.text(skipped ? 'Nessuna corrispondenza' : 'In attesa');
|
|
refreshButton();
|
|
});
|
|
|
|
$form.on('submit', function(e) {
|
|
e.preventDefault();
|
|
$error.hide();
|
|
$btn.prop('disabled', true).text('Salvataggio...');
|
|
|
|
const tasks = $('.binding-row').toArray().map(row => {
|
|
const $row = $(row);
|
|
const $status = $row.find('.binding-status');
|
|
const kind = $row.data('kind');
|
|
const isFixed = kind === 'fixed';
|
|
const datadbIds = JSON.stringify($row.data('datadb-ids') || []);
|
|
const jsonValue = String($row.data('json-value'));
|
|
|
|
const targetFields = isFixed
|
|
? { kind: 'fixed', fixed_field_key: $row.data('fixed-key') }
|
|
: { kind: 'custom', mapping_id: $row.data('mapping-id'), field_id: $row.data('field-id') };
|
|
|
|
// Riga senza corrispondenza: azzera il valore, niente binding.
|
|
if ($row.hasClass('is-skipped')) {
|
|
return $.post('skip_binding.php', {
|
|
...targetFields,
|
|
template_id: TEMPLATE_ID,
|
|
json_value: jsonValue,
|
|
datadb_ids: datadbIds
|
|
}).then(resp => {
|
|
if (resp && resp.success) {
|
|
$status.text('Azzerato').removeClass('text-muted').addClass('text-success');
|
|
return true;
|
|
}
|
|
$status.text('Errore').addClass('text-danger');
|
|
throw new Error((resp && resp.error) || 'Errore azzeramento valore');
|
|
});
|
|
}
|
|
|
|
const $select = $row.find('.binding-select');
|
|
const selectedData = $select.select2('data')[0] || {};
|
|
|
|
return $.post('save_binding.php', {
|
|
...targetFields,
|
|
template_id: TEMPLATE_ID,
|
|
json_value: jsonValue,
|
|
lims_value_id: $select.val(),
|
|
lims_value: selectedData.text || '',
|
|
datadb_ids: datadbIds
|
|
}).then(resp => {
|
|
if (resp && resp.success) {
|
|
$status.text('Salvato').removeClass('text-muted').addClass('text-success');
|
|
return true;
|
|
}
|
|
$status.text('Errore').addClass('text-danger');
|
|
throw new Error((resp && resp.error) || 'Errore salvataggio binding');
|
|
});
|
|
});
|
|
|
|
Promise.all(tasks)
|
|
.then(() => {
|
|
window.location.href = CONTINUE_URL;
|
|
})
|
|
.catch(err => {
|
|
$error.text(err.message || 'Errore durante il salvataggio dei binding.').show();
|
|
$btn.prop('disabled', false).text('Conferma e prosegui');
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
</body>
|
|
|
|
</html>
|