Files
trf_certest/public/userarea/resolve_bindings.php
T
2026-09-23 15:50:03 +03:00

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 &rarr; 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 &rarr; LIMS</h6>
<small><?= htmlspecialchars($templateName) ?> &middot; 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">&middot; <?= $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">&rsaquo;</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 &middot; <?= 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">&rsaquo;</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>