tolims filter, pagination
This commit is contained in:
parent
3e66d67dc5
commit
c573a46318
@ -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("
|
||||||
|
|||||||
@ -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');
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
?>
|
?>
|
||||||
@ -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;">
|
||||||
|
|||||||
@ -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'));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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++;
|
||||||
|
|||||||
@ -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'>×</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='⌕'>";
|
||||||
|
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>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user