cambiato ttl warm cache

This commit is contained in:
2026-09-05 13:41:26 +02:00
parent 14a7cc2738
commit d9d6a7ab58
10 changed files with 215 additions and 88 deletions
@@ -40,7 +40,7 @@ try {
$data = null;
$fromCache = false;
if (!$simulate && !$refresh && file_exists($cache_file) && (time() - filemtime($cache_file) < 3600)) { // 1 ora
if (!$simulate && !$refresh && file_exists($cache_file) && (time() - filemtime($cache_file) < 7200)) { // 1 ora
$data = json_decode(file_get_contents($cache_file), true);
$fromCache = is_array($data);
}
+1 -1
View File
@@ -76,7 +76,7 @@ try {
// Cache file (1 hour TTL)
$cacheFile = __DIR__ . '/cache/clienti.json';
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 7200)) {
readfile($cacheFile);
exit;
}
+1 -1
View File
@@ -101,7 +101,7 @@ try {
// Cache file (1 hour TTL)
$cacheFile = __DIR__ . '/cache/clienti.json';
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 7200)) {
readfile($cacheFile);
exit;
}
+1 -1
View File
@@ -76,7 +76,7 @@ try {
// Cache file (1 hour TTL)
$cacheFile = __DIR__ . '/cache/clienti.json';
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 7200)) {
readfile($cacheFile);
exit;
}
+1 -1
View File
@@ -28,7 +28,7 @@ try {
$cacheFile = $cacheDir . '/customfield_' . $customFieldId . '.json';
// Use cache if fresh (1 hour)
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 7200)) {
$results[$customFieldId] = json_decode(file_get_contents($cacheFile), true);
continue;
}
+1 -1
View File
@@ -67,7 +67,7 @@ try {
}
// Caching skipped in simulate mode to avoid polluting real-data cache files
if (!$simulate && $cache_file && file_exists($cache_file) && (time() - filemtime($cache_file) < 3600)) { // 1 ora
if (!$simulate && $cache_file && file_exists($cache_file) && (time() - filemtime($cache_file) < 7200)) { // 1 ora
$data = json_decode(file_get_contents($cache_file), true);
} else {
$data = $api->get($endpoint, $options);
+6 -2
View File
@@ -2,7 +2,11 @@
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
require_once __DIR__ . '/class/db-functions.php';
include dirname(__DIR__) . '/../extra/auth.php';
if (!Auth::check()) { http_response_code(401); echo json_encode(['error' => 'Unauthorized']); exit; }
if (!Auth::check()) {
http_response_code(401);
echo json_encode(['error' => 'Unauthorized']);
exit;
}
require_once __DIR__ . '/class/VisualLimsApiClient.class.php';
@@ -13,7 +17,7 @@ error_reporting(E_ALL);
try {
// Cache file (1 hour TTL)
$cacheFile = __DIR__ . '/cache/matrici.json';
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 7200)) {
readfile($cacheFile);
exit;
}
+1 -1
View File
@@ -51,7 +51,7 @@ try {
// Load from cache or API
$cacheFile = __DIR__ . '/cache/clienti.json';
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 7200)) {
$data = json_decode(file_get_contents($cacheFile), true);
} else {
$api = VisualLimsApiClient::getInstance();
@@ -29,7 +29,7 @@ try {
$cacheDir = __DIR__ . '/cache';
$cacheFile = $cacheDir . '/customfield_' . $fieldId . '.json';
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 7200)) {
$values = json_decode(file_get_contents($cacheFile), true);
} else {
$api = VisualLimsApiClient::getInstance();
+201 -78
View File
@@ -227,8 +227,9 @@ function fixedDefaultValue(array $f): string
// ── 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)) {
function loadCacheJson(string $path): ?array
{
if (file_exists($path) && (time() - filemtime($path) < 7200)) {
return json_decode(file_get_contents($path), true);
}
return null;
@@ -278,7 +279,8 @@ foreach ($clienteIds as $cid) {
}
// Helper: resolve fixed field id → label
function resolveFixedValue(string $key, $val, array $fixedLookup): string {
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;
@@ -649,6 +651,7 @@ function resolveFixedValue(string $key, $val, array $fixedLookup): string {
.grid-top {
display: none !important;
}
.grid-top-original {
display: flex;
align-items: stretch;
@@ -952,15 +955,34 @@ function resolveFixedValue(string $key, $val, array $fixedLookup): string {
}
/* Hide grid-top (propagate row) */
.grid-top { display: none !important; }
.grid-top {
display: none !important;
}
/* Text cells styling */
.grid-row .grid-cell span { font-size: 12px; padding: 2px 4px; }
.propagate-btn { display: none !important; }
.button-cell { flex: 0 0 120px !important; }
.grid-row .grid-cell span {
font-size: 12px;
padding: 2px 4px;
}
.propagate-btn {
display: none !important;
}
.button-cell {
flex: 0 0 120px !important;
}
/* View modals — ensure they sit above backdrop */
#partsViewModal, #photosViewModal { z-index: 1060 !important; }
.modal-backdrop { z-index: 1050 !important; }
#partsViewModal,
#photosViewModal {
z-index: 1060 !important;
}
.modal-backdrop {
z-index: 1050 !important;
}
/* ── Grid filter row ── */
.grid-filter-row {
background: #f0f4f8 !important;
@@ -969,12 +991,15 @@ function resolveFixedValue(string $key, $val, array $fixedLookup): string {
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;
@@ -985,30 +1010,36 @@ function resolveFixedValue(string $key, $val, array $fixedLookup): string {
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;
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;
}
/* Select2 inside filter row */
.grid-filter-row .select2-container {
width: 100% !important;
min-width: 0;
flex: 1;
}
.grid-filter-row .select2-container .select2-selection--single {
height: 26px !important;
min-height: 26px !important;
@@ -1020,6 +1051,7 @@ function resolveFixedValue(string $key, $val, array $fixedLookup): string {
border-radius: 3px;
position: relative;
}
.grid-filter-row .select2-container .select2-selection__rendered {
line-height: 1 !important;
font-size: 11px;
@@ -1029,32 +1061,39 @@ function resolveFixedValue(string $key, $val, array $fixedLookup): string {
text-overflow: ellipsis;
white-space: nowrap;
}
.grid-filter-row .select2-container .select2-selection__arrow {
height: 26px !important;
position: absolute;
right: 2px;
top: 0;
}
.grid-filter-row .filter-wrap {
overflow: visible;
}
/* Hide our clear button when Select2 is active — Select2 has its own allowClear */
.filter-wrap:has(.select2-container) .filter-clear-btn {
display: none;
}
.grid-filter-row .grid-cell {
overflow: visible !important;
}
.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;
@@ -1066,12 +1105,15 @@ function resolveFixedValue(string $key, $val, array $fixedLookup): string {
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;
@@ -1089,21 +1131,25 @@ function resolveFixedValue(string $key, $val, array $fixedLookup): string {
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;
@@ -1112,24 +1158,41 @@ function resolveFixedValue(string $key, $val, array $fixedLookup): string {
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: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-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 {
.pager-btn,
.pager-num {
display: inline-flex;
align-items: center;
justify-content: center;
@@ -1143,16 +1206,25 @@ function resolveFixedValue(string $key, $val, array $fixedLookup): string {
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-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;
@@ -1188,48 +1260,48 @@ function resolveFixedValue(string $key, $val, array $fixedLookup): string {
</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);
$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; ?>
<?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>
<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 class="card radius-10">
@@ -1512,14 +1584,16 @@ function resolveFixedValue(string $key, $val, array $fixedLookup): string {
<!-- Filter row (server-side) -->
<?php
// Helper: wrap filter element with clear button
function wrapFilter(string $inner, bool $hasValue): string {
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 {
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) {
@@ -1530,12 +1604,14 @@ function resolveFixedValue(string $key, $val, array $fixedLookup): string {
return wrapFilter($html, $currentVal !== '');
}
// Helper: render a text filter
function renderFilterInput(string $name, string $currentVal): string {
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 {
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 !== '') {
@@ -1792,10 +1868,14 @@ function resolveFixedValue(string $key, $val, array $fixedLookup): string {
(function() {
const sceltaEls = document.querySelectorAll('.scelta-value');
const fieldIds = new Set();
sceltaEls.forEach(el => { if (el.dataset.fieldId) fieldIds.add(el.dataset.fieldId); });
sceltaEls.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) {
$.getJSON('get_customfield_values.php', {
field_ids: [...fieldIds].join(',')
}, function(data) {
const lookup = {};
for (const [fid, values] of Object.entries(data)) {
lookup[fid] = {};
@@ -1836,8 +1916,18 @@ function resolveFixedValue(string $key, $val, array $fixedLookup): string {
url: 'search_customfield_values.php',
dataType: 'json',
delay: 150,
data: function(params) { return { field_id: fieldId, q: params.term || '', limit: 20 }; },
processResults: function(data) { return { results: data.results || [] }; },
data: function(params) {
return {
field_id: fieldId,
q: params.term || '',
limit: 20
};
},
processResults: function(data) {
return {
results: data.results || []
};
},
cache: true
}
});
@@ -1859,16 +1949,16 @@ function resolveFixedValue(string $key, $val, array $fixedLookup): string {
const promises = [...ids].map(id =>
fetch('search_clienti.php?id=' + encodeURIComponent(id))
.then(r => r.json())
.then(json => {
const item = (json.results || [])[0];
if (item) {
document.querySelectorAll(`.client-text[data-client-id="${id}"]`).forEach(el => {
el.textContent = item.text;
});
}
})
.catch(() => {})
.then(r => r.json())
.then(json => {
const item = (json.results || [])[0];
if (item) {
document.querySelectorAll(`.client-text[data-client-id="${id}"]`).forEach(el => {
el.textContent = item.text;
});
}
})
.catch(() => {})
);
Promise.all(promises);
})();
@@ -1893,8 +1983,17 @@ function resolveFixedValue(string $key, $val, array $fixedLookup): string {
url: 'search_clienti.php',
dataType: 'json',
delay: 150,
data: function(params) { return { q: params.term || '', limit: 20 }; },
processResults: function(data) { return { results: data.results || [] }; },
data: function(params) {
return {
q: params.term || '',
limit: 20
};
},
processResults: function(data) {
return {
results: data.results || []
};
},
cache: true
}
});
@@ -1909,8 +2008,17 @@ function resolveFixedValue(string $key, $val, array $fixedLookup): string {
url: 'search_clienti.php',
dataType: 'json',
delay: 150,
data: function(params) { return { q: params.term || '', limit: 20 }; },
processResults: function(data) { return { results: data.results || [] }; },
data: function(params) {
return {
q: params.term || '',
limit: 20
};
},
processResults: function(data) {
return {
results: data.results || []
};
},
cache: true
}
});
@@ -1942,14 +2050,16 @@ function resolveFixedValue(string $key, $val, array $fixedLookup): string {
$('#partsViewContent').html('<i class="fas fa-spinner fa-spin"></i> Loading...');
new bootstrap.Modal(document.getElementById('partsViewModal')).show();
$.getJSON('load_parts.php', { iddatadb: iddatadb }, function(data) {
$.getJSON('load_parts.php', {
iddatadb: iddatadb
}, function(data) {
if (!data.parts || data.parts.length === 0) {
$('#partsViewContent').html('<p class="text-muted">No parts found.</p>');
return;
}
let html = '<table class="table table-sm table-bordered"><thead><tr><th>#</th><th>Description</th><th>Material</th><th>Color</th><th>Matrice</th><th>Note</th></tr></thead><tbody>';
data.parts.forEach(p => {
html += '<tr><td>' + (p.part_number||'') + '</td><td>' + (p.part_description||'') + '</td><td>' + (p.material||'') + '</td><td>' + (p.color||'') + '</td><td>' + (p.idmatrice||'') + '</td><td>' + (p.note||'') + '</td></tr>';
html += '<tr><td>' + (p.part_number || '') + '</td><td>' + (p.part_description || '') + '</td><td>' + (p.material || '') + '</td><td>' + (p.color || '') + '</td><td>' + (p.idmatrice || '') + '</td><td>' + (p.note || '') + '</td></tr>';
});
html += '</tbody></table>';
$('#partsViewContent').html(html);
@@ -1964,7 +2074,9 @@ function resolveFixedValue(string $key, $val, array $fixedLookup): string {
$('#photosViewContent').html('<i class="fas fa-spinner fa-spin"></i> Loading...');
new bootstrap.Modal(document.getElementById('photosViewModal')).show();
$.getJSON('load_photo.php', { iddatadb: iddatadb }, function(data) {
$.getJSON('load_photo.php', {
iddatadb: iddatadb
}, function(data) {
if (!data.success || !data.photos || data.photos.length === 0) {
$('#photosViewContent').html('<p class="text-muted">No photos found.</p>');
return;
@@ -1985,8 +2097,12 @@ function resolveFixedValue(string $key, $val, array $fixedLookup): string {
function clearOneFilter(btn) {
const wrap = btn.closest('.filter-wrap');
const el = wrap.querySelector('.grid-filter-input');
if (el) { el.value = ''; submitFilters(); }
if (el) {
el.value = '';
submitFilters();
}
}
function submitFilters() {
const filterRow = document.getElementById('gridFilterRow');
if (!filterRow) return;
@@ -2004,7 +2120,10 @@ function resolveFixedValue(string $key, $val, array $fixedLookup): string {
const filterRow = document.getElementById('gridFilterRow');
if (!filterRow) return;
filterRow.addEventListener('keydown', function(e) {
if (e.key === 'Enter') { e.preventDefault(); submitFilters(); }
if (e.key === 'Enter') {
e.preventDefault();
submitFilters();
}
});
filterRow.querySelectorAll('.grid-filter-input').forEach(el => {
el.classList.toggle('has-value', el.value.trim() !== '');
@@ -2016,7 +2135,10 @@ function resolveFixedValue(string $key, $val, array $fixedLookup): string {
// ── Column resize ──
(function() {
const resizers = document.querySelectorAll('.resizer');
let currentResizer = null, startX = 0, startWidth = 0, columnIndex = null;
let currentResizer = null,
startX = 0,
startWidth = 0,
columnIndex = null;
function resize(e) {
if (!currentResizer || columnIndex === null) return;
@@ -2059,4 +2181,5 @@ function resolveFixedValue(string $key, $val, array $fixedLookup): string {
})();
</script>
</body>
</html>
</html>