fix: implement recurrance configuration properly
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -30,7 +30,10 @@ const timeZoneValue = computed({
|
||||
|
||||
// Format helpers
|
||||
const formatDate = (date: Date): string => {
|
||||
return date.toISOString().split('T')[0]
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
return `${year}-${month}-${day}`
|
||||
}
|
||||
|
||||
const formatTime = (date: Date): string => {
|
||||
@@ -45,16 +48,57 @@ const formatDateTime = (isoString: string | null | undefined): string => {
|
||||
|
||||
const parseDateTime = (date: string, time: string): Date => {
|
||||
if (timelessValue.value) {
|
||||
return new Date(date)
|
||||
return new Date(`${date}T00:00:00`)
|
||||
}
|
||||
return new Date(`${date}T${time}`)
|
||||
}
|
||||
|
||||
const formatDisplayDate = (value: string): string => {
|
||||
if (!value) return ''
|
||||
return new Date(`${value}T00:00:00`).toLocaleDateString(undefined, {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
})
|
||||
}
|
||||
|
||||
const formatDisplayTime = (value: string): string => {
|
||||
if (!value) return ''
|
||||
const [hour, minute] = value.split(':').map(Number)
|
||||
const date = new Date()
|
||||
date.setHours(hour, minute, 0, 0)
|
||||
return date.toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' })
|
||||
}
|
||||
|
||||
const pickerDate = (value: string): Date | null => value ? new Date(`${value}T00:00:00`) : null
|
||||
|
||||
const normalizePickerDate = (value: unknown): string => {
|
||||
const selected = Array.isArray(value) ? value[0] : value
|
||||
const date = selected instanceof Date ? selected : new Date(String(selected))
|
||||
return Number.isNaN(date.getTime()) ? '' : formatDate(date)
|
||||
}
|
||||
|
||||
// Date/time fields
|
||||
const startDate = ref('')
|
||||
const startTime = ref('')
|
||||
const endDate = ref('')
|
||||
const endTime = ref('')
|
||||
const startDateMenu = ref(false)
|
||||
const startTimeMenu = ref(false)
|
||||
const endDateMenu = ref(false)
|
||||
const endTimeMenu = ref(false)
|
||||
|
||||
const selectStartDate = (value: unknown) => {
|
||||
const date = normalizePickerDate(value)
|
||||
if (date) startDate.value = date
|
||||
startDateMenu.value = false
|
||||
}
|
||||
|
||||
const selectEndDate = (value: unknown) => {
|
||||
const date = normalizePickerDate(value)
|
||||
if (date) endDate.value = date
|
||||
endDateMenu.value = false
|
||||
}
|
||||
|
||||
// Initialize date/time from props
|
||||
watch(() => props.startsOn, (newValue) => {
|
||||
@@ -81,17 +125,23 @@ watch(() => props.endsOn, (newValue) => {
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
// Update parent when date/time changes
|
||||
watch([startDate, startTime], () => {
|
||||
if (props.mode === 'edit') {
|
||||
emit('update:startsOn', parseDateTime(startDate.value, startTime.value).toISOString())
|
||||
}
|
||||
})
|
||||
const ensureValidRange = (): boolean => {
|
||||
if (!startDate.value || !endDate.value || !startTime.value || !endTime.value) return false
|
||||
const start = parseDateTime(startDate.value, startTime.value)
|
||||
const end = parseDateTime(endDate.value, endTime.value)
|
||||
if (end.getTime() >= start.getTime()) return false
|
||||
|
||||
watch([endDate, endTime], () => {
|
||||
if (props.mode === 'edit') {
|
||||
emit('update:endsOn', parseDateTime(endDate.value, endTime.value).toISOString())
|
||||
}
|
||||
const correctedEnd = new Date(start)
|
||||
if (!timelessValue.value) correctedEnd.setHours(correctedEnd.getHours() + 1)
|
||||
endDate.value = formatDate(correctedEnd)
|
||||
endTime.value = formatTime(correctedEnd)
|
||||
return true
|
||||
}
|
||||
|
||||
watch([startDate, startTime, endDate, endTime, timelessValue], () => {
|
||||
if (props.mode !== 'edit' || ensureValidRange()) return
|
||||
emit('update:startsOn', parseDateTime(startDate.value, startTime.value).toISOString())
|
||||
emit('update:endsOn', parseDateTime(endDate.value, endTime.value).toISOString())
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -127,22 +177,46 @@ watch([endDate, endTime], () => {
|
||||
<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-menu v-model="startDateMenu" :close-on-content-click="false" location="bottom start">
|
||||
<template #activator="{ props: activatorProps }">
|
||||
<v-text-field
|
||||
v-bind="activatorProps"
|
||||
:model-value="formatDisplayDate(startDate)"
|
||||
label="Start date"
|
||||
prepend-inner-icon="mdi-calendar"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
readonly
|
||||
/>
|
||||
</template>
|
||||
<v-date-picker
|
||||
:model-value="pickerDate(startDate)"
|
||||
color="primary"
|
||||
show-adjacent-months
|
||||
@update:model-value="selectStartDate"
|
||||
/>
|
||||
</v-menu>
|
||||
</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-menu v-model="startTimeMenu" :close-on-content-click="false" location="bottom start">
|
||||
<template #activator="{ props: activatorProps }">
|
||||
<v-text-field
|
||||
v-bind="activatorProps"
|
||||
:model-value="formatDisplayTime(startTime)"
|
||||
label="Start time"
|
||||
prepend-inner-icon="mdi-clock-outline"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
readonly
|
||||
/>
|
||||
</template>
|
||||
<v-card>
|
||||
<v-time-picker v-model="startTime" color="primary" format="ampm" />
|
||||
<v-card-actions class="justify-end">
|
||||
<v-btn variant="text" @click="startTimeMenu = false">Done</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-menu>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
@@ -151,22 +225,47 @@ watch([endDate, endTime], () => {
|
||||
<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-menu v-model="endDateMenu" :close-on-content-click="false" location="bottom start">
|
||||
<template #activator="{ props: activatorProps }">
|
||||
<v-text-field
|
||||
v-bind="activatorProps"
|
||||
:model-value="formatDisplayDate(endDate)"
|
||||
label="End date"
|
||||
prepend-inner-icon="mdi-calendar"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
readonly
|
||||
/>
|
||||
</template>
|
||||
<v-date-picker
|
||||
:model-value="pickerDate(endDate)"
|
||||
:min="pickerDate(startDate)"
|
||||
color="primary"
|
||||
show-adjacent-months
|
||||
@update:model-value="selectEndDate"
|
||||
/>
|
||||
</v-menu>
|
||||
</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-menu v-model="endTimeMenu" :close-on-content-click="false" location="bottom start">
|
||||
<template #activator="{ props: activatorProps }">
|
||||
<v-text-field
|
||||
v-bind="activatorProps"
|
||||
:model-value="formatDisplayTime(endTime)"
|
||||
label="End time"
|
||||
prepend-inner-icon="mdi-clock-outline"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
readonly
|
||||
/>
|
||||
</template>
|
||||
<v-card>
|
||||
<v-time-picker v-model="endTime" color="primary" format="ampm" />
|
||||
<v-card-actions class="justify-end">
|
||||
<v-btn variant="text" @click="endTimeMenu = false">Done</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-menu>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user