diff --git a/public/js/app.js b/public/js/app.js index 474a7c7..44c8b55 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -28,6 +28,8 @@ class AccountManager { this.accountServiceFilter = ''; this.userSearchTerm = ''; this.userRoleFilter = ''; + this.mobileBreakpoint = 900; + this.boundResizeHandler = null; this.configureNotifications(); this.initPromise = this.init(); this.pendingAccountAppId = undefined; @@ -121,6 +123,7 @@ class AccountManager { } this.setupEventListeners(); + this.setupResponsiveShell(); this.loadModals(); // Load modals từ file riêng // Single-page navigation based on hash this.handleRoute(location.hash || '#dashboard'); @@ -129,6 +132,9 @@ class AccountManager { handleRoute(hash) { const route = (hash || '#dashboard').replace('#', '') || 'dashboard'; + if (this.isMobileViewport()) { + this.closeMobileNav(); + } this.renderView(route); } @@ -180,6 +186,74 @@ class AccountManager { }); } + isMobileViewport() { + return window.matchMedia(`(max-width: ${this.mobileBreakpoint}px)`).matches; + } + + setupResponsiveShell() { + const menuBtn = document.getElementById('mobileMenuBtn'); + const backdrop = document.getElementById('sidebarBackdrop'); + + if (menuBtn && !menuBtn.dataset.boundClick) { + menuBtn.addEventListener('click', () => this.toggleMobileNav()); + menuBtn.dataset.boundClick = 'true'; + } + + if (backdrop && !backdrop.dataset.boundClick) { + backdrop.addEventListener('click', () => this.closeMobileNav()); + backdrop.dataset.boundClick = 'true'; + } + + document.querySelectorAll('[data-nav]').forEach(link => { + if (!link.dataset.boundMobileClose) { + link.addEventListener('click', () => { + if (this.isMobileViewport()) { + this.closeMobileNav(); + } + }); + link.dataset.boundMobileClose = 'true'; + } + }); + + if (!this.boundResizeHandler) { + this.boundResizeHandler = () => { + if (!this.isMobileViewport()) { + this.closeMobileNav(); + } + }; + window.addEventListener('resize', this.boundResizeHandler); + } + + if (!this.isMobileViewport()) { + this.closeMobileNav(); + } + } + + toggleMobileNav() { + if (document.body.classList.contains('mobile-nav-open')) { + this.closeMobileNav(); + return; + } + this.openMobileNav(); + } + + openMobileNav() { + if (!this.isMobileViewport()) return; + document.body.classList.add('mobile-nav-open'); + const menuBtn = document.getElementById('mobileMenuBtn'); + if (menuBtn) { + menuBtn.setAttribute('aria-expanded', 'true'); + } + } + + closeMobileNav() { + document.body.classList.remove('mobile-nav-open'); + const menuBtn = document.getElementById('mobileMenuBtn'); + if (menuBtn) { + menuBtn.setAttribute('aria-expanded', 'false'); + } + } + async fetchApplications() { const res = await fetch(`${this.apiBase}/applications`); const data = await res.json(); @@ -284,6 +358,7 @@ class AccountManager { // Close with Escape key document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { + this.closeMobileNav(); this.closeModals(); } }); @@ -306,6 +381,7 @@ class AccountManager { // Account table row clicks this.setupAccountRowListeners(); this.setupFilters(); + this.setupResponsiveShell(); } setupFormListeners() { @@ -390,15 +466,15 @@ class AccountManager { renderDashboard() { return ` -
+
-
+

System Overview

Account & Service Management

-
- +
+ add Add Account @@ -406,7 +482,7 @@ class AccountManager {
-
+
Applications
@@ -461,7 +537,7 @@ class AccountManager {
` : `
-

No accounts yet. Create one

+

No accounts yet. Create one

`}
@@ -475,9 +551,9 @@ class AccountManager { const pageInfo = this.getPaged(filteredAccounts, this.accountPage, this.accountPageSize); this.accountPage = pageInfo.current; return ` -
+
-
+