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:
Claude Agent
2026-02-12 15:34:47 +00:00
parent a4d3f862d2
commit d245c72757
36 changed files with 5275 additions and 1569 deletions

View File

@@ -26,6 +26,26 @@
:style="{ width: getProgress(booking.start_datetime, booking.end_datetime) + '%' }"
/>
</div>
<div class="active-actions">
<router-link :to="`/history`" class="action-btn action-btn-view" title="View bookings">
<Eye :size="16" />
</router-link>
<button
v-if="booking.status === 'pending'"
class="action-btn action-btn-edit"
title="Edit booking"
@click="$emit('refresh')"
>
<Pencil :size="16" />
</button>
<button
class="action-btn action-btn-cancel"
title="Cancel booking"
@click="$emit('cancel', booking)"
>
<X :size="16" />
</button>
</div>
</div>
</div>
</div>
@@ -33,7 +53,7 @@
<script setup lang="ts">
import { computed, ref, onMounted, onUnmounted } from 'vue'
import { Zap } from 'lucide-vue-next'
import { Zap, Eye, Pencil, X } from 'lucide-vue-next'
import { isBookingActive, getBookingProgress, formatRemainingTime } from '@/utils/datetime'
import { formatTime as formatTimeUtil } from '@/utils/datetime'
import { useAuthStore } from '@/stores/auth'
@@ -43,6 +63,11 @@ const props = defineProps<{
bookings: Booking[]
}>()
defineEmits<{
cancel: [booking: Booking]
refresh: []
}>()
const authStore = useAuthStore()
const userTimezone = computed(() => authStore.user?.timezone || 'UTC')
@@ -175,6 +200,50 @@ onUnmounted(() => {
transition: width 1s ease;
}
.active-actions {
display: flex;
gap: 8px;
margin-top: 10px;
}
.action-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border-radius: var(--radius-sm);
border: 1px solid var(--color-border);
background: var(--color-surface);
color: var(--color-text-secondary);
cursor: pointer;
transition: all var(--transition-fast);
text-decoration: none;
}
.action-btn:hover {
transform: translateY(-1px);
box-shadow: var(--shadow-sm);
}
.action-btn-view:hover {
color: var(--color-info);
border-color: var(--color-info);
background: color-mix(in srgb, var(--color-info) 10%, transparent);
}
.action-btn-edit:hover {
color: var(--color-warning);
border-color: var(--color-warning);
background: color-mix(in srgb, var(--color-warning) 10%, transparent);
}
.action-btn-cancel:hover {
color: var(--color-danger);
border-color: var(--color-danger);
background: color-mix(in srgb, var(--color-danger) 10%, transparent);
}
/* Mobile responsive */
@media (max-width: 768px) {
.active-card-top {