418 lines
20 KiB
PHP
418 lines
20 KiB
PHP
<?php
|
|
|
|
include('include/headscript.php');
|
|
require_once __DIR__ . '/class/binding-functions.php';
|
|
|
|
$pending = $_SESSION['pending_bindings'] ?? null;
|
|
|
|
if (isset($_GET['done'])) {
|
|
$tid = $pending['template_id'] ?? ($_SESSION['template_id'] ?? null);
|
|
$ref = $pending['importref'] ?? '';
|
|
unset($_SESSION['pending_bindings']);
|
|
|
|
header($tid
|
|
? "Location: imported.php?id=" . urlencode($tid) . "&importref=" . urlencode($ref)
|
|
: "Location: xlstemplates_grid.php");
|
|
exit;
|
|
}
|
|
|
|
if (empty($pending) || empty($pending['items'])) {
|
|
$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 = array_values($pending['items'] ?? []);
|
|
$autoItems = array_values($pending['auto'] ?? []);
|
|
$savedItems = array_values($pending['saved'] ?? []);
|
|
|
|
$db = DBHandlerSelect::getInstance();
|
|
$pdo = $db->getConnection();
|
|
|
|
$stmt = $pdo->prepare("SELECT name FROM excel_templates WHERE id = ?");
|
|
$stmt->execute([$templateId]);
|
|
$templateName = $stmt->fetchColumn() ?: ('Template ' . $templateId);
|
|
|
|
foreach ($items as $i => $item) {
|
|
$items[$i]['suggestion'] = ($item['kind'] ?? 'custom') === 'fixed'
|
|
? binding_suggest_fixed(
|
|
$pdo,
|
|
(string) ($item['fixed_field_key'] ?? ''),
|
|
(string) $item['source_value'],
|
|
(int) $item['mapping_id']
|
|
)
|
|
: binding_suggest(
|
|
$pdo,
|
|
(int) $item['field_id'],
|
|
(string) $item['source_value'],
|
|
(int) $item['mapping_id']
|
|
);
|
|
}
|
|
|
|
$resolvedItems = [];
|
|
foreach ($autoItems as $a) {
|
|
$a['badge'] = 'automatico';
|
|
$a['badge_class'] = 'bg-success';
|
|
$resolvedItems[] = $a;
|
|
}
|
|
foreach ($savedItems as $s) {
|
|
$s['badge'] = 'binding salvato';
|
|
$s['badge_class'] = 'bg-secondary';
|
|
$resolvedItems[] = $s;
|
|
}
|
|
|
|
$groups = [];
|
|
$groupOrder = [];
|
|
$pushToGroup = function (array $row, string $type, $idx = null) use (&$groups, &$groupOrder) {
|
|
$kind = $row['kind'] ?? 'custom';
|
|
$gkey = $kind . ':' . (int) ($row['mapping_id'] ?? 0);
|
|
if (!isset($groups[$gkey])) {
|
|
$groups[$gkey] = [
|
|
'label' => $row['field_label'] ?? ('Mapping ' . $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');
|
|
}
|
|
|
|
$pendingRecords = 0;
|
|
foreach ($items as $item) {
|
|
$pendingRecords += count($item['datadb_ids'] ?? []);
|
|
}
|
|
?>
|
|
<!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>
|
|
.source-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 valori → 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 valori importati → 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">
|
|
<div class="alert alert-info">
|
|
Alcuni valori importati dal file non corrispondono a nessun valore delle liste LIMS.
|
|
Finche' non vengono collegati, questi campi non possono essere esportati.
|
|
Scegli il valore LIMS corretto per ciascuno: il binding viene salvato e
|
|
riutilizzato nelle importazioni successive.
|
|
<div class="mt-1">
|
|
<strong><?= count($items) ?></strong> valore/i da risolvere su
|
|
<strong><?= $pendingRecords ?></strong> record importati.
|
|
</div>
|
|
</div>
|
|
<?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 importato</th>
|
|
<th style="width:90px;">Record</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="5">
|
|
<strong><?= htmlspecialchars($g['label']) ?></strong>
|
|
<?php if (($g['kind'] ?? 'custom') === 'fixed'): ?><span class="badge bg-light text-dark border">campo fisso</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'];
|
|
$suggestion = $item['suggestion'] ?? null; ?>
|
|
<tr class="binding-row"
|
|
data-index="<?= $idx ?>"
|
|
data-kind="<?= htmlspecialchars($item['kind'] ?? 'custom', ENT_QUOTES) ?>"
|
|
data-fixed-key="<?= htmlspecialchars($item['fixed_field_key'] ?? '', ENT_QUOTES) ?>"
|
|
data-mapping-id="<?= (int) $item['mapping_id'] ?>"
|
|
data-field-id="<?= (int) $item['field_id'] ?>"
|
|
data-source-value="<?= htmlspecialchars($item['source_value'], ENT_QUOTES) ?>"
|
|
data-datadb-ids="<?= htmlspecialchars(json_encode($item['datadb_ids']), ENT_QUOTES) ?>">
|
|
<td class="text-muted ps-4">›</td>
|
|
<td class="source-value"><?= htmlspecialchars($item['source_value']) ?></td>
|
|
<td><?= count($item['datadb_ids'] ?? []) ?></td>
|
|
<td>
|
|
<select class="form-select binding-select">
|
|
<?php if ($suggestion): ?>
|
|
<option value="<?= (int) $suggestion['lims_value_id'] ?>" selected><?= htmlspecialchars($suggestion['lims_value']) ?></option>
|
|
<?php else: ?>
|
|
<option value="">Seleziona valore LIMS...</option>
|
|
<?php endif; ?>
|
|
</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">
|
|
<?php if ($suggestion): ?>
|
|
<span class="badge bg-warning text-dark" title="Stesso valore gia' collegato in un altro template">
|
|
suggerito · <?= htmlspecialchars($suggestion['template_name'] ?: ('template ' . $suggestion['template_id'])) ?>
|
|
</span>
|
|
<?php else: ?>
|
|
<span class="text-muted">In attesa</span>
|
|
<?php endif; ?>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
|
|
<?php foreach ($g['resolved'] as $r): $res = $r['data']; ?>
|
|
<tr class="binding-row"
|
|
data-kind="<?= htmlspecialchars($res['kind'] ?? 'custom', ENT_QUOTES) ?>"
|
|
data-fixed-key="<?= htmlspecialchars($res['fixed_field_key'] ?? '', ENT_QUOTES) ?>"
|
|
data-mapping-id="<?= (int) $res['mapping_id'] ?>"
|
|
data-field-id="<?= (int) $res['field_id'] ?>"
|
|
data-source-value="<?= htmlspecialchars($res['source_value'], ENT_QUOTES) ?>"
|
|
data-datadb-ids="<?= htmlspecialchars(json_encode($res['datadb_ids']), ENT_QUOTES) ?>">
|
|
<td class="text-muted ps-4">›</td>
|
|
<td class="source-value"><?= htmlspecialchars($res['source_value']) ?></td>
|
|
<td><?= count($res['datadb_ids'] ?? []) ?></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="resolve_bindings.php?done=1"
|
|
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 = 'resolve_bindings.php?done=1';
|
|
|
|
const $form = $('#bindingForm');
|
|
const $btn = $('#confirmBindingsBtn');
|
|
const $error = $('#bindingError');
|
|
|
|
$('.binding-select').each(function() {
|
|
const $row = $(this).closest('.binding-row');
|
|
const isFixed = $row.data('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
|
|
});
|
|
});
|
|
|
|
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();
|
|
|
|
$('.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.html(skipped ? 'Nessuna corrispondenza' : '<span class="text-muted">In attesa</span>');
|
|
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 datadbIds = JSON.stringify($row.data('datadb-ids') || []);
|
|
const sourceValue = String($row.data('source-value'));
|
|
const mappingId = $row.data('mapping-id');
|
|
|
|
if ($row.hasClass('is-skipped')) {
|
|
return $.post('skip_binding.php', {
|
|
kind: $row.data('kind') || 'custom',
|
|
mapping_id: mappingId,
|
|
template_id: TEMPLATE_ID,
|
|
source_value: sourceValue,
|
|
datadb_ids: datadbIds
|
|
}).then(resp => {
|
|
if (resp && resp.success) {
|
|
$status.html('<span class="text-success">Azzerato</span>');
|
|
return true;
|
|
}
|
|
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', {
|
|
kind: $row.data('kind') || 'custom',
|
|
mapping_id: mappingId,
|
|
field_id: $row.data('field-id'),
|
|
template_id: TEMPLATE_ID,
|
|
source_value: sourceValue,
|
|
lims_value_id: $select.val(),
|
|
lims_value: selectedData.text || '',
|
|
datadb_ids: datadbIds
|
|
}).then(resp => {
|
|
if (resp && resp.success) {
|
|
$status.html('<span class="text-success">Salvato</span>');
|
|
return true;
|
|
}
|
|
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>
|