-
-
+
+
+
+
+
+
+
+
{{ stats.total }}
+
Total Bookings
+
-
-
{{ stats.total }}
-
Total Bookings
-
-
-
-
-
+
+
+
+
+
+
{{ stats.pending }}
+
Pending
+
-
-
{{ stats.pending }}
-
Pending
-
-
-
-
-
+
+
+
+
+
+
{{ stats.approved }}
+
Approved
+
-
-
{{ stats.approved }}
-
Approved
-
-
-
-
-
-
-
{{ adminStats.pendingRequests }}
-
Pending Requests
+
+
+
+
+
+
+
{{ adminStats.pendingRequests }}
+
Pending Requests
+
-
+
Quick Actions
-
+
Book a Space
-
+
My Bookings
-
+
Manage Bookings
-
+
Manage Spaces
@@ -138,21 +84,13 @@
-
-
+
-
+
No upcoming bookings
@@ -163,14 +101,7 @@
class="booking-item"
>
{{ booking.title }}
@@ -184,24 +115,16 @@
-
+
-
-
+
-
+
No available spaces
@@ -213,14 +136,7 @@
class="space-item"
>
{{ space.name }}
@@ -228,25 +144,17 @@
{{ formatType(space.type) }} · Capacity: {{ space.capacity }}
-
+
-
+
-
-
+
No recent activity
@@ -255,14 +163,7 @@
{{ log.action }}
@@ -271,7 +172,7 @@
-
+
@@ -288,6 +189,22 @@ import {
} from '@/services/api'
import { useAuthStore } from '@/stores/auth'
import { formatDateTime as formatDateTimeUtil, ensureUTC } from '@/utils/datetime'
+import CollapsibleSection from '@/components/CollapsibleSection.vue'
+import {
+ Calendar,
+ CalendarDays,
+ Clock,
+ CheckCircle,
+ Users,
+ Search,
+ ClipboardList,
+ ClipboardCheck,
+ Building2,
+ ChevronRight,
+ FileText,
+ ScrollText,
+ BarChart3
+} from 'lucide-vue-next'
import type { Space, Booking, AuditLog, User } from '@/types'
const authStore = useAuthStore()
@@ -402,15 +319,10 @@ onMounted(() => {
diff --git a/frontend/src/views/MyBookings.vue b/frontend/src/views/MyBookings.vue
index 6f47dff..58e4b2c 100644
--- a/frontend/src/views/MyBookings.vue
+++ b/frontend/src/views/MyBookings.vue
@@ -297,11 +297,9 @@ const formatStatus = (status: string): string => {
const openEditModal = (booking: Booking) => {
editingBooking.value = booking
- // Extract date and time from ISO datetime
const startLocal = isoToLocalDateTime(booking.start_datetime, userTimezone.value)
const endLocal = isoToLocalDateTime(booking.end_datetime, userTimezone.value)
- // Split YYYY-MM-DDTHH:mm into date and time parts
const [startDate, startTime] = startLocal.split('T')
const [endDate, endTime] = endLocal.split('T')
@@ -330,7 +328,6 @@ const saveEdit = async () => {
editError.value = ''
try {
- // Combine date and time, then convert to ISO
const startDateTime = `${editForm.value.start_date}T${editForm.value.start_time}`
const endDateTime = `${editForm.value.end_date}T${editForm.value.end_time}`
@@ -352,7 +349,6 @@ const saveEdit = async () => {
}
const canCancel = (booking: Booking): boolean => {
- // Can only cancel pending or approved bookings
return booking.status === 'pending' || booking.status === 'approved'
}
@@ -365,9 +361,6 @@ const handleCancel = async (booking: Booking) => {
error.value = ''
try {
- // TODO: Implement cancel endpoint when available
- // await bookingsApi.cancel(booking.id)
- // For now, just show a message
alert('Cancel functionality will be implemented in a future update.')
await loadBookings()
} catch (err) {
@@ -383,17 +376,18 @@ onMounted(() => {
diff --git a/frontend/src/views/Register.vue b/frontend/src/views/Register.vue
index f5ba373..efd724f 100644
--- a/frontend/src/views/Register.vue
+++ b/frontend/src/views/Register.vue
@@ -145,12 +145,12 @@ const handleRegister = async () => {
h2 {
text-align: center;
margin-bottom: 0.5rem;
- color: #2c3e50;
+ color: var(--color-text-primary);
}
.subtitle {
text-align: center;
- color: #7f8c8d;
+ color: var(--color-text-secondary);
margin-bottom: 2rem;
}
@@ -158,7 +158,7 @@ h2 {
display: block;
margin-top: 0.25rem;
font-size: 0.85rem;
- color: #7f8c8d;
+ color: var(--color-text-secondary);
}
.btn-block {
@@ -169,11 +169,11 @@ h2 {
.login-link {
text-align: center;
margin-top: 1.5rem;
- color: #7f8c8d;
+ color: var(--color-text-secondary);
}
.login-link a {
- color: #3498db;
+ color: var(--color-accent);
text-decoration: none;
}
@@ -184,18 +184,18 @@ h2 {
.error {
margin-top: 1rem;
padding: 0.75rem;
- background: #fee;
- border-left: 3px solid #e74c3c;
- border-radius: 4px;
- color: #c0392b;
+ background: color-mix(in srgb, var(--color-danger) 10%, transparent);
+ border-left: 3px solid var(--color-danger);
+ border-radius: var(--radius-sm);
+ color: var(--color-danger);
}
.success {
margin-top: 1rem;
padding: 0.75rem;
- background: #d4edda;
- border-left: 3px solid #28a745;
- border-radius: 4px;
- color: #155724;
+ background: color-mix(in srgb, var(--color-success) 10%, transparent);
+ border-left: 3px solid var(--color-success);
+ border-radius: var(--radius-sm);
+ color: var(--color-success);
}
diff --git a/frontend/src/views/Settings.vue b/frontend/src/views/Settings.vue
index c714d6d..b0047cc 100644
--- a/frontend/src/views/Settings.vue
+++ b/frontend/src/views/Settings.vue
@@ -3,9 +3,7 @@
Global Booking Settings
-
-
Booking Rules Configuration
-
+
Loading settings...
+
-
+
diff --git a/frontend/src/views/Users.vue b/frontend/src/views/Users.vue
index 34cf233..d6a675b 100644
--- a/frontend/src/views/Users.vue
+++ b/frontend/src/views/Users.vue
@@ -3,13 +3,13 @@
-
-
Filters
+
@@ -31,68 +31,69 @@
/>
-
+
-
-
All Users
+
Loading users...
No users found. {{ filterRole || filterOrganization ? 'Try different filters.' : 'Create one above!' }}
-
-
-
- | Email |
- Full Name |
- Role |
- Organization |
- Status |
- Actions |
-
-
-
-
- | {{ user.email }} |
- {{ user.full_name }} |
-
-
- {{ user.role }}
-
- |
- {{ user.organization || '-' }} |
-
-
- {{ user.is_active ? 'Active' : 'Inactive' }}
-
- |
-
-
-
-
- |
-
-
-
-
+
+
+
+
+ | Email |
+ Full Name |
+ Role |
+ Organization |
+ Status |
+ Actions |
+
+
+
+
+ | {{ user.email }} |
+ {{ user.full_name }} |
+
+
+ {{ user.role }}
+
+ |
+ {{ user.organization || '-' }} |
+
+
+ {{ user.is_active ? 'Active' : 'Inactive' }}
+
+ |
+
+
+
+
+ |
+
+
+
+
+
@@ -200,6 +201,8 @@
diff --git a/frontend/src/views/VerifyEmail.vue b/frontend/src/views/VerifyEmail.vue
index ac6def8..d85487a 100644
--- a/frontend/src/views/VerifyEmail.vue
+++ b/frontend/src/views/VerifyEmail.vue
@@ -129,8 +129,8 @@ const resendVerification = async () => {
.spinner {
width: 50px;
height: 50px;
- border: 4px solid #f3f3f3;
- border-top: 4px solid #3498db;
+ border: 4px solid var(--color-border);
+ border-top: 4px solid var(--color-accent);
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 1.5rem;
@@ -153,7 +153,7 @@ const resendVerification = async () => {
.icon-success {
width: 80px;
height: 80px;
- background: #28a745;
+ background: var(--color-success);
color: white;
font-size: 3rem;
line-height: 80px;
@@ -164,7 +164,7 @@ const resendVerification = async () => {
.icon-error {
width: 80px;
height: 80px;
- background: #e74c3c;
+ background: var(--color-danger);
color: white;
font-size: 3rem;
line-height: 80px;
@@ -173,17 +173,17 @@ const resendVerification = async () => {
}
h2 {
- color: #2c3e50;
+ color: var(--color-text-primary);
margin-bottom: 1rem;
}
p {
- color: #7f8c8d;
+ color: var(--color-text-secondary);
margin-bottom: 1rem;
}
.error-message {
- color: #c0392b;
+ color: var(--color-danger);
font-weight: 500;
}
@@ -192,7 +192,7 @@ p {
}
.login-link a {
- color: #3498db;
+ color: var(--color-accent);
text-decoration: none;
}
@@ -203,13 +203,13 @@ p {
.resend-section {
margin-top: 2rem;
padding-top: 2rem;
- border-top: 1px solid #e1e8ed;
+ border-top: 1px solid var(--color-border);
}
.resend-section > p {
margin-bottom: 1rem;
font-weight: 500;
- color: #2c3e50;
+ color: var(--color-text-primary);
}
.resend-form {
@@ -221,22 +221,25 @@ p {
.resend-input {
flex: 1;
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);
}
.resend-input:focus {
outline: none;
- border-color: #3498db;
+ border-color: var(--color-accent);
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 15%, transparent);
}
.resend-message {
padding: 0.75rem;
- background: #d4edda;
- border-left: 3px solid #28a745;
- border-radius: 4px;
- color: #155724;
+ background: color-mix(in srgb, var(--color-success) 10%, transparent);
+ border-left: 3px solid var(--color-success);
+ border-radius: var(--radius-sm);
+ color: var(--color-success);
text-align: left;
}