feat: complete UI/UX overhaul - dashboard unification, calendar UX, mobile optimization
- Dashboard redesign as command center with filters, quick actions, inline approve/reject - Reusable components: BookingRow, BookingFilters, ActionMenu, BookingPreviewModal, BookingEditModal - Calendar: drag & drop reschedule, eventClick preview modal, grid/list toggle - Mobile: segmented control bookings/calendar toggle, compact pills, responsive layout - Collapsible filters with active count badge - Smart menu positioning with Teleport - Calendar/list bidirectional data sync - Navigation: unified History page, removed AdminPending - Google Calendar OAuth integration - Dark mode contrast improvements, breadcrumb navigation - useLocalStorage composable for state persistence Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<aside class="sidebar" :class="{ collapsed, 'mobile-open': mobileOpen }">
|
||||
<div class="sidebar-header">
|
||||
<div class="sidebar-header" @click="handleHeaderClick" :title="collapsed ? 'Expand sidebar' : 'Collapse sidebar'">
|
||||
<LayoutDashboard :size="24" class="sidebar-logo-icon" />
|
||||
<span v-show="!collapsed" class="sidebar-title">Space Booking</span>
|
||||
<span v-show="showLabels" class="sidebar-title">Space Booking</span>
|
||||
</div>
|
||||
|
||||
<nav class="sidebar-nav">
|
||||
<div class="nav-section">
|
||||
<span v-show="!collapsed" class="nav-section-label">Main</span>
|
||||
<span v-show="showLabels" class="nav-section-label">Main</span>
|
||||
<router-link
|
||||
v-for="item in mainNav"
|
||||
:key="item.to"
|
||||
@@ -17,12 +17,12 @@
|
||||
@click="closeMobile"
|
||||
>
|
||||
<component :is="item.icon" :size="20" class="nav-icon" />
|
||||
<span v-show="!collapsed" class="nav-label">{{ item.label }}</span>
|
||||
<span v-show="showLabels" class="nav-label">{{ item.label }}</span>
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
<div v-if="authStore.isAdmin" class="nav-section">
|
||||
<span v-show="!collapsed" class="nav-section-label">Admin</span>
|
||||
<span v-show="showLabels" class="nav-section-label">Admin</span>
|
||||
<router-link
|
||||
v-for="item in adminNav"
|
||||
:key="item.to"
|
||||
@@ -32,13 +32,13 @@
|
||||
@click="closeMobile"
|
||||
>
|
||||
<component :is="item.icon" :size="20" class="nav-icon" />
|
||||
<span v-show="!collapsed" class="nav-label">{{ item.label }}</span>
|
||||
<span v-show="showLabels" class="nav-label">{{ item.label }}</span>
|
||||
</router-link>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="sidebar-footer">
|
||||
<div v-show="!collapsed" class="user-info">
|
||||
<div v-show="showLabels" class="user-info">
|
||||
<div class="user-avatar">
|
||||
{{ authStore.user?.email?.charAt(0).toUpperCase() }}
|
||||
</div>
|
||||
@@ -79,7 +79,6 @@ import {
|
||||
User,
|
||||
Settings2,
|
||||
Users,
|
||||
ClipboardCheck,
|
||||
Sliders,
|
||||
BarChart3,
|
||||
ScrollText,
|
||||
@@ -96,6 +95,9 @@ const router = useRouter()
|
||||
const { collapsed, mobileOpen, toggle, closeMobile } = useSidebar()
|
||||
const { theme, resolvedTheme, toggleTheme } = useTheme()
|
||||
|
||||
// On mobile, always show labels when sidebar is open (even if collapsed on desktop)
|
||||
const showLabels = computed(() => !collapsed.value || mobileOpen.value)
|
||||
|
||||
const themeTitle = computed(() => {
|
||||
if (theme.value === 'light') return 'Switch to dark mode'
|
||||
if (theme.value === 'dark') return 'Switch to auto mode'
|
||||
@@ -105,14 +107,13 @@ const themeTitle = computed(() => {
|
||||
const mainNav = [
|
||||
{ to: '/dashboard', icon: LayoutDashboard, label: 'Dashboard' },
|
||||
{ to: '/spaces', icon: Building2, label: 'Spaces' },
|
||||
{ to: '/my-bookings', icon: CalendarDays, label: 'My Bookings' },
|
||||
{ to: '/history', icon: CalendarDays, label: 'History' },
|
||||
{ to: '/profile', icon: User, label: 'Profile' },
|
||||
]
|
||||
|
||||
const adminNav = [
|
||||
{ to: '/admin', icon: Settings2, label: 'Spaces Admin' },
|
||||
{ to: '/users', icon: Users, label: 'Users' },
|
||||
{ to: '/admin/pending', icon: ClipboardCheck, label: 'Pending' },
|
||||
{ to: '/admin/settings', icon: Sliders, label: 'Settings' },
|
||||
{ to: '/admin/reports', icon: BarChart3, label: 'Reports' },
|
||||
{ to: '/admin/audit-log', icon: ScrollText, label: 'Audit Log' },
|
||||
@@ -123,6 +124,13 @@ const isActive = (path: string) => {
|
||||
return route.path.startsWith(path)
|
||||
}
|
||||
|
||||
const handleHeaderClick = () => {
|
||||
// Only toggle on desktop (≥768px)
|
||||
if (window.innerWidth >= 768) {
|
||||
toggle()
|
||||
}
|
||||
}
|
||||
|
||||
const handleLogout = () => {
|
||||
authStore.logout()
|
||||
router.push('/login')
|
||||
@@ -166,6 +174,12 @@ const handleLogout = () => {
|
||||
padding: 1.25rem 1.25rem 1rem;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
||||
min-height: 60px;
|
||||
cursor: pointer;
|
||||
transition: background var(--transition-fast);
|
||||
}
|
||||
|
||||
.sidebar-header:hover {
|
||||
background: var(--sidebar-hover-bg);
|
||||
}
|
||||
|
||||
.sidebar-logo-icon {
|
||||
@@ -323,6 +337,14 @@ const handleLogout = () => {
|
||||
width: var(--sidebar-width);
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.sidebar-header:hover {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.desktop-only {
|
||||
display: none;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user