diff --git a/public/userarea/filter_records.php b/public/userarea/filter_records.php index 2ed5565..6c47bda 100644 --- a/public/userarea/filter_records.php +++ b/public/userarea/filter_records.php @@ -28,6 +28,60 @@ $wantAllIds = !empty($body['want_all_ids']); $cacheDir = __DIR__ . '/cache'; +$explicitIds = []; +if (isset($body['ids']) && is_array($body['ids'])) { + $explicitIds = array_values(array_unique(array_filter( + array_map('intval', $body['ids']), + fn($v) => $v > 0 + ))); +} +if (!empty($explicitIds)) { + // Scoping di sicurezza: solo record del template/utente/import corretti. + $conds = ['d.templateid = ?', 'd.status = ?']; + $params = [$templateId, $status]; + if (!$showAll) { $conds[] = 'd.user_id = ?'; $params[] = $userId; } + if ($importref !== '') { $conds[] = 'd.importreferencecode = ?'; $params[] = $importref; } + $ph = implode(',', array_fill(0, count($explicitIds), '?')); + $conds[] = "d.iddatadb IN ($ph)"; + foreach ($explicitIds as $id) $params[] = $id; + $whereSql = implode(' AND ', $conds); + + try { + $countStmt = $pdo->prepare("SELECT COUNT(*) FROM datadb d WHERE {$whereSql}"); + $countStmt->execute($params); + $total = (int)$countStmt->fetchColumn(); + $totalPages = max(1, (int)ceil($total / $perPage)); + if ($page > $totalPages) $page = $totalPages; + $offset = ($page - 1) * $perPage; + + $idStmt = $pdo->prepare(" + SELECT d.iddatadb FROM datadb d + WHERE {$whereSql} + ORDER BY d.excelrow ASC, d.iddatadb ASC + LIMIT {$perPage} OFFSET {$offset} + "); + $idStmt->execute($params); + $pageIds = array_map('intval', $idStmt->fetchAll(PDO::FETCH_COLUMN)); + + $config = buildGridConfig($pdo, $templateId); + $rows = buildGridRows($pdo, $pageIds, $config); + + echo json_encode([ + 'success' => true, + 'total' => $total, + 'page' => $page, + 'per_page' => $perPage, + 'total_pages' => $totalPages, + 'rows' => $rows, + 'mode' => 'selection', + ]); + } catch (Exception $e) { + error_log('filter_records (ids mode) error: ' . $e->getMessage()); + echo json_encode(['success' => false, 'message' => $e->getMessage()]); + } + exit; +} + $fixedAliasMap = [ 'ClienteResponsabile' => 'cliente_responsabile_id', 'ClienteFornitore' => 'cliente_fornitore_id', diff --git a/public/userarea/gridFilter.js b/public/userarea/gridFilter.js index 1f9f056..7b8b721 100644 --- a/public/userarea/gridFilter.js +++ b/public/userarea/gridFilter.js @@ -39,6 +39,8 @@ let filterTotal = 0; let originalData = null; // snapshot della pagina originale (per ripristino) let filterReqSeq = 0; + let showingSelection = false; // true quando mostriamo l'UNIONE dei selezionati + let pagerFn = null; // paginazione corrente (applyServerFilter | showSelectedRecords) function collectActiveFilters() { const cols = meta().columns || []; @@ -77,14 +79,16 @@ return; } el.style.display = "inline-flex"; + const label = showingSelection ? "selezionati" : "trovati"; el.innerHTML = `` + - `${filterTotal} trovati · pag ${filterPage}/${filterTotalPages}` + + `${filterTotal} ${label} · pag ${filterPage}/${filterTotalPages}` + ``; + const go = pagerFn || applyServerFilter; const prev = document.getElementById("filterPrevBtn"); const next = document.getElementById("filterNextBtn"); - if (prev) prev.addEventListener("click", () => applyServerFilter(filterPage - 1)); - if (next) next.addEventListener("click", () => applyServerFilter(filterPage + 1)); + if (prev) prev.addEventListener("click", () => go(filterPage - 1)); + if (next) next.addEventListener("click", () => go(filterPage + 1)); } function restoreOriginal() { @@ -128,6 +132,8 @@ return; } serverFiltered = true; + showingSelection = false; + pagerFn = applyServerFilter; filterPage = json.page; filterTotalPages = json.total_pages; filterTotal = json.total; @@ -140,6 +146,91 @@ } } + // ── Mostra i SELEZIONATI (unione tra filtri diversi) ────────────────── + // La selezione si accumula tra filtri; questo carica dal server TUTTI i record + // selezionati (per id) e li mostra, paginati, a prescindere dai filtri correnti. + async function showSelectedRecords(page = 1) { + const ids = [...selected]; + if (!ids.length) { + showingSelection = false; + updateToolbar(); + return; + } + pagerFn = showSelectedRecords; + try { + const resp = await fetch("filter_records.php", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + template_id: parseInt(TEMPLATE_ID, 10), + all_users: SHOW_ALL, + importref: IMPORTREF, + status: "i", + ids: ids, + page: Math.max(1, page), + per_page: FILTER_PER_PAGE, + }), + }); + const json = await resp.json(); + if (!json.success) { + console.error("[gridFilter] mostra selezionati:", json.message); + return; + } + if (!originalData) originalData = [...data()]; + showingSelection = true; + serverFiltered = true; + filterPage = json.page; + filterTotalPages = json.total_pages; + filterTotal = json.total; + swapData(json.rows || []); + updateFilterPager(); + hidePagination(true); + updateToolbar(); + } catch (e) { + console.error("[gridFilter] mostra selezionati fallita", e); + } + } + + // Tumbler "Mostra selezionati" / "Torna al filtro". + function toggleShowSelected() { + if (!showingSelection) { + if (!selected.size) return; + showSelectedRecords(1); + } else { + exitSelectionView(); + } + } + + // Torna dal "mostra selezionati" al filtro corrente (o alla pagina originale). + function exitSelectionView() { + showingSelection = false; + pagerFn = applyServerFilter; + if (collectActiveFilters().length) { + applyServerFilter(1); + } else { + restoreOriginal(); + } + updateToolbar(); + } + + // Deseleziona tutto. + function clearSelection() { + selected.clear(); + document + .querySelectorAll("#gridRowContainer .grid-row.row-selected") + .forEach((el) => el.classList.remove("row-selected")); + document + .querySelectorAll(".filter-row-checkbox") + .forEach((cb) => (cb.checked = false)); + const sa = document.getElementById("filterSelectAll"); + if (sa) sa.checked = false; + if (showingSelection) { + exitSelectionView(); + } else { + updateToolbar(); + } + } + // "Seleziona tutti i filtrati": chiede al server TUTTI gli id del match e li seleziona. async function selectAllMatching(on) { if (!serverFiltered) return; @@ -466,11 +557,15 @@ const cntEl = document.getElementById("filterSelCount"); if (cntEl) cntEl.textContent = count; document.getElementById("filterDeleteBtn").disabled = count === 0; - document.getElementById("filterRestrictBtn").disabled = - count === 0 && !restricted; - document.getElementById("filterRestrictBtn").innerHTML = restricted - ? ' Mostra tutti' - : ' Restringi selezione'; + + const showBtn = document.getElementById("filterRestrictBtn"); + showBtn.disabled = count === 0 && !showingSelection; + showBtn.innerHTML = showingSelection + ? ' Torna al filtro' + : ' Mostra selezionati'; + + const clearSelBtn = document.getElementById("filterClearSelBtn"); + if (clearSelBtn) clearSelBtn.disabled = count === 0; } // ── Main toggle ── @@ -584,7 +679,8 @@ Selezionate: 0 - + + `; fBtn.parentNode.insertBefore(bar, fBtn.nextSibling); @@ -598,7 +694,10 @@ .addEventListener("click", batchDelete); document .getElementById("filterRestrictBtn") - .addEventListener("click", toggleRestrict); + .addEventListener("click", toggleShowSelected); + document + .getElementById("filterClearSelBtn") + .addEventListener("click", clearSelection); } // Svuota tutti i filtri di colonna (input + select) e torna alla pagina originale.