feat: complete UI redesign with dark mode, sidebar navigation, and modern design system

Implemented comprehensive UI overhaul with three-layer architecture:

Layer 1 - Theme System:
- CSS variables for light/dark themes (theme.css)
- Theme composable with light/dark/auto mode (useTheme.ts)
- Sidebar state management composable (useSidebar.ts)
- Refactored main.css to use CSS variables throughout

Layer 2 - Core Components:
- AppSidebar with collapsible navigation (desktop) and overlay (mobile)
- CollapsibleSection reusable component for expandable cards
- Restructured App.vue with new sidebar layout
- Integrated Lucide icons library (lucide-vue-next)

Layer 3 - Views & Components:
- Updated all 14 views with CSS variables and responsive design
- Replaced inline SVG with Lucide icon components
- Added collapsible sections to Dashboard, Admin pages, UserProfile
- Updated 3 shared components (BookingForm, SpaceCalendar, AttachmentsList)

Features:
- Dark/light/auto theme with persistent preference
- Collapsible sidebar (icons-only on desktop, overlay on mobile)
- Consistent color palette using CSS variables
- Full responsive design across all pages
- Modern minimalist aesthetic with Indigo accent color

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-02-11 21:27:05 +00:00
parent 9c2846cf00
commit 0bf3e6a7e2
28 changed files with 1960 additions and 1641 deletions

View File

@@ -3,9 +3,7 @@
<h2>Global Booking Settings</h2>
<!-- Settings Form -->
<div class="card">
<h3>Booking Rules Configuration</h3>
<CollapsibleSection title="Booking Rules Configuration" :icon="Sliders">
<div v-if="loadingSettings" class="loading">Loading settings...</div>
<form v-else @submit.prevent="handleSubmit" class="settings-form">
@@ -108,25 +106,26 @@
<div v-if="error" class="error">{{ error }}</div>
<div v-if="success" class="success">{{ success }}</div>
</div>
</CollapsibleSection>
<!-- Info Card -->
<div class="card info-card">
<h4>About These Settings</h4>
<ul>
<CollapsibleSection title="About These Settings" :icon="Info">
<ul class="info-list">
<li><strong>Duration:</strong> Controls minimum and maximum booking length</li>
<li><strong>Working Hours:</strong> Bookings outside these hours will be rejected</li>
<li><strong>Max Bookings:</strong> Limits how many bookings a user can make per day</li>
<li><strong>Cancel Policy:</strong> Users cannot cancel bookings too close to start time</li>
</ul>
<p class="note">These rules apply to all new booking requests.</p>
</div>
</CollapsibleSection>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { settingsApi, handleApiError } from '@/services/api'
import CollapsibleSection from '@/components/CollapsibleSection.vue'
import { Sliders, Info } from 'lucide-vue-next'
import type { Settings } from '@/types'
const loadingSettings = ref(true)
@@ -163,7 +162,6 @@ const loadSettings = async () => {
}
const validateForm = (): boolean => {
// Validate all fields are positive
if (
formData.value.min_duration_minutes <= 0 ||
formData.value.max_duration_minutes <= 0 ||
@@ -176,19 +174,16 @@ const validateForm = (): boolean => {
return false
}
// Validate min < max duration
if (formData.value.min_duration_minutes >= formData.value.max_duration_minutes) {
error.value = 'Minimum duration must be less than maximum duration'
return false
}
// Validate working hours start < end
if (formData.value.working_hours_start >= formData.value.working_hours_end) {
error.value = 'Working hours start must be less than working hours end'
return false
}
// Validate working hours are within 0-23 for start and 1-24 for end
if (formData.value.working_hours_start < 0 || formData.value.working_hours_start > 23) {
error.value = 'Working hours start must be between 0 and 23'
return false
@@ -206,7 +201,6 @@ const handleSubmit = async () => {
error.value = ''
success.value = ''
// Client-side validation
if (!validateForm()) {
return
}
@@ -239,21 +233,7 @@ onMounted(() => {
h2 {
margin-bottom: 1.5rem;
color: #333;
}
.card {
background: white;
border-radius: 8px;
padding: 1.5rem;
margin-bottom: 1.5rem;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
h3, h4 {
margin-top: 0;
margin-bottom: 1.5rem;
color: #444;
color: var(--color-text-primary);
}
.settings-form {
@@ -276,18 +256,27 @@ h3, h4 {
.form-group label {
font-weight: 500;
color: #555;
color: var(--color-text-primary);
font-size: 14px;
}
.form-group input {
padding: 0.5rem;
border: 1px solid #ddd;
border-radius: 4px;
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
font-size: 1rem;
background: var(--color-surface);
color: var(--color-text-primary);
}
.form-group input:focus {
outline: none;
border-color: var(--color-accent);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 15%, transparent);
}
.form-group small {
color: #666;
color: var(--color-text-secondary);
font-size: 0.875rem;
}
@@ -300,19 +289,19 @@ h3, h4 {
.btn {
padding: 0.6rem 1.2rem;
border: none;
border-radius: 4px;
border-radius: var(--radius-sm);
font-size: 1rem;
cursor: pointer;
transition: background-color 0.2s;
transition: all var(--transition-fast);
}
.btn-primary {
background-color: #4caf50;
background: var(--color-accent);
color: white;
}
.btn-primary:hover:not(:disabled) {
background-color: #45a049;
background: var(--color-accent-hover);
}
.btn:disabled {
@@ -323,46 +312,48 @@ h3, h4 {
.loading {
text-align: center;
padding: 2rem;
color: #666;
color: var(--color-text-secondary);
}
.error {
padding: 0.75rem;
background-color: #fee;
border: 1px solid #fcc;
border-radius: 4px;
color: #c33;
background: color-mix(in srgb, var(--color-danger) 10%, transparent);
border-radius: var(--radius-sm);
color: var(--color-danger);
margin-top: 1rem;
}
.success {
padding: 0.75rem;
background-color: #efe;
border: 1px solid #cfc;
border-radius: 4px;
color: #3c3;
background: color-mix(in srgb, var(--color-success) 10%, transparent);
border-radius: var(--radius-sm);
color: var(--color-success);
margin-top: 1rem;
}
.info-card {
background-color: #f8f9fa;
}
.info-card ul {
.info-list {
margin: 0;
padding-left: 1.5rem;
}
.info-card li {
.info-list li {
margin-bottom: 0.5rem;
color: #555;
color: var(--color-text-secondary);
}
.info-list strong {
color: var(--color-text-primary);
}
.note {
margin-top: 1rem;
margin-bottom: 0;
font-style: italic;
color: #666;
color: var(--color-text-secondary);
}
.collapsible-section + .collapsible-section {
margin-top: 16px;
}
@media (max-width: 768px) {