diff --git a/admin.html b/admin.html index ab22480..672889f 100644 --- a/admin.html +++ b/admin.html @@ -12,7 +12,7 @@ - +
diff --git a/app.js b/app.js index 4027a29..4a51565 100644 --- a/app.js +++ b/app.js @@ -35,6 +35,8 @@ const logoutButton = document.querySelector('#dashboardLogoutButton'); const favoritesLink = document.querySelector('#favoritesLink'); const adminPanelLink = document.querySelector('#adminPanelLink'); const userBadge = document.querySelector('#dashboardUserBadge'); +const dashboardMenuButton = document.querySelector('#dashboardMenuButton'); +const dashboardMenuPanel = document.querySelector('#dashboardMenuPanel'); function normalize(value) { return String(value || '').toLowerCase().trim(); @@ -491,6 +493,23 @@ viewOptions.forEach((option) => { searchInput.addEventListener('input', updateVisibility); +function closeDashboardMenu() { + if (!dashboardMenuButton || !dashboardMenuPanel) return; + dashboardMenuButton.setAttribute('aria-expanded', 'false'); + dashboardMenuButton.classList.remove('is-open'); + dashboardMenuPanel.classList.remove('is-open'); + dashboardMenuPanel.hidden = true; +} + +function toggleDashboardMenu() { + if (!dashboardMenuButton || !dashboardMenuPanel) return; + const isOpen = dashboardMenuButton.getAttribute('aria-expanded') === 'true'; + dashboardMenuButton.setAttribute('aria-expanded', String(!isOpen)); + dashboardMenuButton.classList.toggle('is-open', !isOpen); + dashboardMenuPanel.classList.toggle('is-open', !isOpen); + dashboardMenuPanel.hidden = isOpen; +} + function updateAuthNav() { const authenticated = MischLabsAuth.isAuthenticated(); const claims = MischLabsAuth.claims(); @@ -507,6 +526,24 @@ function updateAuthNav() { } } +dashboardMenuButton?.addEventListener('click', (event) => { + event.stopPropagation(); + toggleDashboardMenu(); +}); + +document.addEventListener('click', (event) => { + if (!dashboardMenuPanel || dashboardMenuPanel.hidden) return; + if (!event.target.closest('.dashboard-menu')) { + closeDashboardMenu(); + } +}); + +document.addEventListener('keydown', (event) => { + if (event.key === 'Escape') { + closeDashboardMenu(); + } +}); + loginButton?.addEventListener('click', () => MischLabsAuth.login()); logoutButton?.addEventListener('click', () => MischLabsAuth.logout()); diff --git a/favorites.html b/favorites.html index ba61185..b545422 100644 --- a/favorites.html +++ b/favorites.html @@ -12,7 +12,7 @@ - +
diff --git a/index.html b/index.html index f10c740..96672ef 100644 --- a/index.html +++ b/index.html @@ -15,7 +15,7 @@ - +
@@ -43,13 +43,20 @@ - +
+ + +
@@ -135,6 +142,6 @@ - + diff --git a/offline.html b/offline.html index f86bf62..8218353 100644 --- a/offline.html +++ b/offline.html @@ -11,7 +11,7 @@ - +
diff --git a/style.css b/style.css index 0ff248c..6850ee7 100644 --- a/style.css +++ b/style.css @@ -60,9 +60,9 @@ input { .hero { display: grid; - grid-template-columns: minmax(0, 1fr) auto; - gap: 24px; - align-items: end; + grid-template-columns: minmax(0, 1fr) auto auto; + gap: 16px; + align-items: start; padding: 32px 0 22px; } @@ -119,27 +119,106 @@ h1 { display: grid; grid-template-columns: repeat(3, minmax(82px, 1fr)); gap: 10px; + margin-top: 12px; } -.dashboard-actions { - grid-column: 1 / -1; - display: flex; - justify-content: flex-end; - align-items: center; - gap: 8px; - margin-top: -10px; +.dashboard-menu { + position: relative; + z-index: 30; + margin-top: 12px; } -.user-badge { - min-height: 34px; - display: inline-flex; - align-items: center; - padding: 0 12px; - color: #c4f1ff; - background: rgba(17, 22, 36, 0.72); +.dashboard-menu-button { + width: 42px; + height: 42px; + display: inline-grid; + place-content: center; + gap: 5px; + color: #d8e7ff; + background: rgba(17, 22, 36, 0.82); border: 1px solid var(--border); border-radius: var(--radius); - font-size: 0.82rem; + cursor: pointer; + transition: border-color 0.2s ease, background 0.2s ease, transform 0.2s ease; +} + +.dashboard-menu-button span { + width: 17px; + height: 2px; + display: block; + background: currentColor; + border-radius: 999px; +} + +.dashboard-menu-button:hover, +.dashboard-menu-button.is-open { + background: rgba(103, 232, 249, 0.12); + border-color: rgba(103, 232, 249, 0.38); +} + +.dashboard-menu-button:active { + transform: translateY(1px); +} + +.dashboard-menu-panel { + position: absolute; + top: calc(100% + 10px); + right: 0; + width: min(260px, calc(100vw - 32px)); + display: grid; + gap: 6px; + padding: 10px; + background: rgba(9, 13, 26, 0.96); + border: 1px solid rgba(148, 163, 184, 0.22); + border-radius: 10px; + box-shadow: 0 22px 55px rgba(0, 0, 0, 0.42); + backdrop-filter: blur(18px); +} + +.dashboard-menu-panel[hidden] { + display: none; +} + +.dashboard-menu-user { + display: block; + padding: 9px 10px; + color: #c4f1ff; + border-bottom: 1px solid rgba(148, 163, 184, 0.14); + font-size: 0.78rem; + font-weight: 700; +} + +.dashboard-menu-user[hidden] { + display: none; +} + +.dashboard-menu-item { + min-height: 36px; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 10px; + color: var(--text); + background: transparent; + border: 1px solid transparent; + border-radius: 8px; + font: inherit; + font-size: 0.84rem; + text-decoration: none; + cursor: pointer; +} + +.dashboard-menu-item:hover { + background: rgba(148, 163, 184, 0.1); + border-color: rgba(148, 163, 184, 0.16); +} + +.dashboard-menu-item[hidden] { + display: none; +} + +.dashboard-menu-item.is-danger { + color: #fecdd3; } .metric { @@ -931,10 +1010,20 @@ main { @media (max-width: 900px) { .hero { - grid-template-columns: 1fr; + grid-template-columns: minmax(0, 1fr) auto; align-items: start; } + .brand { + grid-column: 1 / 2; + } + + .dashboard-menu { + grid-column: 2 / 3; + grid-row: 1; + justify-self: end; + } + .admin-actions { justify-content: flex-start; } @@ -960,14 +1049,10 @@ main { } .hero-meta { + grid-column: 1 / -1; width: 100%; } - .dashboard-actions { - justify-content: flex-start; - margin-top: 0; - } - .control-bar { grid-template-columns: 1fr; } diff --git a/sw.js b/sw.js index ef2f977..efbae3d 100644 --- a/sw.js +++ b/sw.js @@ -1,12 +1,12 @@ -const CACHE_NAME = 'mischlabs-pwa-v19'; +const CACHE_NAME = 'mischlabs-pwa-v20'; const APP_SHELL = [ '/', '/index.html', '/favorites.html', '/offline.html', - '/style.css?v=29', + '/style.css?v=30', '/auth.js?v=2', - '/app.js?v=35', + '/app.js?v=36', '/favorites.js?v=1', '/services.json?v=1', '/manifest.webmanifest?v=4',