From 5c8c73b6ff8d9ffefb6cdc631afc02d5d705d7e8 Mon Sep 17 00:00:00 2001 From: "Beau Beauchamp, WebTigers" Date: Fri, 2 Oct 2026 10:47:20 -0400 Subject: [PATCH] Module Manager: remember the tab filter + table state across the activate reload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The type-filter tab was a transient JS var reset to '' on every load, and activate/deactivate does a full window.location.reload() — so a reload (especially the one after a toggle) always dropped you back on "All" and lost your sort/page/search. Now the chosen tab is persisted per-browser (localStorage, try/catch-guarded) and restored on load (a stale type falls back to All), and the DataTable gets stateSave so sort/page/length/search survive the reload too. The reload still runs (it reflects the server-rendered row state, filter counts, sidebar nav and theme-default marker that activation changes) — it's just no longer destructive to where you were. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01ASauLLscjqdsNqBNsx2Typ --- .../system/views/scripts/modules/index.phtml | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/modules/system/views/scripts/modules/index.phtml b/modules/system/views/scripts/modules/index.phtml index e1de4a6..545f2b5 100644 --- a/modules/system/views/scripts/modules/index.phtml +++ b/modules/system/views/scripts/modules/index.phtml @@ -182,27 +182,44 @@ document.addEventListener('DOMContentLoaded', function () { pageLength: 25, lengthMenu: [[10, 25, 50, -1], [10, 25, 50, 'All']], order: [[0, 'asc']], + stateSave: true, /* remember sort / page / length / search across a reload (incl. the activate reload) */ columnDefs: [{ targets: 5, orderable: false, searchable: false }], language: { search: '', searchPlaceholder: 'Filter modules…' } }); - /* Type filter: a custom search scoped to THIS table, matching each row's data-type. */ + /* Type filter: a custom search scoped to THIS table, matching each row's data-type. The chosen + tab is remembered per-browser (localStorage) so a reload — including the one after an + activate/deactivate — lands back on the same filter, not on "All". */ + var FILTER_KEY = 'tiger.modules.typeFilter'; var activeType = ''; + try { activeType = window.localStorage.getItem(FILTER_KEY) || ''; } catch (e) { activeType = ''; } jQuery.fn.dataTable.ext.search.push(function (settings, data, dataIndex) { if (settings.nTable.id !== 'mods-table') { return true; } if (!activeType) { return true; } var tr = settings.aoData[dataIndex].nTr; return !!tr && tr.getAttribute('data-type') === activeType; }); + function applyType(type) { + activeType = type || ''; + try { + if (activeType) { window.localStorage.setItem(FILTER_KEY, activeType); } + else { window.localStorage.removeItem(FILTER_KEY); } + } catch (e) {} + var pill = activeType + ? document.querySelector('#mods-type-filter [data-type="' + activeType + '"]') + : document.querySelector('#mods-type-filter [data-type=""]'); + if (!pill) { pill = document.querySelector('#mods-type-filter [data-type=""]'); activeType = ''; } /* stale type → All */ + document.querySelectorAll('#mods-type-filter .nav-link').forEach(function (x) { x.classList.remove('active'); }); + if (pill) { pill.classList.add('active'); } + table.draw(); + } document.querySelectorAll('#mods-type-filter [data-type]').forEach(function (pill) { pill.addEventListener('click', function (e) { e.preventDefault(); - document.querySelectorAll('#mods-type-filter .nav-link').forEach(function (x) { x.classList.remove('active'); }); - pill.classList.add('active'); - activeType = pill.getAttribute('data-type'); - table.draw(); + applyType(pill.getAttribute('data-type')); }); }); + if (activeType) { applyType(activeType); } /* restore the remembered tab on load */ } /* ---- activate / deactivate (conflict-aware) — DELEGATED so it survives DataTables redraws ---- */