fix: implement timezone-aware datetime display and editing

All datetime values are stored in UTC but were displaying raw UTC times
to users, causing confusion (e.g., 10:00 Bucharest showing as 08:00).
This implements proper timezone conversion throughout the app using each
user's profile timezone setting.

Changes:
- Frontend: Replace local formatters with timezone-aware utilities
- Backend: Add timezone conversion to PUT /bookings endpoint
- FullCalendar: Configure to display events in user timezone
- Fix edit modal to preserve times when editing bookings

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-02-11 13:43:31 +00:00
parent df4031d99c
commit b93b8d2e71
10 changed files with 899 additions and 87 deletions

View File

@@ -319,6 +319,7 @@
import { ref, computed, onMounted, watch } from 'vue'
import { bookingsApi, bookingTemplatesApi, spacesApi, attachmentsApi, handleApiError } from '@/services/api'
import { useAuthStore } from '@/stores/auth'
import { formatDate as formatDateUtil } from '@/utils/datetime'
import type {
Space,
BookingCreate,
@@ -328,6 +329,7 @@ import type {
} from '@/types'
const authStore = useAuthStore()
const userTimezone = computed(() => authStore.user?.timezone || 'UTC')
interface Props {
spaceId?: number
@@ -396,8 +398,6 @@ const weekDays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
// Computed values
const activeSpaces = computed(() => spaces.value.filter((s) => s.is_active))
const userTimezone = computed(() => authStore.user?.timezone || 'UTC')
const selectedSpaceName = computed(() => {
if (!props.spaceId) return ''
const space = spaces.value.find((s) => s.id === props.spaceId)
@@ -454,7 +454,7 @@ const firstOccurrences = computed(() => {
while (current <= end && dates.length < 5) {
const dayIndex = current.getDay() === 0 ? 6 : current.getDay() - 1
if (selectedDays.value.includes(dayIndex)) {
dates.push(current.toLocaleDateString('en-GB'))
dates.push(formatDateUtil(current.toISOString(), userTimezone.value))
}
current.setDate(current.getDate() + 1)
}
@@ -853,7 +853,7 @@ const showRecurringResult = (result: RecurringBookingResult) => {
if (result.total_skipped > 0) {
message += `\n\nSkipped ${result.total_skipped} dates due to conflicts:`
result.skipped_dates.slice(0, 5).forEach((skip) => {
const formattedDate = new Date(skip.date).toLocaleDateString('en-GB')
const formattedDate = formatDateUtil(skip.date, userTimezone.value)
message += `\n- ${formattedDate}: ${skip.reason}`
})
if (result.total_skipped > 5) {