mobile: off-canvas sidebar, responsive grids/charts/top-bar
CI / Lint (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Build (push) Has been cancelled

- Sidebar becomes slide-in overlay under 768px with backdrop
- Hamburger button in top-bar for mobile nav toggle
- Grids stack single-column, charts shrink to 280px
- Tables scroll horizontally with touch momentum
- Status text hidden on mobile (dot stays visible)
- Card headers/actions stack vertically
- Period selectors scroll horizontally

also: image size validation clamp in handleImageGenerations
This commit is contained in:
newkirk
2026-07-27 23:47:23 -04:00
parent ba25f5e6c8
commit c96e1d0350
4 changed files with 234 additions and 9 deletions
+57 -5
View File
@@ -60,23 +60,75 @@ class Dashboard {
const toggleBtn = document.getElementById('sidebar-toggle');
const sidebar = document.querySelector('.sidebar');
const logoutBtn = document.getElementById('logout-btn');
const backdrop = document.getElementById('sidebar-backdrop');
const mobileBtn = document.getElementById('mobile-menu-btn');
const isMobile = () => window.innerWidth < 768;
const closeMobileNav = () => {
if (sidebar) sidebar.classList.remove('mobile-visible');
if (backdrop) backdrop.classList.remove('visible');
};
const toggleMobileNav = () => {
if (!sidebar || !backdrop) return;
const opening = !sidebar.classList.contains('mobile-visible');
sidebar.classList.toggle('mobile-visible', opening);
backdrop.classList.toggle('visible', opening);
document.body.style.overflow = opening && isMobile() ? 'hidden' : '';
};
if (toggleBtn && sidebar) {
toggleBtn.onclick = () => {
sidebar.classList.toggle('collapsed');
localStorage.setItem('sidebar_collapsed', sidebar.classList.contains('collapsed'));
if (isMobile()) {
toggleMobileNav();
} else {
sidebar.classList.toggle('collapsed');
localStorage.setItem('sidebar_collapsed', sidebar.classList.contains('collapsed'));
}
};
if (localStorage.getItem('sidebar_collapsed') === 'true') {
if (!isMobile() && localStorage.getItem('sidebar_collapsed') === 'true') {
sidebar.classList.add('collapsed');
}
}
if (mobileBtn) {
mobileBtn.onclick = toggleMobileNav;
}
if (backdrop) {
backdrop.onclick = closeMobileNav;
}
// Close sidebar on page navigation (mobile)
const menuItems = document.querySelectorAll('.menu-item');
menuItems.forEach(item => {
const origClick = item.onclick;
item.onclick = (e) => {
if (isMobile()) closeMobileNav();
if (origClick) origClick(e);
};
});
if (logoutBtn) {
logoutBtn.onclick = () => {
if (isMobile()) closeMobileNav();
window.authManager.logout();
};
}
// Handle resize
let resizeTimer;
window.addEventListener('resize', () => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
if (!isMobile()) {
closeMobileNav();
document.body.style.overflow = '';
}
}, 200);
});
}
setupRefresh() {