Hi all,
Working and testing on my first Phoenix V 1.0.9.8 release / site something that borders me in admin/catalog.php is when you update a product - it goes back to the overview page. Instead of giving me feed-back message "product is updated" and reload the edit page. By changing the params on update - this can be accomplished - and this is not to hard.
But I want to stay / reload my opened Tab ... I tried some jQuery code to accccomplish opnening LastTab - but I have to little knowledge to succeed.
Does anyone know a solution?
Thanks!
Admin catalog and orders Tabs - Update
- burt
- Core Team
- Posts: 4550
- Joined: Tue Oct 29, 2019 9:37 am
- Phoenix Version: v1.1.0.8
- : Buy Me A Beverage
- Has thanked: 252 times
- Been thanked: 412 times
Re: Admin catalog
Set the tab in storage, when you redirect to the page, read the storage and open the relevant tab?
That is, more or less, how the product_attributes.php page works - the relevant show/hide thing is opened and stays open for the session. Look at that hook for pointers;
https://github.com/CE-PhoenixCart/Phoen ... te.php#L19
That is, more or less, how the product_attributes.php page works - the relevant show/hide thing is opened and stays open for the session. Look at that hook for pointers;
https://github.com/CE-PhoenixCart/Phoen ... te.php#L19
I am not here to build for you.
I am here to build with you. Let's help each other.
I am here to build with you. Let's help each other.
-
azpro
- Contributor
- Posts: 177
- Joined: Fri Nov 06, 2020 8:25 am
- Phoenix Version: v1.1.0.6
- Has thanked: 30 times
- Been thanked: 34 times
Re: Admin catalog
Hi all,
Me and my friend ChatGPT did some fiddling to create a hook - so we can save eg a product or edit an order without loosing the chosen tab when you click "Save". So on page reload the chosen tab you work on keeps open. It should work for all pages you have tabs (eg catalog.php en orders.php) - cross bwosers and even cross sessions - because it is stored locally on your PC/laptop. Also if you add more tabs (later) it should work.
You can safely put the contents of the code below in a new file named TabsHistoryStorage.php and upload to:
includes/hooks/admin/siteWide/TabsHistoryStorage.php
I can not uppload an attachment (I suppose because of forum safety).
In the file you are using to update a page EG eg
So now you stay on the same page. There must be a more clever way to change the return URL without touching core code. Maybe @burt or @ecartz could give directions.
I tested on V1.1.0.6 - but should work on all versions. As always - backup before testing.
Anyway - I hope this helps!
Arjan
Me and my friend ChatGPT did some fiddling to create a hook - so we can save eg a product or edit an order without loosing the chosen tab when you click "Save". So on page reload the chosen tab you work on keeps open. It should work for all pages you have tabs (eg catalog.php en orders.php) - cross bwosers and even cross sessions - because it is stored locally on your PC/laptop. Also if you add more tabs (later) it should work.
You can safely put the contents of the code below in a new file named TabsHistoryStorage.php and upload to:
includes/hooks/admin/siteWide/TabsHistoryStorage.php
I can not uppload an attachment (I suppose because of forum safety).
Code: Select all
<?php
class hook_admin_siteWide_TabsHistoryStorage {
public function listen_injectBodyEnd() {
$js = <<<'JS'
<script>
(function () {
// --- Polyfill for CSS.escape (needed for some older browsers) ---
if (typeof window.CSS === 'undefined' || typeof window.CSS.escape !== 'function') {
window.CSS = window.CSS || {};
window.CSS.escape = function (sel) { return String(sel).replace(/[^a-zA-Z0-9_\-]/g, "\\$&"); };
}
const TAB_TRIGGER_SELECTOR = '[data-bs-toggle="tab"]';
const STORAGE_PREFIX = 'activeTab';
const PAGE_KEY = location.pathname; // make storage unique per page
// --- Find the tabset container ---
function getTabsetContainer(el) {
let node = el;
while (node && node !== document.documentElement) {
if (node.querySelector && node.querySelector('.tab-content')) return node;
node = node.parentElement;
}
return document.body;
}
// --- Generate a unique key per tabset (page + id or index) ---
function getStorageKey(container) {
const id = container.id ? container.id : getTabsetIndex(container);
return `${STORAGE_PREFIX}::${PAGE_KEY}::${id}`;
}
function getTabsetIndex(container) {
const allSets = Array.from(document.querySelectorAll('.nav.nav-tabs')).map(getTabsetContainer);
const idx = allSets.findIndex((c) => c === container);
return `idx-${Math.max(idx, 0)}`;
}
// --- Helper functions ---
const getTargetFromTrigger = (trigger) => trigger.getAttribute('data-bs-target') || trigger.getAttribute('href');
const findTrigger = (container, target) =>
container.querySelector(`${TAB_TRIGGER_SELECTOR}[href="${target}"], ${TAB_TRIGGER_SELECTOR}[data-bs-target="${target}"]`);
// --- Restore the saved tab for a given tabset ---
function restoreTabset(container) {
let target = null;
// 1) URL hash (if present and valid)
if (location.hash) {
const id = location.hash.substring(1);
if (container.querySelector(`[id="${CSS.escape(id)}"]`)) target = `#${id}`;
}
// 2) Saved tab in localStorage
if (!target) {
const saved = localStorage.getItem(getStorageKey(container));
if (saved && findTrigger(container, saved)) target = saved;
}
// 3) Fallback to first tab
if (!target) {
const first = container.querySelector(TAB_TRIGGER_SELECTOR);
if (first) target = getTargetFromTrigger(first);
}
// Activate via Bootstrap API
if (target) {
const trigger = findTrigger(container, target);
if (trigger) new bootstrap.Tab(trigger).show();
}
}
// --- Save active tab when user switches ---
document.addEventListener('show.bs.tab', function (e) {
const trigger = e.target;
const container = getTabsetContainer(trigger);
const target = getTargetFromTrigger(trigger);
if (target && container) {
localStorage.setItem(getStorageKey(container), target);
// intentionally NOT changing the URL hash
}
});
// --- Restore all tabsets on page ---
function restoreAllTabsets() {
const seen = new Set();
document.querySelectorAll('.nav.nav-tabs').forEach((nav) => {
const container = getTabsetContainer(nav);
if (container && !seen.has(container)) {
seen.add(container);
restoreTabset(container);
}
});
}
// --- Wait until DOM and other scripts are ready ---
function delayedRestore() { queueMicrotask(() => setTimeout(restoreAllTabsets, 0)); }
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', delayedRestore);
} else {
delayedRestore();
}
// --- Observe dynamically added tabs and restore automatically ---
const mo = new MutationObserver((mutations) => {
for (const m of mutations) {
for (const n of m.addedNodes) {
if (n.nodeType === 1 && (n.matches?.(TAB_TRIGGER_SELECTOR) || n.querySelector?.(TAB_TRIGGER_SELECTOR))) {
const trigger = n.matches?.(TAB_TRIGGER_SELECTOR) ? n : n.querySelector(TAB_TRIGGER_SELECTOR);
const container = trigger ? getTabsetContainer(trigger) : null;
if (container) restoreTabset(container);
}
}
}
});
mo.observe(document.documentElement, { childList: true, subtree: true });
})();
</script>
JS;
return $js;
}
}
you should fix the return URL by copying and editing the last line to :admin/includes/actions/catalog/update_product.php
Code: Select all
//return $Admin->link('catalog.php', ['cPath' => $cPath, 'pID' => $products_id]);Code: Select all
return $Admin->link('catalog.php', ['cPath' => $cPath, 'pID' => $products_id ,'action' => 'new_product']);
I tested on V1.1.0.6 - but should work on all versions. As always - backup before testing.
Anyway - I hope this helps!
Arjan
Last edited by azpro on Thu Oct 16, 2025 6:05 am, edited 3 times in total.
-
azpro
- Contributor
- Posts: 177
- Joined: Fri Nov 06, 2020 8:25 am
- Phoenix Version: v1.1.0.6
- Has thanked: 30 times
- Been thanked: 34 times
Re: Admin catalog
Summary of what it does:
- Remembers the last active tab across page reloads and browser sessions (localStorage).
- Works on all admin pages with Bootstrap tabs (any container ID).
- Automatically handles dynamically added tabs.
- Written safely using NOWDOC, so PHP won’t interpret or break the JS.
- Injected at the end of <body>, ensuring Bootstrap JS is already loaded.
- burt
- Core Team
- Posts: 4550
- Joined: Tue Oct 29, 2019 9:37 am
- Phoenix Version: v1.1.0.8
- : Buy Me A Beverage
- Has thanked: 252 times
- Been thanked: 412 times
Re: Admin catalog
You can probably amend the redirecting url with a Hook targetting
listen_updateProductAction
I think you need to manipulate
$GLOBALS['action_redirect']
listen_updateProductAction
I think you need to manipulate
$GLOBALS['action_redirect']
I am not here to build for you.
I am here to build with you. Let's help each other.
I am here to build with you. Let's help each other.