191 lines
4.9 KiB
Vue
191 lines
4.9 KiB
Vue
<script setup lang="ts">
|
|
import { ref, computed, watch } from 'vue'
|
|
|
|
interface Props {
|
|
mode: 'edit' | 'view'
|
|
startsOn?: string | null
|
|
endsOn?: string | null
|
|
timeless?: boolean | null
|
|
timeZone?: string | null
|
|
}
|
|
|
|
const props = defineProps<Props>()
|
|
|
|
const emit = defineEmits<{
|
|
'update:startsOn': [date: string | null]
|
|
'update:endsOn': [date: string | null]
|
|
'update:timeless': [timeless: boolean | null]
|
|
'update:timeZone': [timeZone: string | null]
|
|
}>()
|
|
|
|
const timelessValue = computed({
|
|
get: () => props.timeless ?? false,
|
|
set: (value) => emit('update:timeless', value)
|
|
})
|
|
|
|
const timeZoneValue = computed({
|
|
get: () => props.timeZone ?? '',
|
|
set: (value) => emit('update:timeZone', value || null)
|
|
})
|
|
|
|
// Format helpers
|
|
const formatDate = (date: Date): string => {
|
|
return date.toISOString().split('T')[0]
|
|
}
|
|
|
|
const formatTime = (date: Date): string => {
|
|
return date.toTimeString().slice(0, 5)
|
|
}
|
|
|
|
const formatDateTime = (isoString: string | null | undefined): string => {
|
|
if (!isoString) return 'Not set'
|
|
const date = new Date(isoString)
|
|
return date.toLocaleString()
|
|
}
|
|
|
|
const parseDateTime = (date: string, time: string): Date => {
|
|
if (timelessValue.value) {
|
|
return new Date(date)
|
|
}
|
|
return new Date(`${date}T${time}`)
|
|
}
|
|
|
|
// Date/time fields
|
|
const startDate = ref('')
|
|
const startTime = ref('')
|
|
const endDate = ref('')
|
|
const endTime = ref('')
|
|
|
|
// Initialize date/time from props
|
|
watch(() => props.startsOn, (newValue) => {
|
|
if (newValue) {
|
|
const start = new Date(newValue)
|
|
startDate.value = formatDate(start)
|
|
startTime.value = formatTime(start)
|
|
} else {
|
|
const now = new Date()
|
|
startDate.value = formatDate(now)
|
|
startTime.value = formatTime(now)
|
|
}
|
|
}, { immediate: true })
|
|
|
|
watch(() => props.endsOn, (newValue) => {
|
|
if (newValue) {
|
|
const end = new Date(newValue)
|
|
endDate.value = formatDate(end)
|
|
endTime.value = formatTime(end)
|
|
} else {
|
|
const later = new Date(Date.now() + 60 * 60 * 1000)
|
|
endDate.value = formatDate(later)
|
|
endTime.value = formatTime(later)
|
|
}
|
|
}, { immediate: true })
|
|
|
|
// Update parent when date/time changes
|
|
watch([startDate, startTime], () => {
|
|
if (props.mode === 'edit') {
|
|
emit('update:startsOn', parseDateTime(startDate.value, startTime.value).toISOString())
|
|
}
|
|
})
|
|
|
|
watch([endDate, endTime], () => {
|
|
if (props.mode === 'edit') {
|
|
emit('update:endsOn', parseDateTime(endDate.value, endTime.value).toISOString())
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div class="event-editor-section">
|
|
<div class="text-subtitle-1 mb-2">Date & Time</div>
|
|
|
|
<!-- Read-only view -->
|
|
<div v-if="mode === 'view'">
|
|
<div class="mb-2 pa-2">
|
|
<div class="mb-1"><strong>Starts:</strong> {{ formatDateTime(startsOn) }}</div>
|
|
<div class="mb-1"><strong>Ends:</strong> {{ formatDateTime(endsOn) }}</div>
|
|
<div v-if="timelessValue" class="mb-1">
|
|
<v-chip size="small" color="info" variant="tonal">
|
|
<v-icon start size="small">mdi-calendar-blank</v-icon>
|
|
All Day Event
|
|
</v-chip>
|
|
</div>
|
|
<div v-if="timeZoneValue" class="text-caption text-grey">Time Zone: {{ timeZoneValue }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Edit view -->
|
|
<div v-else>
|
|
<v-checkbox
|
|
v-model="timelessValue"
|
|
label="All day event"
|
|
density="compact"
|
|
class="mb-2"
|
|
/>
|
|
|
|
<div class="mb-3">
|
|
<div class="text-subtitle-2 mb-2">Start</div>
|
|
<v-row dense>
|
|
<v-col cols="12" md="6">
|
|
<v-text-field
|
|
v-model="startDate"
|
|
label="Start Date"
|
|
type="date"
|
|
variant="outlined"
|
|
density="compact"
|
|
/>
|
|
</v-col>
|
|
<v-col v-if="!timelessValue" cols="12" md="6">
|
|
<v-text-field
|
|
v-model="startTime"
|
|
label="Start Time"
|
|
type="time"
|
|
variant="outlined"
|
|
density="compact"
|
|
/>
|
|
</v-col>
|
|
</v-row>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<div class="text-subtitle-2 mb-2">End</div>
|
|
<v-row dense>
|
|
<v-col cols="12" md="6">
|
|
<v-text-field
|
|
v-model="endDate"
|
|
label="End Date"
|
|
type="date"
|
|
variant="outlined"
|
|
density="compact"
|
|
/>
|
|
</v-col>
|
|
<v-col v-if="!timelessValue" cols="12" md="6">
|
|
<v-text-field
|
|
v-model="endTime"
|
|
label="End Time"
|
|
type="time"
|
|
variant="outlined"
|
|
density="compact"
|
|
/>
|
|
</v-col>
|
|
</v-row>
|
|
</div>
|
|
|
|
<v-text-field
|
|
v-model="timeZoneValue"
|
|
label="Time Zone"
|
|
variant="outlined"
|
|
density="compact"
|
|
hint="e.g., America/New_York"
|
|
persistent-hint
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.event-editor-section {
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
</style>
|