From f18907d9e46a032cc8bf1675d57ae642fe524497 Mon Sep 17 00:00:00 2001 From: Patrick Balsiger Date: Sun, 31 Aug 2025 11:06:39 +0200 Subject: [PATCH] refactor(tabs): centralize tab wiring in base Component.setupTabs with onChange hook; persist and restore NodeDetails active tab; reuse base tabs in ClusterMembersComponent --- public/scripts/components.js | 56 ++++++++++-------------------------- public/scripts/framework.js | 6 +++- 2 files changed, 20 insertions(+), 42 deletions(-) diff --git a/public/scripts/components.js b/public/scripts/components.js index 7554296..b81e452 100644 --- a/public/scripts/components.js +++ b/public/scripts/components.js @@ -571,32 +571,14 @@ class ClusterMembersComponent extends Component { } setupTabs(container) { - const tabButtons = container.querySelectorAll('.tab-button'); - const tabContents = container.querySelectorAll('.tab-content'); - - tabButtons.forEach(button => { - this.addEventListener(button, 'click', (e) => { - e.stopPropagation(); - - const targetTab = button.dataset.tab; - - // Use base helper to set active tab - this.setActiveTab(targetTab, container); - - // Store active tab state + super.setupTabs(container, { + onChange: (targetTab) => { const memberCard = container.closest('.member-card'); if (memberCard) { const memberIp = memberCard.dataset.memberIp; this.viewModel.storeActiveTab(memberIp, targetTab); } - }); - }); - - // Also prevent event propagation on tab content areas - tabContents.forEach(content => { - this.addEventListener(content, 'click', (e) => { - e.stopPropagation(); - }); + } }); } @@ -884,6 +866,11 @@ class NodeDetailsComponent extends Component { this.setHTML('', html); this.setupTabs(); + // Restore last active tab from view model if available + const restored = this.viewModel && typeof this.viewModel.get === 'function' ? this.viewModel.get('activeTab') : null; + if (restored) { + this.setActiveTab(restored); + } this.setupFirmwareUpload(); } @@ -1158,26 +1145,13 @@ class NodeDetailsComponent extends Component { setupTabs() { console.log('NodeDetailsComponent: Setting up tabs'); - const tabButtons = this.findAllElements('.tab-button'); - const tabContents = this.findAllElements('.tab-content'); - - tabButtons.forEach(button => { - this.addEventListener(button, 'click', (e) => { - e.stopPropagation(); - - const targetTab = button.dataset.tab; - console.log('NodeDetailsComponent: Tab clicked:', targetTab); - - // Update tab UI locally, don't store in view model - this.setActiveTab(targetTab); - }); - }); - - // Also prevent event propagation on tab content areas - tabContents.forEach(content => { - this.addEventListener(content, 'click', (e) => { - e.stopPropagation(); - }); + super.setupTabs(this.container, { + onChange: (tab) => { + // Persist active tab in the view model for restoration + if (this.viewModel && typeof this.viewModel.setActiveTab === 'function') { + this.viewModel.setActiveTab(tab); + } + } }); } diff --git a/public/scripts/framework.js b/public/scripts/framework.js index 1196235..ada9fb0 100644 --- a/public/scripts/framework.js +++ b/public/scripts/framework.js @@ -570,7 +570,8 @@ class Component { } // Tab helpers - setupTabs(container = this.container) { + setupTabs(container = this.container, options = {}) { + const { onChange } = options; const tabButtons = container.querySelectorAll('.tab-button'); const tabContents = container.querySelectorAll('.tab-content'); tabButtons.forEach(button => { @@ -578,6 +579,9 @@ class Component { e.stopPropagation(); const targetTab = button.dataset.tab; this.setActiveTab(targetTab, container); + if (typeof onChange === 'function') { + try { onChange(targetTab); } catch (_) {} + } }); }); tabContents.forEach(content => {