tolims filter, pagination

This commit is contained in:
r.mubarakzyanov 2026-03-29 17:13:54 +03:00
parent 3e66d67dc5
commit c573a46318
7 changed files with 768 additions and 57 deletions

View File

@ -5,6 +5,7 @@ header('Content-Type: application/json');
try { try {
$input = json_decode(file_get_contents('php://input'), true); $input = json_decode(file_get_contents('php://input'), true);
$templateId = intval($input['template_id'] ?? 0); $templateId = intval($input['template_id'] ?? 0);
$importRefFromClient = trim($input['importreferencecode'] ?? '');
if (!$templateId) { if (!$templateId) {
throw new Exception('Template ID missing'); throw new Exception('Template ID missing');
@ -21,8 +22,8 @@ try {
$template = $stmt->fetch(PDO::FETCH_ASSOC); $template = $stmt->fetch(PDO::FETCH_ASSOC);
$idclient = $template['idclient'] ?? null; $idclient = $template['idclient'] ?? null;
// Generate import reference code // Use provided importreferencecode (from filtered page) or generate new
$importReferenceCode = date('YmdHis') . '-' . uniqid(); $importReferenceCode = $importRefFromClient !== '' ? $importRefFromClient : date('YmdHis') . '-' . uniqid();
// Insert empty record // Insert empty record
$stmt = $pdo->prepare(" $stmt = $pdo->prepare("

View File

@ -18,7 +18,7 @@
const data = window.gridData || []; const data = window.gridData || [];
const meta = window.gridMeta || {}; const meta = window.gridMeta || {};
const columns = meta.columns || []; const columns = meta.columns || [];
const totalRows = data.length; let totalRows = data.length;
// ── DOM refs ──────────────────────────────────────────────────────────── // ── DOM refs ────────────────────────────────────────────────────────────
let rowContainer = null; let rowContainer = null;
@ -463,6 +463,10 @@
function renderVisibleRows() { function renderVisibleRows() {
if (!rowContainer) return; if (!rowContainer) return;
// Refresh totalRows in case data was modified externally
totalRows = data.length;
if (revealedCount < totalRows) revealedCount = totalRows;
// Destroy Select2 on existing rows // Destroy Select2 on existing rows
$(rowContainer).find('.select2-hidden-accessible').select2('destroy'); $(rowContainer).find('.select2-hidden-accessible').select2('destroy');

View File

@ -147,28 +147,6 @@ foreach ($selected_rows as $rowIndex) {
$_SESSION['inserted_ids'] = $insertedIds; $_SESSION['inserted_ids'] = $insertedIds;
$params = [ header("Location: imported.php?id=" . urlencode($template_id) . "&importref=" . urlencode($importReferenceCode));
'template_id' => $template_id,
'filename' => $newFilename,
];
?>
<form id="redirectForm" action="import_edit2.php" method="post">
<input type="hidden" name="template_id" value="<?= htmlspecialchars($template_id) ?>">
<input type="hidden" name="filename" value="<?= htmlspecialchars($newFilename) ?>">
<?php foreach ($selected_rows as $row): ?>
<input type="hidden" name="selected_rows[]" value="<?= htmlspecialchars($row) ?>">
<?php endforeach; ?>
<?php foreach ($insertedIds as $id): ?>
<input type="hidden" name="inserted_ids[]" value="<?= htmlspecialchars($id) ?>">
<?php endforeach; ?>
<input type="hidden" name="columns" value='<?= json_encode($columns) ?>'>
<input type="hidden" name="rows" value='<?= json_encode($rows) ?>'>
<input type="hidden" name="excelrows" value='<?= json_encode($excelrows) ?>'>
</form>
<script>
document.getElementById('redirectForm').submit();
</script>
<?php
exit; exit;
?> ?>

View File

@ -13,6 +13,12 @@ if (!$template_id) {
$user_id = $iduserlogin ?? 1; $user_id = $iduserlogin ?? 1;
$show_all_users = isset($_GET['all_users']) && $_GET['all_users'] == '1'; $show_all_users = isset($_GET['all_users']) && $_GET['all_users'] == '1';
$importref = trim($_GET['importref'] ?? '');
$usePagination = ($importref === '');
$allowedLimits = [20, 40, 60, 100];
$rawLimit = (int)($_GET['limit'] ?? 20);
$perPage = in_array($rawLimit, $allowedLimits) ? $rawLimit : 20;
$page = max(1, (int)($_GET['page'] ?? 1));
$db = DBHandlerSelect::getInstance(); $db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection(); $pdo = $db->getConnection();
@ -70,16 +76,29 @@ $default_idclient = $template['idclient'] ?? null;
// Fetch records with status='i' for this template // Fetch records with status='i' for this template
$userFilter = $show_all_users ? '' : 'AND d.user_id = ?'; $userFilter = $show_all_users ? '' : 'AND d.user_id = ?';
$importrefFilter = $importref !== '' ? 'AND d.importreferencecode = ?' : '';
$baseWhere = "WHERE d.templateid = ? AND d.status = 'i' {$userFilter} {$importrefFilter}";
$baseParams = [$template_id];
if (!$show_all_users) $baseParams[] = $user_id;
if ($importref !== '') $baseParams[] = $importref;
// Count total records
$countStmt = $pdo->prepare("SELECT COUNT(*) FROM datadb d {$baseWhere}");
$countStmt->execute($baseParams);
$totalRows = (int)$countStmt->fetchColumn();
$totalPages = $usePagination ? max(1, (int)ceil($totalRows / $perPage)) : 1;
if ($page > $totalPages) $page = $totalPages;
$limitClause = $usePagination ? 'LIMIT ' . $perPage . ' OFFSET ' . (($page - 1) * $perPage) : '';
$stmt = $pdo->prepare(" $stmt = $pdo->prepare("
SELECT d.*, CONCAT(u.first_name, ' ', u.last_name) AS user_name SELECT d.*, CONCAT(u.first_name, ' ', u.last_name) AS user_name
FROM datadb d FROM datadb d
LEFT JOIN auth_users u ON d.user_id = u.id LEFT JOIN auth_users u ON d.user_id = u.id
WHERE d.templateid = ? AND d.status = 'i' {$userFilter} {$baseWhere}
ORDER BY d.iddatadb DESC ORDER BY d.iddatadb DESC
{$limitClause}
"); ");
$params = [$template_id]; $stmt->execute($baseParams);
if (!$show_all_users) $params[] = $user_id;
$stmt->execute($params);
$importedData = $stmt->fetchAll(PDO::FETCH_ASSOC); $importedData = $stmt->fetchAll(PDO::FETCH_ASSOC);
$insertedIds = array_column($importedData, 'iddatadb'); $insertedIds = array_column($importedData, 'iddatadb');
@ -872,6 +891,14 @@ window.gridMeta = <?= json_encode($gridMeta, JSON_UNESCAPED_UNICODE | JSON_UNESC
transition: background-color 0.3s ease; transition: background-color 0.3s ease;
} }
@keyframes new-row-pulse {
0%, 100% { background-color: transparent; }
50% { background-color: #cce5ff; }
}
.row-just-created {
animation: new-row-pulse 1s ease-in-out 3;
}
.actions-dropdown .dropdown-toggle { .actions-dropdown .dropdown-toggle {
background-color: #6c757d; background-color: #6c757d;
color: white; color: white;
@ -990,6 +1017,87 @@ window.gridMeta = <?= json_encode($gridMeta, JSON_UNESCAPED_UNICODE | JSON_UNESC
border-color: #dc3545 !important; border-color: #dc3545 !important;
} }
/* ── Pagination bar ── */
.pager-bar {
display: flex;
align-items: center;
justify-content: space-between;
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 6px;
padding: 6px 14px;
font-size: 13px;
color: #495057;
}
.pager-rows-per-page {
display: flex;
align-items: center;
gap: 8px;
}
.pager-label {
font-weight: 500;
white-space: nowrap;
}
.pager-limit-group {
display: inline-flex;
border: 1px solid #ced4da;
border-radius: 4px;
overflow: hidden;
}
.pager-limit-btn {
padding: 3px 10px;
text-decoration: none;
color: #495057;
border-right: 1px solid #ced4da;
transition: background .15s, color .15s;
font-weight: 500;
}
.pager-limit-btn:last-child { border-right: none; }
.pager-limit-btn:hover { background: #e9ecef; text-decoration: none; color: #212529; }
.pager-limit-btn.active {
background: #0d6efd;
color: #fff;
}
.pager-limit-btn.active:hover { background: #0b5ed7; color: #fff; }
.pager-nav {
display: flex;
align-items: center;
gap: 4px;
}
.pager-info {
margin-right: 8px;
font-weight: 500;
white-space: nowrap;
}
.pager-btn, .pager-num {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 28px;
height: 28px;
padding: 0 6px;
border: 1px solid #ced4da;
border-radius: 4px;
text-decoration: none;
color: #495057;
font-weight: 500;
transition: background .15s, color .15s, border-color .15s;
}
.pager-btn:hover, .pager-num:hover { background: #e9ecef; text-decoration: none; color: #212529; }
.pager-num.active {
background: #0d6efd;
color: #fff;
border-color: #0d6efd;
}
.pager-btn.disabled {
pointer-events: none;
opacity: .4;
}
.pager-dots {
padding: 0 2px;
color: #adb5bd;
user-select: none;
}
</style> </style>
<title>Edit Imported Data - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title> <title>Edit Imported Data - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
</head> </head>
@ -1003,6 +1111,7 @@ window.gridMeta = <?= json_encode($gridMeta, JSON_UNESCAPED_UNICODE | JSON_UNESC
<div class="mb-3 text d-flex align-items-center gap-2"> <div class="mb-3 text d-flex align-items-center gap-2">
<a href="imported.php?id=<?= $template_id ?>" class="btn btn-warning me-2">Imported (i)</a> <a href="imported.php?id=<?= $template_id ?>" class="btn btn-warning me-2">Imported (i)</a>
<a href="tolims.php?id=<?= $template_id ?>" class="btn btn-success">To LIMS (l)</a> <a href="tolims.php?id=<?= $template_id ?>" class="btn btn-success">To LIMS (l)</a>
<?php if ($importref === ''): ?>
<span class="ms-3"> <span class="ms-3">
<label class="form-check-label" style="font-size: 13px; cursor: pointer;"> <label class="form-check-label" style="font-size: 13px; cursor: pointer;">
<input type="checkbox" class="form-check-input" id="showAllUsers" <?= $show_all_users ? 'checked' : '' ?> <input type="checkbox" class="form-check-input" id="showAllUsers" <?= $show_all_users ? 'checked' : '' ?>
@ -1010,8 +1119,58 @@ window.gridMeta = <?= json_encode($gridMeta, JSON_UNESCAPED_UNICODE | JSON_UNESC
Show all users Show all users
</label> </label>
</span> </span>
<span class="text-muted" style="font-size: 12px;">(<?= count($importedData) ?> records<?= !$show_all_users ? ' — my records only' : '' ?>)</span> <span class="text-muted" style="font-size: 12px;">
(<?= $usePagination ? count($importedData) . " of {$totalRows}" : count($importedData) ?> records<?= !$show_all_users ? ' — my records only' : '' ?>)
</span>
<?php endif; ?>
</div> </div>
<?php if ($usePagination): ?>
<?php
$baseQuery = $_GET;
unset($baseQuery['limit'], $baseQuery['page']);
$pageQuery = $_GET;
unset($pageQuery['page']);
$pageBase = 'imported.php?' . http_build_query($pageQuery);
$fromRow = ($page - 1) * $perPage + 1;
$toRow = min($page * $perPage, $totalRows);
?>
<div class="pager-bar mb-2">
<div class="pager-rows-per-page">
<span class="pager-label">Rows per page</span>
<div class="pager-limit-group">
<?php foreach ($allowedLimits as $lim):
$isActive = ($perPage === $lim);
$url = 'imported.php?' . http_build_query(array_merge($baseQuery, ['limit' => $lim]));
?>
<a href="<?= htmlspecialchars($url) ?>" class="pager-limit-btn <?= $isActive ? 'active' : '' ?>"><?= $lim ?></a>
<?php endforeach; ?>
</div>
</div>
<?php if ($totalPages > 1): ?>
<div class="pager-nav">
<span class="pager-info"><?= $fromRow ?><?= $toRow ?> of <?= $totalRows ?></span>
<a href="<?= htmlspecialchars($pageBase . '&page=1') ?>" class="pager-btn <?= $page <= 1 ? 'disabled' : '' ?>" title="First"><i class="fas fa-angle-double-left"></i></a>
<a href="<?= htmlspecialchars($pageBase . '&page=' . ($page - 1)) ?>" class="pager-btn <?= $page <= 1 ? 'disabled' : '' ?>" title="Previous"><i class="fas fa-angle-left"></i></a>
<?php
$startPage = max(1, $page - 2);
$endPage = min($totalPages, $page + 2);
if ($startPage > 1): ?>
<a href="<?= htmlspecialchars($pageBase . '&page=1') ?>" class="pager-num">1</a>
<?php if ($startPage > 2): ?><span class="pager-dots">...</span><?php endif; ?>
<?php endif;
for ($p = $startPage; $p <= $endPage; $p++): ?>
<a href="<?= htmlspecialchars($pageBase . '&page=' . $p) ?>" class="pager-num <?= $p === $page ? 'active' : '' ?>"><?= $p ?></a>
<?php endfor;
if ($endPage < $totalPages): ?>
<?php if ($endPage < $totalPages - 1): ?><span class="pager-dots">...</span><?php endif; ?>
<a href="<?= htmlspecialchars($pageBase . '&page=' . $totalPages) ?>" class="pager-num"><?= $totalPages ?></a>
<?php endif; ?>
<a href="<?= htmlspecialchars($pageBase . '&page=' . ($page + 1)) ?>" class="pager-btn <?= $page >= $totalPages ? 'disabled' : '' ?>" title="Next"><i class="fas fa-angle-right"></i></a>
<a href="<?= htmlspecialchars($pageBase . '&page=' . $totalPages) ?>" class="pager-btn <?= $page >= $totalPages ? 'disabled' : '' ?>" title="Last"><i class="fas fa-angle-double-right"></i></a>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
<div class="card radius-10"> <div class="card radius-10">
<div class="card-header"> <div class="card-header">
<div class="d-flex align-items-center" style="min-height: 42px; gap: 12px;"> <div class="d-flex align-items-center" style="min-height: 42px; gap: 12px;">

View File

@ -164,10 +164,12 @@
const templateId = window.gridMeta?.templateId; const templateId = window.gridMeta?.templateId;
if (!templateId) { alert('Template ID missing'); return; } if (!templateId) { alert('Template ID missing'); return; }
const urlParams = new URLSearchParams(window.location.search);
const importref = urlParams.get('importref') || '';
const resp = await fetch('add_record.php', { const resp = await fetch('add_record.php', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ template_id: templateId }) body: JSON.stringify({ template_id: templateId, importreferencecode: importref })
}); });
const result = await resp.json(); const result = await resp.json();
@ -186,7 +188,7 @@
fixedFields: {}, fixedFields: {},
details: {}, details: {},
mainFieldValue: '', mainFieldValue: '',
_dirty: true, _dirty: false,
}; };
// Add to beginning of gridData // Add to beginning of gridData
@ -195,6 +197,14 @@
// Re-render // Re-render
const gr = window.gridRenderer; const gr = window.gridRenderer;
if (gr) gr.renderVisibleRows(); if (gr) gr.renderVisibleRows();
// Highlight new row briefly
const newGridRow = document.querySelector(`.grid-row[data-id="${result.iddatadb}"]`);
if (newGridRow) {
newGridRow.classList.add('row-just-created');
newGridRow.scrollIntoView({ behavior: 'smooth', block: 'center' });
setTimeout(() => newGridRow.classList.remove('row-just-created'), 4000);
}
} else { } else {
alert('Error: ' + (result.message || 'Unknown error')); alert('Error: ' + (result.message || 'Unknown error'));
} }

View File

@ -28,6 +28,7 @@
if (result.success) { if (result.success) {
row._dirty = false; row._dirty = false;
if (window.gridRenderer?.clearDirty) window.gridRenderer.clearDirty(rowIndex);
// Flash success on row without re-rendering (preserves Select2 state) // Flash success on row without re-rendering (preserves Select2 state)
const gridRow = document.querySelector(`.grid-row[data-id="${row.iddatadb}"]`); const gridRow = document.querySelector(`.grid-row[data-id="${row.iddatadb}"]`);
if (gridRow) { if (gridRow) {
@ -94,6 +95,7 @@
const result = await resp.json(); const result = await resp.json();
if (result.success) { if (result.success) {
data[idx]._dirty = false; data[idx]._dirty = false;
if (window.gridRenderer?.clearDirty) window.gridRenderer.clearDirty(idx);
success++; success++;
} else { } else {
fail++; fail++;

View File

@ -14,6 +14,10 @@ if (!$template_id) {
$user_id = $iduserlogin ?? 1; $user_id = $iduserlogin ?? 1;
$is_readonly = true; $is_readonly = true;
$show_all_users = isset($_GET['all_users']) && $_GET['all_users'] == '1'; $show_all_users = isset($_GET['all_users']) && $_GET['all_users'] == '1';
$allowedLimits = [20, 40, 60, 100];
$rawLimit = (int)($_GET['limit'] ?? 20);
$perPage = in_array($rawLimit, $allowedLimits) ? $rawLimit : 20;
$page = max(1, (int)($_GET['page'] ?? 1));
$db = DBHandlerSelect::getInstance(); $db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection(); $pdo = $db->getConnection();
@ -69,18 +73,80 @@ $template_stmt->execute([$template_id]);
$template = $template_stmt->fetch(PDO::FETCH_ASSOC); $template = $template_stmt->fetch(PDO::FETCH_ASSOC);
$default_idclient = $template['idclient'] ?? null; $default_idclient = $template['idclient'] ?? null;
// Maps logical fixed_field_key → real datadb column name
$fixedAliasMap = [
'ClienteResponsabile' => 'cliente_responsabile_id',
'ClienteFornitore' => 'cliente_fornitore_id',
'ClienteAnalisi' => 'clienteAnalisi',
'MoltiplicatorePrezzo' => 'moltiplicatore_prezzo_id',
'AnagraficaCertestObject' => 'anagrafica_certest_object_id',
'AnagraficaCertestService' => 'anagrafica_certest_service_id',
'ConsegnaRichiesta' => 'consegna_richiesta',
];
// Fetch records with status='l' (exported to LIMS) for this template // Fetch records with status='l' (exported to LIMS) for this template
$userFilter = $show_all_users ? '' : 'AND d.user_id = ?'; $userFilter = $show_all_users ? '' : 'AND d.user_id = ?';
$filters = $_GET['f'] ?? [];
if (!is_array($filters)) $filters = [];
// Map of filter keys → datadb columns (direct columns)
$directColumnMap = [
'commessaweb' => 'd.commessaweb',
'idclient' => 'd.idclient',
'importreferencecode' => 'd.importreferencecode',
'importdate' => 'd.importdate',
'filename_import' => 'd.filename_import',
'user_name' => "CONCAT(u.first_name, ' ', u.last_name)",
];
// Add fixed field columns
foreach ($fixedAliasMap as $fixedKey => $dbCol) {
$directColumnMap[$fixedKey] = 'd.' . $dbCol;
}
$filterSQL = '';
$filterParams = [];
foreach ($filters as $key => $val) {
$val = trim($val);
if ($val === '') continue;
if (isset($directColumnMap[$key])) {
// Direct datadb column
$filterSQL .= " AND {$directColumnMap[$key]} LIKE ?";
$filterParams[] = '%' . $val . '%';
} elseif (str_starts_with($key, 'detail_')) {
// import_data_details field: detail_{mapping_id}
$mappingId = (int)substr($key, 7);
if ($mappingId > 0) {
$filterSQL .= " AND EXISTS (SELECT 1 FROM import_data_details dd WHERE dd.id = d.iddatadb AND dd.mapping_id = ? AND dd.field_value LIKE ?)";
$filterParams[] = $mappingId;
$filterParams[] = '%' . $val . '%';
}
}
}
$baseWhere = "WHERE d.templateid = ? AND d.status = 'l' {$userFilter} {$filterSQL}";
$baseParams = [$template_id];
if (!$show_all_users) $baseParams[] = $user_id;
$baseParams = array_merge($baseParams, $filterParams);
// Check if any filter is active
$hasActiveFilters = !empty(array_filter($filters, fn($v) => trim($v) !== ''));
$countStmt = $pdo->prepare("SELECT COUNT(*) FROM datadb d LEFT JOIN auth_users u ON d.user_id = u.id {$baseWhere}");
$countStmt->execute($baseParams);
$totalRows = (int)$countStmt->fetchColumn();
$totalPages = max(1, (int)ceil($totalRows / $perPage));
if ($page > $totalPages) $page = $totalPages;
$stmt = $pdo->prepare(" $stmt = $pdo->prepare("
SELECT d.*, CONCAT(u.first_name, ' ', u.last_name) AS user_name SELECT d.*, CONCAT(u.first_name, ' ', u.last_name) AS user_name
FROM datadb d FROM datadb d
LEFT JOIN auth_users u ON d.user_id = u.id LEFT JOIN auth_users u ON d.user_id = u.id
WHERE d.templateid = ? AND d.status = 'l' {$userFilter} {$baseWhere}
ORDER BY d.iddatadb DESC ORDER BY d.iddatadb DESC
LIMIT {$perPage} OFFSET " . (($page - 1) * $perPage) . "
"); ");
$params = [$template_id]; $stmt->execute($baseParams);
if (!$show_all_users) $params[] = $user_id;
$stmt->execute($params);
$importedData = $stmt->fetchAll(PDO::FETCH_ASSOC); $importedData = $stmt->fetchAll(PDO::FETCH_ASSOC);
$insertedIds = array_column($importedData, 'iddatadb'); $insertedIds = array_column($importedData, 'iddatadb');
@ -154,17 +220,6 @@ foreach ($tempMap as $f) {
$fixedFields[] = $f; $fixedFields[] = $f;
} }
// Maps logical fixed_field_key → real datadb column name (mirrors JS fixedAliasMap)
$fixedAliasMap = [
'ClienteResponsabile' => 'cliente_responsabile_id',
'ClienteFornitore' => 'cliente_fornitore_id',
'ClienteAnalisi' => 'clienteAnalisi',
'MoltiplicatorePrezzo' => 'moltiplicatore_prezzo_id',
'AnagraficaCertestObject' => 'anagrafica_certest_object_id',
'AnagraficaCertestService' => 'anagrafica_certest_service_id',
'ConsegnaRichiesta' => 'consegna_richiesta',
];
// helper default (DATE can use 'today' if you already use it elsewhere) // helper default (DATE can use 'today' if you already use it elsewhere)
function fixedDefaultValue(array $f): string function fixedDefaultValue(array $f): string
{ {
@ -173,6 +228,66 @@ function fixedDefaultValue(array $f): string
return (string)$v; return (string)$v;
} }
// ── Build lookup maps: id → label for fixed fields ──
$fixedLookup = []; // e.g. $fixedLookup['MoltiplicatorePrezzo'][2] = 'Urgente (1.5x)'
function loadCacheJson(string $path): ?array {
if (file_exists($path) && (time() - filemtime($path) < 3600)) {
return json_decode(file_get_contents($path), true);
}
return null;
}
// MoltiplicatorePrezzo
$cached = loadCacheJson(__DIR__ . '/cache/moltiplicatori_prezzo.json');
if ($cached) {
$items = $cached['value'] ?? $cached;
foreach ($items as $item) {
$id = $item['IdMoltiplicatorePrezzo'] ?? null;
if ($id !== null) $fixedLookup['MoltiplicatorePrezzo'][$id] = $item['Descrizione'] ?? $item['Codice'] ?? $id;
}
}
// AnagraficaCertestObject
$cached = loadCacheJson(__DIR__ . '/cache/anagrafica_certest_object.json');
if ($cached) {
$items = $cached['value'] ?? $cached;
foreach ($items as $item) {
$id = $item['IdAnagrafica'] ?? null;
if ($id !== null) $fixedLookup['AnagraficaCertestObject'][$id] = $item['NomeAnagrafica'] ?? $item['Codice'] ?? $id;
}
}
// AnagraficaCertestService
$cached = loadCacheJson(__DIR__ . '/cache/anagrafica_certest_service.json');
if ($cached) {
$items = $cached['value'] ?? $cached;
foreach ($items as $item) {
$id = $item['IdAnagrafica'] ?? null;
if ($id !== null) $fixedLookup['AnagraficaCertestService'][$id] = $item['NomeAnagrafica'] ?? $item['Codice'] ?? $id;
}
}
// ClienteResponsabile — per-client cache files
$clienteIds = array_unique(array_filter(array_column($importedData, 'idclient')));
foreach ($clienteIds as $cid) {
$cached = loadCacheJson(__DIR__ . '/cache/cliente_responsabili_' . (int)$cid . '.json');
if ($cached) {
$items = $cached['Responsabili'] ?? [];
foreach ($items as $item) {
$id = $item['IdClienteResponsabile'] ?? null;
if ($id !== null) $fixedLookup['ClienteResponsabile'][$id] = $item['Nominativo'] ?? $id;
}
}
}
// Helper: resolve fixed field id → label
function resolveFixedValue(string $key, $val, array $fixedLookup): string {
if ($val === '' || $val === null) return '';
if (isset($fixedLookup[$key][(int)$val])) return $fixedLookup[$key][(int)$val];
return (string)$val;
}
?> ?>
<!doctype html> <!doctype html>
@ -850,6 +965,161 @@ function fixedDefaultValue(array $f): string
/* View modals — ensure they sit above backdrop */ /* View modals — ensure they sit above backdrop */
#partsViewModal, #photosViewModal { z-index: 1060 !important; } #partsViewModal, #photosViewModal { z-index: 1060 !important; }
.modal-backdrop { z-index: 1050 !important; } .modal-backdrop { z-index: 1050 !important; }
/* ── Grid filter row ── */
.grid-filter-row {
background: #f0f4f8 !important;
border-bottom: 2px solid #dee2e6 !important;
position: sticky;
top: 0;
z-index: 5;
}
.grid-filter-row:hover {
background: #f0f4f8 !important;
}
.grid-filter-row .grid-cell {
padding: 4px 6px !important;
}
.grid-filter-input {
width: 100% !important;
padding: 3px 6px !important;
font-size: 11px !important;
border: 1px solid #ced4da !important;
border-radius: 3px !important;
background: #fff !important;
color: #333 !important;
box-sizing: border-box;
}
.grid-filter-input:focus {
border-color: #80bdff !important;
box-shadow: 0 0 0 2px rgba(0,123,255,.15) !important;
outline: none !important;
}
.grid-filter-input::placeholder {
color: #adb5bd;
font-style: italic;
}
.grid-filter-row .grid-filter-input.has-value {
border-color: #0d6efd !important;
background: #f0f7ff !important;
}
select.grid-filter-input {
cursor: pointer;
padding-right: 18px !important;
appearance: auto;
}
.filter-wrap {
display: flex;
align-items: center;
gap: 2px;
width: 100%;
}
.filter-wrap .grid-filter-input {
flex: 1;
min-width: 0;
}
.filter-clear-btn {
flex-shrink: 0;
border: none;
background: #dc3545;
color: #fff;
font-size: 10px;
cursor: pointer;
padding: 2px 4px;
line-height: 1;
border-radius: 3px;
}
.filter-clear-btn:hover {
background: #c82333;
}
.grid-filter-row .grid-cell {
background: #f0f4f8 !important;
}
/* First sticky cell fills full row height */
.grid-filter-row .grid-cell.button-cell {
align-self: stretch;
}
/* ── Pagination bar ── */
.pager-bar {
display: flex;
align-items: center;
justify-content: space-between;
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 6px;
padding: 6px 14px;
font-size: 13px;
color: #495057;
}
.pager-rows-per-page {
display: flex;
align-items: center;
gap: 8px;
}
.pager-label {
font-weight: 500;
white-space: nowrap;
}
.pager-limit-group {
display: inline-flex;
border: 1px solid #ced4da;
border-radius: 4px;
overflow: hidden;
}
.pager-limit-btn {
padding: 3px 10px;
text-decoration: none;
color: #495057;
border-right: 1px solid #ced4da;
transition: background .15s, color .15s;
font-weight: 500;
}
.pager-limit-btn:last-child { border-right: none; }
.pager-limit-btn:hover { background: #e9ecef; text-decoration: none; color: #212529; }
.pager-limit-btn.active {
background: #0d6efd;
color: #fff;
}
.pager-limit-btn.active:hover { background: #0b5ed7; color: #fff; }
.pager-nav {
display: flex;
align-items: center;
gap: 4px;
}
.pager-info {
margin-right: 8px;
font-weight: 500;
white-space: nowrap;
}
.pager-btn, .pager-num {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 28px;
height: 28px;
padding: 0 6px;
border: 1px solid #ced4da;
border-radius: 4px;
text-decoration: none;
color: #495057;
font-weight: 500;
transition: background .15s, color .15s, border-color .15s;
}
.pager-btn:hover, .pager-num:hover { background: #e9ecef; text-decoration: none; color: #212529; }
.pager-num.active {
background: #0d6efd;
color: #fff;
border-color: #0d6efd;
}
.pager-btn.disabled {
pointer-events: none;
opacity: .4;
}
.pager-dots {
padding: 0 2px;
color: #adb5bd;
user-select: none;
}
</style> </style>
<title>Edit Imported Data - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title> <title>Edit Imported Data - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
</head> </head>
@ -865,17 +1135,72 @@ function fixedDefaultValue(array $f): string
<a href="tolims.php?id=<?= $template_id ?>" class="btn btn-success">To LIMS (l)</a> <a href="tolims.php?id=<?= $template_id ?>" class="btn btn-success">To LIMS (l)</a>
<span class="ms-3"> <span class="ms-3">
<label class="form-check-label" style="font-size: 13px; cursor: pointer;"> <label class="form-check-label" style="font-size: 13px; cursor: pointer;">
<?php
$toggleParams = $_GET;
unset($toggleParams['all_users']);
$toggleBase = 'tolims.php?' . http_build_query($toggleParams);
?>
<input type="checkbox" class="form-check-input" id="showAllUsers" <?= $show_all_users ? 'checked' : '' ?> <input type="checkbox" class="form-check-input" id="showAllUsers" <?= $show_all_users ? 'checked' : '' ?>
onchange="window.location.href='tolims.php?id=<?= $template_id ?>' + (this.checked ? '&all_users=1' : '')"> onchange="window.location.href='<?= htmlspecialchars($toggleBase) ?>' + (this.checked ? '&all_users=1' : '')">
Show all users Show all users
</label> </label>
</span> </span>
<span class="text-muted" style="font-size: 12px;">(<?= count($importedData) ?> records<?= !$show_all_users ? ' — my records' : '' ?>)</span> <span class="text-muted" style="font-size: 12px;">
(<?= count($importedData) ?> of <?= $totalRows ?> records<?= !$show_all_users ? ' — my records' : '' ?>)
</span>
</div>
<?php
$baseQuery = $_GET;
unset($baseQuery['limit'], $baseQuery['page']);
$pageQuery = $_GET;
unset($pageQuery['page']);
$pageBase = 'tolims.php?' . http_build_query($pageQuery);
$fromRow = ($page - 1) * $perPage + 1;
$toRow = min($page * $perPage, $totalRows);
?>
<div class="pager-bar mb-2">
<div class="pager-rows-per-page">
<span class="pager-label">Rows per page</span>
<div class="pager-limit-group">
<?php foreach ($allowedLimits as $lim):
$isActive = ($perPage === $lim);
$url = 'tolims.php?' . http_build_query(array_merge($baseQuery, ['limit' => $lim]));
?>
<a href="<?= htmlspecialchars($url) ?>" class="pager-limit-btn <?= $isActive ? 'active' : '' ?>"><?= $lim ?></a>
<?php endforeach; ?>
</div>
</div>
<?php if ($totalPages > 1): ?>
<div class="pager-nav">
<span class="pager-info"><?= $fromRow ?><?= $toRow ?> of <?= $totalRows ?></span>
<a href="<?= htmlspecialchars($pageBase . '&page=1') ?>" class="pager-btn <?= $page <= 1 ? 'disabled' : '' ?>" title="First"><i class="fas fa-angle-double-left"></i></a>
<a href="<?= htmlspecialchars($pageBase . '&page=' . ($page - 1)) ?>" class="pager-btn <?= $page <= 1 ? 'disabled' : '' ?>" title="Previous"><i class="fas fa-angle-left"></i></a>
<?php
$startPage = max(1, $page - 2);
$endPage = min($totalPages, $page + 2);
if ($startPage > 1): ?>
<a href="<?= htmlspecialchars($pageBase . '&page=1') ?>" class="pager-num">1</a>
<?php if ($startPage > 2): ?><span class="pager-dots">...</span><?php endif; ?>
<?php endif;
for ($p = $startPage; $p <= $endPage; $p++): ?>
<a href="<?= htmlspecialchars($pageBase . '&page=' . $p) ?>" class="pager-num <?= $p === $page ? 'active' : '' ?>"><?= $p ?></a>
<?php endfor;
if ($endPage < $totalPages): ?>
<?php if ($endPage < $totalPages - 1): ?><span class="pager-dots">...</span><?php endif; ?>
<a href="<?= htmlspecialchars($pageBase . '&page=' . $totalPages) ?>" class="pager-num"><?= $totalPages ?></a>
<?php endif; ?>
<a href="<?= htmlspecialchars($pageBase . '&page=' . ($page + 1)) ?>" class="pager-btn <?= $page >= $totalPages ? 'disabled' : '' ?>" title="Next"><i class="fas fa-angle-right"></i></a>
<a href="<?= htmlspecialchars($pageBase . '&page=' . $totalPages) ?>" class="pager-btn <?= $page >= $totalPages ? 'disabled' : '' ?>" title="Last"><i class="fas fa-angle-double-right"></i></a>
</div>
<?php endif; ?>
</div> </div>
<div class="card radius-10"> <div class="card radius-10">
<div class="card-header"> <div class="card-header">
<div class="d-flex align-items-center" style="min-height: 42px; gap: 12px;"> <div class="d-flex align-items-center" style="min-height: 42px; gap: 12px;">
<span style="font-weight: 600; font-size: 14px;"><i class="fas fa-check-circle" style="color: #28a745;"></i> Exported to LIMS</span> <span style="font-weight: 600; font-size: 14px;"><i class="fas fa-check-circle" style="color: #28a745;"></i> Exported to LIMS</span>
<?php if ($hasActiveFilters): ?>
<a href="tolims.php?id=<?= $template_id ?><?= $show_all_users ? '&all_users=1' : '' ?><?= $perPage !== 20 ? '&limit=' . $perPage : '' ?>" class="btn btn-outline-danger btn-sm" style="font-size: 12px;"><i class="fas fa-times"></i> Clear filters</a>
<?php endif; ?>
</div> </div>
</div> </div>
<div class="card-body"> <div class="card-body">
@ -1146,6 +1471,131 @@ function fixedDefaultValue(array $f): string
</div> </div>
<!-- Filter row (server-side) -->
<?php
// Helper: wrap filter element with clear button
function wrapFilter(string $inner, bool $hasValue): string {
$btn = $hasValue
? "<button type='button' class='filter-clear-btn' onclick='clearOneFilter(this)' title='Clear'>&times;</button>"
: '';
return "<div class='filter-wrap'>{$inner}{$btn}</div>";
}
// Helper: render a select filter from a lookup array
function renderFilterSelect(string $name, array $options, string $currentVal, string $style = ''): string {
$html = "<select class='grid-filter-input' name='" . htmlspecialchars($name) . "' style='" . htmlspecialchars($style) . "' onchange='submitFilters()'>";
$html .= "<option value=''>All</option>";
foreach ($options as $id => $label) {
$sel = ((string)$id === $currentVal) ? ' selected' : '';
$html .= "<option value='" . htmlspecialchars((string)$id) . "'{$sel}>" . htmlspecialchars($label) . "</option>";
}
$html .= "</select>";
return wrapFilter($html, $currentVal !== '');
}
// Helper: render a text filter
function renderFilterInput(string $name, string $currentVal): string {
$inner = "<input type='text' class='grid-filter-input' name='" . htmlspecialchars($name) . "' value='" . htmlspecialchars($currentVal) . "' placeholder='&#8981;'>";
return wrapFilter($inner, $currentVal !== '');
}
// Helper: render a select filter populated by JS
function renderFilterSelectJS(string $name, string $currentVal, string $jsClass, string $dataAttr = ''): string {
$html = "<select class='grid-filter-input {$jsClass}' name='" . htmlspecialchars($name) . "' {$dataAttr} onchange='submitFilters()'>";
$html .= "<option value=''>All</option>";
if ($currentVal !== '') {
$html .= "<option value='" . htmlspecialchars($currentVal) . "' selected>" . htmlspecialchars($currentVal) . "</option>";
}
$html .= "</select>";
return wrapFilter($html, $currentVal !== '');
}
// Fields that are client-id lookups
$clientFilterKeys = ['ClienteFornitore', 'ClienteAnalisi'];
?>
<div class="grid-row grid-filter-row" id="gridFilterRow">
<div class="grid-cell button-cell"></div>
<?php if ($mainFieldMapping):
$fKey = 'detail_' . $mainFieldMapping['id'];
$fVal = $filters[$fKey] ?? '';
?>
<div class="grid-cell" style="flex: 0 0 150px; padding: 4px 6px;">
<?php if ($mainFieldMapping['data_type'] === 'SceltaMultipla'): ?>
<?= renderFilterSelectJS("f[$fKey]", $fVal, 'scelta-filter', "data-field-id='{$mainFieldMapping['field_id']}'") ?>
<?php else: ?>
<?= renderFilterInput("f[$fKey]", $fVal) ?>
<?php endif; ?>
</div>
<?php endif; ?>
<!-- Status/CommessaWeb -->
<div class="grid-cell" style="flex: 0 0 150px; padding: 4px 6px;">
<?= renderFilterInput('f[commessaweb]', $filters['commessaweb'] ?? '') ?>
</div>
<!-- Client -->
<div class="grid-cell" style="flex: 0 0 300px; padding: 4px 6px;">
<?= renderFilterSelectJS('f[idclient]', $filters['idclient'] ?? '', 'client-filter') ?>
</div>
<?php
// Auto fields
foreach ($allMappings as $mapping) {
if (!$mapping['is_manual'] && $mapping['main_field'] != 1 && $mapping['is_visible_import'] == 1) {
$fKey = 'detail_' . $mapping['id'];
$fVal = $filters[$fKey] ?? '';
echo "<div class='grid-cell' style='flex: 0 0 150px; padding: 4px 6px;'>";
if ($mapping['data_type'] === 'SceltaMultipla') {
echo renderFilterSelectJS("f[$fKey]", $fVal, 'scelta-filter', "data-field-id='{$mapping['field_id']}'");
} else {
echo renderFilterInput("f[$fKey]", $fVal);
}
echo "</div>";
}
}
// Manual fields
foreach ($allMappings as $mapping) {
if ($mapping['is_manual'] && $mapping['main_field'] != 1 && $mapping['is_visible_import'] == 1) {
$fKey = 'detail_' . $mapping['id'];
$fVal = $filters[$fKey] ?? '';
echo "<div class='grid-cell' style='flex: 0 0 150px; padding: 4px 6px;'>";
if ($mapping['data_type'] === 'SceltaMultipla') {
echo renderFilterSelectJS("f[$fKey]", $fVal, 'scelta-filter', "data-field-id='{$mapping['field_id']}'");
} else {
echo renderFilterInput("f[$fKey]", $fVal);
}
echo "</div>";
}
}
// Tested Component, AWB, Tracking
echo "<div class='grid-cell' style='flex: 0 0 150px; padding: 4px 6px;'></div>";
echo "<div class='grid-cell' style='flex: 0 0 200px; padding: 4px 6px;'></div>";
echo "<div class='grid-cell' style='flex: 0 0 250px; padding: 4px 6px;'></div>";
// Fixed fields
if (!empty($fixedFields)) {
$insertedFilterAfterConsegna = false;
foreach ($fixedFields as $f) {
$key = $f['fixed_field_key'];
$fVal = $filters[$key] ?? '';
echo "<div class='grid-cell' style='flex: 0 0 180px; padding: 4px 6px;'>";
if (in_array($key, $clientFilterKeys, true)) {
// Client-based selects (populated by JS)
echo renderFilterSelectJS("f[$key]", $fVal, 'client-filter');
} elseif (isset($fixedLookup[$key]) && !empty($fixedLookup[$key])) {
// PHP-cached lookups
echo renderFilterSelect("f[$key]", $fixedLookup[$key], $fVal);
} elseif ($key === 'ConsegnaRichiesta') {
echo renderFilterInput("f[$key]", $fVal);
} else {
echo renderFilterInput("f[$key]", $fVal);
}
echo "</div>";
if ($key === 'ConsegnaRichiesta' && !$insertedFilterAfterConsegna) {
echo "<div class='grid-cell' style='flex: 0 0 150px; padding: 4px 6px;'>" . renderFilterInput('f[importreferencecode]', $filters['importreferencecode'] ?? '') . "</div>";
echo "<div class='grid-cell' style='flex: 0 0 150px; padding: 4px 6px;'>" . renderFilterInput('f[filename_import]', $filters['filename_import'] ?? '') . "</div>";
echo "<div class='grid-cell' style='flex: 0 0 150px; padding: 4px 6px;'>" . renderFilterInput('f[importdate]', $filters['importdate'] ?? '') . "</div>";
$insertedFilterAfterConsegna = true;
}
}
}
?>
</div>
<?php foreach ($importedData as $index => $row): ?> <?php foreach ($importedData as $index => $row): ?>
<div class="grid-row" data-id="<?= $row['iddatadb'] ?>"> <div class="grid-row" data-id="<?= $row['iddatadb'] ?>">
<div class="grid-cell button-cell" style="flex: 0 0 120px; position: relative;"> <div class="grid-cell button-cell" style="flex: 0 0 120px; position: relative;">
@ -1156,9 +1606,10 @@ function fixedDefaultValue(array $f): string
$detail = array_filter($manualDetails, fn($d) => $d['mapping_id'] == $mainFieldMapping['id'] && $d['datadb_id'] == $row['iddatadb']); $detail = array_filter($manualDetails, fn($d) => $d['mapping_id'] == $mainFieldMapping['id'] && $d['datadb_id'] == $row['iddatadb']);
$detail = reset($detail) ?: ['field_value' => $mainFieldMapping['manual_default']]; $detail = reset($detail) ?: ['field_value' => $mainFieldMapping['manual_default']];
$fieldValue = $detail['field_value'] ?? $mainFieldMapping['manual_default'] ?? ''; $fieldValue = $detail['field_value'] ?? $mainFieldMapping['manual_default'] ?? '';
$isScelta = ($mainFieldMapping['data_type'] === 'SceltaMultipla');
?> ?>
<div class="grid-cell" data-col="main_field" data-row="<?= $index ?>" data-index="1" style="flex: 0 0 150px;"> <div class="grid-cell" data-col="main_field" data-row="<?= $index ?>" data-index="1" style="flex: 0 0 150px;">
<span><?= htmlspecialchars($fieldValue) ?></span> <span <?= $isScelta ? "class='scelta-value' data-field-id='{$mainFieldMapping['field_id']}'" : '' ?>><?= htmlspecialchars($fieldValue) ?></span>
</div> </div>
<?php endif; ?> <?php endif; ?>
<div class="grid-cell" data-col="status" data-row="<?= $index ?>" data-index="<?= $mainFieldMapping ? 2 : 1 ?>" style="flex: 0 0 150px;"> <div class="grid-cell" data-col="status" data-row="<?= $index ?>" data-index="<?= $mainFieldMapping ? 2 : 1 ?>" style="flex: 0 0 150px;">
@ -1180,8 +1631,11 @@ function fixedDefaultValue(array $f): string
$detail = array_filter($rowDetails, fn($d) => $d['mapping_id'] == $mapping['id']); $detail = array_filter($rowDetails, fn($d) => $d['mapping_id'] == $mapping['id']);
$detail = reset($detail) ?: ['field_value' => $mapping['manual_default']]; $detail = reset($detail) ?: ['field_value' => $mapping['manual_default']];
$fieldValue = $detail['field_value'] ?? $mapping['manual_default'] ?? ''; $fieldValue = $detail['field_value'] ?? $mapping['manual_default'] ?? '';
$isScelta = ($mapping['data_type'] === 'SceltaMultipla');
echo "<div class='grid-cell' data-col='auto_$autoIndex' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'>"; echo "<div class='grid-cell' data-col='auto_$autoIndex' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'>";
echo "<span>" . htmlspecialchars($fieldValue) . "</span>"; echo $isScelta
? "<span class='scelta-value' data-field-id='{$mapping['field_id']}'>" . htmlspecialchars($fieldValue) . "</span>"
: "<span>" . htmlspecialchars($fieldValue) . "</span>";
echo "</div>"; echo "</div>";
$cellIndex++; $cellIndex++;
$autoIndex++; $autoIndex++;
@ -1192,8 +1646,11 @@ function fixedDefaultValue(array $f): string
$detail = array_filter($rowDetails, fn($d) => $d['mapping_id'] == $mapping['id']); $detail = array_filter($rowDetails, fn($d) => $d['mapping_id'] == $mapping['id']);
$detail = reset($detail) ?: ['field_value' => $mapping['manual_default']]; $detail = reset($detail) ?: ['field_value' => $mapping['manual_default']];
$fieldValue = $detail['field_value'] ?? $mapping['manual_default'] ?? ''; $fieldValue = $detail['field_value'] ?? $mapping['manual_default'] ?? '';
$isScelta = ($mapping['data_type'] === 'SceltaMultipla');
echo "<div class='grid-cell' data-col='manual_$autoIndex' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'>"; echo "<div class='grid-cell' data-col='manual_$autoIndex' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'>";
echo "<span>" . htmlspecialchars($fieldValue) . "</span>"; echo $isScelta
? "<span class='scelta-value' data-field-id='{$mapping['field_id']}'>" . htmlspecialchars($fieldValue) . "</span>"
: "<span>" . htmlspecialchars($fieldValue) . "</span>";
echo "</div>"; echo "</div>";
$cellIndex++; $cellIndex++;
} }
@ -1218,9 +1675,15 @@ function fixedDefaultValue(array $f): string
$key = $f['fixed_field_key']; $key = $f['fixed_field_key'];
$dbCol = $fixedAliasMap[$key] ?? $key; $dbCol = $fixedAliasMap[$key] ?? $key;
$val = $row[$dbCol] ?? ''; $val = $row[$dbCol] ?? '';
$isClientField = in_array($key, ['ClienteFornitore', 'ClienteAnalisi'], true);
$displayVal = $isClientField ? (string)$val : resolveFixedValue($key, $val, $fixedLookup);
echo "<div class='grid-cell' data-col='" . htmlspecialchars($key) . "' data-row='$index' data-index='$cellIndex' style='flex: 0 0 180px;'>"; echo "<div class='grid-cell' data-col='" . htmlspecialchars($key) . "' data-row='$index' data-index='$cellIndex' style='flex: 0 0 180px;'>";
echo "<span>" . htmlspecialchars((string)$val) . "</span>"; if ($isClientField && $val !== '' && $val !== null) {
echo "<span class='client-text' data-client-id='" . htmlspecialchars((string)$val) . "'>" . htmlspecialchars((string)$val) . "</span>";
} else {
echo "<span>" . htmlspecialchars($displayVal) . "</span>";
}
echo "</div>"; echo "</div>";
$cellIndex++; $cellIndex++;
@ -1286,18 +1749,80 @@ function fixedDefaultValue(array $f): string
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script> <script>
$(document).ready(function() { $(document).ready(function() {
// Load client names to replace IDs // Resolve SceltaMultipla IDs → Valore (cells + filter selects)
(function() {
const sceltaEls = document.querySelectorAll('.scelta-value');
const sceltaFilters = document.querySelectorAll('select.scelta-filter');
const fieldIds = new Set();
sceltaEls.forEach(el => { if (el.dataset.fieldId) fieldIds.add(el.dataset.fieldId); });
sceltaFilters.forEach(el => { if (el.dataset.fieldId) fieldIds.add(el.dataset.fieldId); });
if (!fieldIds.size) return;
$.getJSON('get_customfield_values.php', { field_ids: [...fieldIds].join(',') }, function(data) {
const lookup = {};
for (const [fid, values] of Object.entries(data)) {
lookup[fid] = {};
(values || []).forEach(v => {
lookup[fid][v.IdCustomFieldsValue] = v.Valore;
});
}
// Resolve cell values
sceltaEls.forEach(el => {
const fid = el.dataset.fieldId;
const raw = (el.textContent || '').trim();
if (fid && lookup[fid] && lookup[fid][raw]) {
el.textContent = lookup[fid][raw];
}
});
// Populate filter selects
sceltaFilters.forEach(sel => {
const fid = sel.dataset.fieldId;
if (!fid || !lookup[fid]) return;
const currentVal = sel.value;
// Sort by Valore
const sorted = Object.entries(lookup[fid]).sort((a, b) => a[1].localeCompare(b[1]));
sel.innerHTML = '<option value="">All</option>';
sorted.forEach(([id, label]) => {
const opt = document.createElement('option');
opt.value = id;
opt.textContent = label;
if (id === currentVal) opt.selected = true;
sel.appendChild(opt);
});
});
});
})();
// Load client names → resolve cells + populate filter selects
$.getJSON('get_clienti.php', function(data) { $.getJSON('get_clienti.php', function(data) {
const clients = data.value || []; const clients = data.value || [];
const map = {}; const map = {};
const sorted = [];
clients.forEach(c => { clients.forEach(c => {
const code = (c.CodiceCliente || '').split('_')[1] || c.CodiceCliente || ''; const name = (c.Nominativo || '').trim();
map[c.IdCliente] = (c.Nominativo || '').trim() + (code ? ' - ' + code : '') + ' (' + c.IdCliente + ')'; map[c.IdCliente] = name;
sorted.push({ id: c.IdCliente, name: name });
}); });
sorted.sort((a, b) => a.name.localeCompare(b.name));
// Resolve cell values
document.querySelectorAll('.client-text').forEach(el => { document.querySelectorAll('.client-text').forEach(el => {
const id = el.getAttribute('data-client-id'); const id = el.getAttribute('data-client-id');
if (id && map[id]) el.textContent = map[id]; if (id && map[id]) el.textContent = map[id];
}); });
// Populate client filter selects
document.querySelectorAll('select.client-filter').forEach(sel => {
const currentVal = sel.value;
sel.innerHTML = '<option value="">All</option>';
sorted.forEach(c => {
const opt = document.createElement('option');
opt.value = c.id;
opt.textContent = c.name;
if (String(c.id) === currentVal) opt.selected = true;
sel.appendChild(opt);
});
});
}); });
// Modal close handlers // Modal close handlers
@ -1358,6 +1883,38 @@ function fixedDefaultValue(array $f): string
}); });
}); });
}); });
// ── Column filters ──
function clearOneFilter(btn) {
const wrap = btn.closest('.filter-wrap');
const el = wrap.querySelector('.grid-filter-input');
if (el) { el.value = ''; submitFilters(); }
}
function submitFilters() {
const filterRow = document.getElementById('gridFilterRow');
if (!filterRow) return;
const params = new URLSearchParams(window.location.search);
for (const key of [...params.keys()]) {
if (key.startsWith('f[') || key === 'page') params.delete(key);
}
filterRow.querySelectorAll('.grid-filter-input').forEach(el => {
const val = el.value.trim();
if (val) params.set(el.name, val);
});
window.location.href = 'tolims.php?' + params.toString();
}
(function() {
const filterRow = document.getElementById('gridFilterRow');
if (!filterRow) return;
filterRow.addEventListener('keydown', function(e) {
if (e.key === 'Enter') { e.preventDefault(); submitFilters(); }
});
filterRow.querySelectorAll('.grid-filter-input').forEach(el => {
el.classList.toggle('has-value', el.value.trim() !== '');
el.addEventListener('input', () => el.classList.toggle('has-value', el.value.trim() !== ''));
el.addEventListener('change', () => el.classList.toggle('has-value', el.value.trim() !== ''));
});
})();
</script> </script>
</body> </body>
</html> </html>