feat(ui): add responsive sidebar with mobile toggle and overlay

Implement responsive sidebar functionality that auto-opens on desktop (≥1024px) and auto-closes on mobile, with a toggle button for mobile users. Added overlay for mobile sidebar dismissal and CSS for collapsed state on desktop. Minor adjustments to dashboard chart borders and grid layouts.
This commit is contained in:
jgor20
2026-01-11 11:26:37 +00:00
parent ed4231310b
commit f9dd71f411
6 changed files with 148 additions and 72 deletions

View File

@@ -57,9 +57,33 @@ document.addEventListener('alpine:init', () => {
return Alpine.store('data')?.loading || false;
},
sidebarOpen: window.innerWidth >= 1024,
toggleSidebar() {
this.sidebarOpen = !this.sidebarOpen;
},
init() {
console.log('App controller initialized');
// Handle responsive sidebar transitions
let lastWidth = window.innerWidth;
window.addEventListener('resize', () => {
const currentWidth = window.innerWidth;
const lgBreakpoint = 1024;
// Desktop -> Mobile: Auto-close sidebar to prevent overlay blocking screen
if (lastWidth >= lgBreakpoint && currentWidth < lgBreakpoint) {
this.sidebarOpen = false;
}
// Mobile -> Desktop: Auto-open sidebar (restore standard desktop layout)
if (lastWidth < lgBreakpoint && currentWidth >= lgBreakpoint) {
this.sidebarOpen = true;
}
lastWidth = currentWidth;
});
// Theme setup
document.documentElement.setAttribute('data-theme', 'black');
document.documentElement.classList.add('dark');

View File

@@ -489,3 +489,12 @@
.skeleton-table-row {
@apply skeleton h-12 w-full mb-2;
}
/* Desktop Sidebar Collapsed State */
@media (min-width: 1024px) {
.sidebar-collapsed {
width: 0 !important;
padding: 0 !important;
border: none !important;
}
}

2
public/css/style.css generated

File diff suppressed because one or more lines are too long

View File

@@ -62,8 +62,15 @@
<!-- Navbar -->
<div
class="h-14 border-b border-space-border flex items-center px-6 justify-between bg-space-900/50 backdrop-blur-md z-50">
class="h-14 border-b border-space-border flex items-center px-4 lg:px-6 justify-between bg-space-900/50 backdrop-blur-md z-50 relative">
<div class="flex items-center gap-3">
<!-- Mobile Menu Button -->
<button @click="toggleSidebar()" class="text-gray-400 hover:text-white focus:outline-none p-1 transition-colors">
<svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
<div
class="w-8 h-8 rounded bg-gradient-to-br from-neon-purple to-blue-600 flex items-center justify-center text-white font-bold shadow-[0_0_15px_rgba(168,85,247,0.4)]">
AG</div>
@@ -103,77 +110,113 @@
</div>
<!-- Layout -->
<div class="flex h-[calc(100vh-56px)]">
<div class="flex h-[calc(100vh-56px)] relative">
<!-- Mobile Sidebar Overlay -->
<div x-show="sidebarOpen"
x-transition:enter="transition-opacity ease-linear duration-300"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition-opacity ease-linear duration-300"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
@click="sidebarOpen = false"
class="fixed inset-0 bg-black/50 z-40 lg:hidden"
style="display: none;"></div>
<!-- Sidebar -->
<div class="w-64 bg-space-900 border-r border-space-border flex flex-col pt-6 pb-4">
<div class="px-4 mb-2 text-xs font-bold text-gray-600 uppercase tracking-widest"
x-text="$store.global.t('main')">Main</div>
<nav class="flex flex-col gap-1">
<button
class="nav-item flex items-center gap-3 px-6 py-3 text-sm font-medium text-gray-400 hover:text-white hover:bg-white/5"
:class="{'active': $store.global.activeTab === 'dashboard'}"
@click="$store.global.activeTab = 'dashboard'">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z" />
</svg>
<span x-text="$store.global.t('dashboard')">Dashboard</span>
</button>
<button
class="nav-item flex items-center gap-3 px-6 py-3 text-sm font-medium text-gray-400 hover:text-white hover:bg-white/5"
:class="{'active': $store.global.activeTab === 'models'}"
@click="$store.global.activeTab = 'models'">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />
</svg>
<span x-text="$store.global.t('models')">Models</span>
</button>
<button
class="nav-item flex items-center gap-3 px-6 py-3 text-sm font-medium text-gray-400 hover:text-white hover:bg-white/5"
:class="{'active': $store.global.activeTab === 'accounts'}"
@click="$store.global.activeTab = 'accounts'">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
</svg>
<span x-text="$store.global.t('accounts')">Accounts</span>
</button>
</nav>
<div class="fixed top-14 bottom-0 left-0 z-40 bg-space-900 border-r border-space-border transition-all duration-300 shadow-2xl overflow-hidden lg:static lg:h-auto lg:shadow-none lg:flex-shrink-0"
:class="{
'translate-x-0': sidebarOpen,
'-translate-x-full': !sidebarOpen,
'w-64': true,
'lg:translate-x-0': true,
'lg:w-64': sidebarOpen,
'sidebar-collapsed': !sidebarOpen
}">
<div class="px-4 mt-8 mb-2 text-xs font-bold text-gray-600 uppercase tracking-widest"
x-text="$store.global.t('system')">System</div>
<nav class="flex flex-col gap-1">
<button
class="nav-item flex items-center gap-3 px-6 py-3 text-sm font-medium text-gray-400 hover:text-white hover:bg-white/5"
:class="{'active': $store.global.activeTab === 'logs'}" @click="$store.global.activeTab = 'logs'">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
<span x-text="$store.global.t('logs')">Logs</span>
</button>
<button
class="nav-item flex items-center gap-3 px-6 py-3 text-sm font-medium text-gray-400 hover:text-white hover:bg-white/5"
:class="{'active': $store.global.activeTab === 'settings'}"
@click="$store.global.activeTab = 'settings'">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
<span x-text="$store.global.t('settings')">Settings</span>
</button>
</nav>
<!-- Inner Sidebar Content (Fixed Width to prevent squashing) -->
<div class="w-64 flex flex-col h-full pt-6 pb-4 flex-shrink-0">
<!-- Mobile Menu Header -->
<div class="flex items-center justify-between px-4 mb-6 lg:hidden">
<span class="text-sm font-bold text-white">Menu</span>
<button @click="sidebarOpen = false" class="text-gray-400 hover:text-white">
<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<!-- Footer Info -->
<div class="mt-auto px-6 text-[10px] text-gray-700 font-mono">
<div class="flex justify-between">
<span x-text="'V ' + $store.global.version">V 1.0.0</span>
<a href="https://github.com/badri-s2001/antigravity-claude-proxy" target="_blank" rel="noopener noreferrer"
class="hover:text-neon-purple transition-colors">GitHub</a>
<!-- Desktop Header (Main) -->
<div class="px-4 mb-2 text-xs font-bold text-gray-600 uppercase tracking-widest hidden lg:block"
x-text="$store.global.t('main')">Main</div>
<nav class="flex flex-col gap-1">
<button
class="nav-item flex items-center gap-3 px-6 py-3 text-sm font-medium text-gray-400 hover:text-white hover:bg-white/5"
:class="{'active': $store.global.activeTab === 'dashboard'}"
@click="$store.global.activeTab = 'dashboard'">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z" />
</svg>
<span x-text="$store.global.t('dashboard')">Dashboard</span>
</button>
<button
class="nav-item flex items-center gap-3 px-6 py-3 text-sm font-medium text-gray-400 hover:text-white hover:bg-white/5"
:class="{'active': $store.global.activeTab === 'models'}"
@click="$store.global.activeTab = 'models'">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />
</svg>
<span x-text="$store.global.t('models')">Models</span>
</button>
<button
class="nav-item flex items-center gap-3 px-6 py-3 text-sm font-medium text-gray-400 hover:text-white hover:bg-white/5"
:class="{'active': $store.global.activeTab === 'accounts'}"
@click="$store.global.activeTab = 'accounts'">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
</svg>
<span x-text="$store.global.t('accounts')">Accounts</span>
</button>
</nav>
<div class="px-4 mt-8 mb-2 text-xs font-bold text-gray-600 uppercase tracking-widest"
x-text="$store.global.t('system')">System</div>
<nav class="flex flex-col gap-1">
<button
class="nav-item flex items-center gap-3 px-6 py-3 text-sm font-medium text-gray-400 hover:text-white hover:bg-white/5"
:class="{'active': $store.global.activeTab === 'logs'}" @click="$store.global.activeTab = 'logs'">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
<span x-text="$store.global.t('logs')">Logs</span>
</button>
<button
class="nav-item flex items-center gap-3 px-6 py-3 text-sm font-medium text-gray-400 hover:text-white hover:bg-white/5"
:class="{'active': $store.global.activeTab === 'settings'}"
@click="$store.global.activeTab = 'settings'">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
<span x-text="$store.global.t('settings')">Settings</span>
</button>
</nav>
<!-- Footer Info -->
<div class="mt-auto px-6 text-[10px] text-gray-700 font-mono">
<div class="flex justify-between">
<span x-text="'V ' + $store.global.version">V 1.0.0</span>
<a href="https://github.com/badri-s2001/antigravity-claude-proxy" target="_blank" rel="noopener noreferrer"
class="hover:text-neon-purple transition-colors">GitHub</a>
</div>
</div>
</div>
</div>

View File

@@ -260,7 +260,7 @@ window.DashboardCharts.updateCharts = function (component) {
data: data,
backgroundColor: colors,
borderColor: getThemeColor("--color-space-950"),
borderWidth: 2,
borderWidth: 0,
hoverOffset: 0,
borderRadius: 0,
},

View File

@@ -34,7 +34,7 @@
<div class="skeleton-stat-card"></div>
<div class="skeleton-stat-card"></div>
<div class="skeleton-stat-card"></div>
<div class="skeleton-stat-card"></div>
<div class="skeleton-stat-card col-span-2 sm:col-span-1"></div>
</div>
<!-- Skeleton Charts -->
@@ -155,7 +155,7 @@
<!-- Global Quota Chart -->
<div
class="stat bg-space-900/40 border border-space-border/30 rounded-xl p-6 h-full flex items-center justify-between gap-3 overflow-hidden relative group hover:border-space-border/60 transition-colors">
class="stat bg-space-900/40 border border-space-border/30 rounded-xl p-6 h-full flex items-center justify-between gap-3 overflow-hidden relative group hover:border-space-border/60 transition-colors col-span-2 sm:col-span-1">
<!-- Chart Container -->
<div class="h-14 w-14 lg:h-16 lg:w-16 relative flex-shrink-0">
<canvas id="quotaChart"></canvas>