fix: synchronize event updates and expose updateEvent method in CalendarEventPanel
This commit is contained in:
parent
4b4b188827
commit
c69a468e35
@ -635,6 +635,7 @@ function close() {
|
|||||||
|
|
||||||
function reset() {
|
function reset() {
|
||||||
Object.assign(_event.value, oldEvent.value)
|
Object.assign(_event.value, oldEvent.value)
|
||||||
|
sync()
|
||||||
}
|
}
|
||||||
|
|
||||||
function showDiscardChangesModal(action) {
|
function showDiscardChangesModal(action) {
|
||||||
@ -745,4 +746,10 @@ const toOptions = computed(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function updateEvent(_e) {
|
||||||
|
Object.assign(_event.value, _e)
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ updateEvent })
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -7,9 +7,7 @@
|
|||||||
<Button
|
<Button
|
||||||
variant="solid"
|
variant="solid"
|
||||||
:label="__('Create')"
|
:label="__('Create')"
|
||||||
:disabled="
|
:disabled="isCreateDisabled"
|
||||||
mode == 'edit' || mode == 'new-event' || mode == 'duplicate-event'
|
|
||||||
"
|
|
||||||
@click="newEvent"
|
@click="newEvent"
|
||||||
>
|
>
|
||||||
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
|
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
|
||||||
@ -31,7 +29,7 @@
|
|||||||
}"
|
}"
|
||||||
:events="events.data"
|
:events="events.data"
|
||||||
@create="(event) => createEvent(event)"
|
@create="(event) => createEvent(event)"
|
||||||
@update="(event) => updateEvent(event)"
|
@update="(event) => updateEvent(event, true)"
|
||||||
@delete="(eventID) => deleteEvent(eventID)"
|
@delete="(eventID) => deleteEvent(eventID)"
|
||||||
:onClick="showDetails"
|
:onClick="showDetails"
|
||||||
:onDblClick="editDetails"
|
:onDblClick="editDetails"
|
||||||
@ -100,6 +98,7 @@
|
|||||||
"
|
"
|
||||||
>
|
>
|
||||||
<CalendarEventPanel
|
<CalendarEventPanel
|
||||||
|
ref="eventPanel"
|
||||||
v-if="showEventPanel"
|
v-if="showEventPanel"
|
||||||
v-model="showEventPanel"
|
v-model="showEventPanel"
|
||||||
v-model:event="event"
|
v-model:event="event"
|
||||||
@ -130,7 +129,7 @@ import {
|
|||||||
CalendarActiveEvent as activeEvent,
|
CalendarActiveEvent as activeEvent,
|
||||||
call,
|
call,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref, computed } from 'vue'
|
||||||
|
|
||||||
const { user } = sessionStore()
|
const { user } = sessionStore()
|
||||||
const { $dialog } = globalStore()
|
const { $dialog } = globalStore()
|
||||||
@ -156,82 +155,105 @@ const events = createListResource({
|
|||||||
filters: { status: 'Open', owner: user },
|
filters: { status: 'Open', owner: user },
|
||||||
pageLength: 9999,
|
pageLength: 9999,
|
||||||
auto: true,
|
auto: true,
|
||||||
transform: (data) => {
|
transform: (data) =>
|
||||||
return data.map((event) => {
|
data.map((ev) => ({
|
||||||
let fromDate = dayjs(event.starts_on).format('YYYY-MM-DD')
|
id: ev.name,
|
||||||
let toDate = dayjs(event.ends_on).format('YYYY-MM-DD')
|
title: ev.subject,
|
||||||
let fromTime = dayjs(event.starts_on).format('HH:mm')
|
description: ev.description,
|
||||||
let toTime = dayjs(event.ends_on).format('HH:mm')
|
status: ev.status,
|
||||||
|
fromDate: dayjs(ev.starts_on).format('YYYY-MM-DD'),
|
||||||
return {
|
toDate: dayjs(ev.ends_on).format('YYYY-MM-DD'),
|
||||||
id: event.name,
|
fromTime: dayjs(ev.starts_on).format('HH:mm'),
|
||||||
title: event.subject,
|
toTime: dayjs(ev.ends_on).format('HH:mm'),
|
||||||
description: event.description,
|
isFullDay: ev.all_day,
|
||||||
status: event.status,
|
eventType: ev.event_type,
|
||||||
fromDate,
|
color: ev.color,
|
||||||
toDate,
|
referenceDoctype: ev.reference_doctype,
|
||||||
fromTime,
|
referenceDocname: ev.reference_docname,
|
||||||
toTime,
|
})),
|
||||||
isFullDay: event.all_day,
|
|
||||||
eventType: event.event_type,
|
|
||||||
color: event.color,
|
|
||||||
referenceDoctype: event.reference_doctype,
|
|
||||||
referenceDocname: event.reference_docname,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const eventPanel = ref(null)
|
||||||
|
const showEventPanel = ref(false)
|
||||||
|
const event = ref({})
|
||||||
|
const mode = ref('')
|
||||||
|
|
||||||
|
const isCreateDisabled = computed(() =>
|
||||||
|
['edit', 'new-event', 'duplicate-event'].includes(mode.value),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Temp event helpers
|
||||||
|
const TEMP_EVENT_IDS = new Set(['new-event', 'duplicate-event'])
|
||||||
|
const isTempEvent = (id) => TEMP_EVENT_IDS.has(id)
|
||||||
|
function removeTempEvents() {
|
||||||
|
if (!Array.isArray(events.data)) return
|
||||||
|
events.data = events.data.filter((ev) => !isTempEvent(ev.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
function openEvent(e, nextMode) {
|
||||||
|
const _e = e?.calendarEvent || e
|
||||||
|
if (!_e?.id || isTempEvent(_e.id)) return
|
||||||
|
removeTempEvents()
|
||||||
|
showEventPanel.value = true
|
||||||
|
event.value = { id: _e.id }
|
||||||
|
activeEvent.value = _e.id
|
||||||
|
mode.value = nextMode
|
||||||
|
}
|
||||||
|
|
||||||
function saveEvent(_event) {
|
function saveEvent(_event) {
|
||||||
if (
|
if (!_event?.id || isTempEvent(_event.id)) return createEvent(_event)
|
||||||
!_event.id ||
|
updateEvent(_event)
|
||||||
_event.id === 'new-event' ||
|
}
|
||||||
_event.id === 'duplicate-event'
|
|
||||||
) {
|
function buildEventPayload(_event) {
|
||||||
createEvent(_event)
|
return {
|
||||||
} else {
|
subject: _event.title,
|
||||||
updateEvent(_event)
|
description: _event.description,
|
||||||
|
starts_on: `${_event.fromDate} ${_event.fromTime}`,
|
||||||
|
ends_on: `${_event.toDate} ${_event.toTime}`,
|
||||||
|
all_day: _event.isFullDay || false,
|
||||||
|
event_type: _event.eventType,
|
||||||
|
color: _event.color,
|
||||||
|
reference_doctype: _event.referenceDoctype,
|
||||||
|
reference_docname: _event.referenceDocname,
|
||||||
|
event_participants: _event.event_participants,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createEvent(_event) {
|
function createEvent(_event) {
|
||||||
if (!_event.title) return
|
if (!_event?.title) return
|
||||||
|
events.insert.submit(buildEventPayload(_event), {
|
||||||
events.insert.submit(
|
onSuccess: async (e) => {
|
||||||
{
|
await events.reload()
|
||||||
subject: _event.title,
|
showDetails({ id: e.name })
|
||||||
description: _event.description,
|
|
||||||
starts_on: _event.fromDate + ' ' + _event.fromTime,
|
|
||||||
ends_on: _event.toDate + ' ' + _event.toTime,
|
|
||||||
all_day: _event.isFullDay || false,
|
|
||||||
event_type: _event.eventType,
|
|
||||||
color: _event.color,
|
|
||||||
reference_doctype: _event.referenceDoctype,
|
|
||||||
reference_docname: _event.referenceDocname,
|
|
||||||
event_participants: _event.event_participants,
|
|
||||||
},
|
},
|
||||||
{
|
})
|
||||||
onSuccess: async (e) => {
|
|
||||||
await events.reload()
|
|
||||||
showDetails({ id: e.name })
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateEvent(_event) {
|
async function updateEvent(_event, afterDrag = false) {
|
||||||
if (!_event.id) return
|
if (!_event.id) return
|
||||||
|
|
||||||
if (
|
if (
|
||||||
['duplicate', 'new'].includes(mode.value) &&
|
['duplicate', 'new'].includes(mode.value) &&
|
||||||
!['duplicate-event', 'new-event'].includes(_event.id)
|
!['duplicate-event', 'new-event'].includes(_event.id) &&
|
||||||
|
afterDrag
|
||||||
) {
|
) {
|
||||||
event.value = { id: _event.id }
|
event.value = { id: _event.id }
|
||||||
activeEvent.value = _event.id
|
activeEvent.value = _event.id
|
||||||
mode.value = 'details'
|
mode.value = 'details'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mode.value || mode.value === 'edit' || mode.value === 'details') {
|
if (mode.value == 'edit' && afterDrag) {
|
||||||
|
eventPanel.value.updateEvent({
|
||||||
|
fromDate: _event.fromDate,
|
||||||
|
toDate: _event.toDate,
|
||||||
|
fromTime: _event.fromTime,
|
||||||
|
toTime: _event.toTime,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mode.value || mode.value == 'edit' || mode.value === 'details') {
|
||||||
// Ensure Contacts exist for participants referencing a new/unknown Contact, if not create them
|
// Ensure Contacts exist for participants referencing a new/unknown Contact, if not create them
|
||||||
if (
|
if (
|
||||||
Array.isArray(_event.event_participants) &&
|
Array.isArray(_event.event_participants) &&
|
||||||
@ -243,19 +265,7 @@ async function updateEvent(_event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
events.setValue.submit(
|
events.setValue.submit(
|
||||||
{
|
{ name: _event.id, ...buildEventPayload(_event) },
|
||||||
name: _event.id,
|
|
||||||
subject: _event.title,
|
|
||||||
description: _event.description,
|
|
||||||
starts_on: _event.fromDate + ' ' + _event.fromTime,
|
|
||||||
ends_on: _event.toDate + ' ' + _event.toTime,
|
|
||||||
all_day: _event.isFullDay,
|
|
||||||
event_type: _event.eventType,
|
|
||||||
color: _event.color,
|
|
||||||
reference_doctype: _event.referenceDoctype,
|
|
||||||
reference_docname: _event.referenceDocname,
|
|
||||||
event_participants: _event.event_participants,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
onSuccess: async (e) => {
|
onSuccess: async (e) => {
|
||||||
await events.reload()
|
await events.reload()
|
||||||
@ -305,67 +315,25 @@ onMounted(() => {
|
|||||||
showEventPanel.value = false
|
showEventPanel.value = false
|
||||||
})
|
})
|
||||||
|
|
||||||
const showEventPanel = ref(false)
|
|
||||||
const event = ref({})
|
|
||||||
const mode = ref('')
|
|
||||||
|
|
||||||
function showDetails(e) {
|
function showDetails(e) {
|
||||||
let _e = e?.calendarEvent || e
|
openEvent(e, 'details')
|
||||||
if (_e.id === 'new-event' || _e.id === 'duplicate-event') return
|
|
||||||
|
|
||||||
events.data = events.data.filter(
|
|
||||||
(ev) => ev.id !== 'new-event' && ev.id !== 'duplicate-event',
|
|
||||||
)
|
|
||||||
|
|
||||||
showEventPanel.value = true
|
|
||||||
event.value = { id: _e.id }
|
|
||||||
activeEvent.value = _e.id
|
|
||||||
mode.value = 'details'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function editDetails(e) {
|
function editDetails(e) {
|
||||||
let _e = e?.calendarEvent || e
|
openEvent(e, 'edit')
|
||||||
if (_e.id === 'new-event' || _e.id === 'duplicate-event') return
|
|
||||||
|
|
||||||
events.data = events.data.filter(
|
|
||||||
(ev) => ev.id !== 'new-event' && ev.id !== 'duplicate-event',
|
|
||||||
)
|
|
||||||
|
|
||||||
showEventPanel.value = true
|
|
||||||
event.value = { id: _e.id }
|
|
||||||
activeEvent.value = _e.id
|
|
||||||
mode.value = 'edit'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function newEvent(e, duplicate = false) {
|
function buildTempEvent(e, duplicate) {
|
||||||
events.data = events.data.filter(
|
const id = duplicate ? 'duplicate-event' : 'new-event'
|
||||||
(ev) => ev.id !== 'new-event' && ev.id !== 'duplicate-event',
|
return {
|
||||||
)
|
id,
|
||||||
|
|
||||||
let fromTime = e.fromTime
|
|
||||||
let toTime = e.toTime
|
|
||||||
let fromDate = e.fromDate
|
|
||||||
let toDate = e.toDate
|
|
||||||
let isFullDay = e.isFullDay
|
|
||||||
|
|
||||||
if (!duplicate) {
|
|
||||||
let t = getFromToTime(e.time)
|
|
||||||
fromTime = t[0]
|
|
||||||
toTime = t[1]
|
|
||||||
fromDate = dayjs(e.date).format('YYYY-MM-DD')
|
|
||||||
toDate = fromDate
|
|
||||||
e = { fromDate, toDate, fromTime, toTime, isFullDay }
|
|
||||||
}
|
|
||||||
|
|
||||||
event.value = {
|
|
||||||
id: duplicate ? 'duplicate-event' : 'new-event',
|
|
||||||
title: duplicate ? `${e.title} (Copy)` : '',
|
title: duplicate ? `${e.title} (Copy)` : '',
|
||||||
description: e.description || '',
|
description: e.description || '',
|
||||||
date: fromDate,
|
date: e.fromDate,
|
||||||
fromDate,
|
fromDate: e.fromDate,
|
||||||
toDate,
|
toDate: e.toDate,
|
||||||
fromTime,
|
fromTime: e.fromTime,
|
||||||
toTime,
|
toTime: e.toTime,
|
||||||
isFullDay: e.isFullDay || false,
|
isFullDay: e.isFullDay || false,
|
||||||
eventType: e.eventType || 'Public',
|
eventType: e.eventType || 'Public',
|
||||||
color: e.color || 'green',
|
color: e.color || 'green',
|
||||||
@ -373,11 +341,29 @@ function newEvent(e, duplicate = false) {
|
|||||||
referenceDocname: e.referenceDocname,
|
referenceDocname: e.referenceDocname,
|
||||||
event_participants: e.event_participants || [],
|
event_participants: e.event_participants || [],
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function newEvent(e = {}, duplicate = false) {
|
||||||
|
removeTempEvents()
|
||||||
|
|
||||||
|
let base = { ...e }
|
||||||
|
if (!duplicate) {
|
||||||
|
const [fromTime, toTime] = getFromToTime(e.time)
|
||||||
|
const fromDate = dayjs(e.date).format('YYYY-MM-DD')
|
||||||
|
base = {
|
||||||
|
...base,
|
||||||
|
fromDate,
|
||||||
|
toDate: fromDate,
|
||||||
|
fromTime,
|
||||||
|
toTime,
|
||||||
|
isFullDay: e.isFullDay,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event.value = buildTempEvent(base, duplicate)
|
||||||
events.data.push(event.value)
|
events.data.push(event.value)
|
||||||
|
|
||||||
showEventPanel.value = true
|
showEventPanel.value = true
|
||||||
activeEvent.value = duplicate ? 'duplicate-event' : 'new-event'
|
activeEvent.value = event.value.id
|
||||||
mode.value = duplicate ? 'duplicate' : 'new'
|
mode.value = duplicate ? 'duplicate' : 'new'
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,50 +377,37 @@ function close() {
|
|||||||
activeEvent.value = ''
|
activeEvent.value = ''
|
||||||
mode.value = ''
|
mode.value = ''
|
||||||
|
|
||||||
events.data = events.data.filter(
|
removeTempEvents()
|
||||||
(ev) => ev.id !== 'new-event' && ev.id !== 'duplicate-event',
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// utils
|
// utils
|
||||||
function getFromToTime(time) {
|
function getFromToTime(time) {
|
||||||
let currentTime = dayjs().format('HH:mm') || '00:00'
|
const pad = (v) => String(v).padStart(2, '0')
|
||||||
let h = currentTime.split(':')[0]
|
let now = dayjs()
|
||||||
let m = parseInt(currentTime.split(':')[1])
|
let h = now.hour()
|
||||||
|
let m = Math.floor(now.minute() / 15) * 15
|
||||||
m = Math.floor(m / 15) * 15
|
let fromHour = h
|
||||||
m = m < 10 ? '0' + m : String(m)
|
let fromMinute = m
|
||||||
|
if (time) {
|
||||||
let fromTime = `${h}:${m}`
|
if (/am|pm/i.test(time)) {
|
||||||
let toTime = `${parseInt(h) + 1}:${m}`
|
const raw = time.trim().replace(' ', '')
|
||||||
|
const ampm = raw.slice(-2).toLowerCase()
|
||||||
if (
|
let hour = parseInt(raw.slice(0, -2))
|
||||||
time?.toLowerCase().includes('am') ||
|
if (ampm === 'pm' && hour < 12) hour += 12
|
||||||
time?.toLowerCase().includes('pm')
|
if (ampm === 'am' && hour === 12) hour = 0
|
||||||
) {
|
fromHour = hour
|
||||||
// 12 hour format
|
fromMinute = 0
|
||||||
time = time.trim().replace(' ', '')
|
} else if (/^\d{1,2}:?\d{0,2}$/.test(time)) {
|
||||||
const ampm = time.slice(-2)
|
const [hh, mm = '00'] = time.split(':')
|
||||||
time = time.slice(0, -2)
|
fromHour = parseInt(hh)
|
||||||
let hour = time
|
fromMinute = parseInt(mm) || 0
|
||||||
|
|
||||||
if (ampm === 'pm' && parseInt(hour) < 12) {
|
|
||||||
hour = parseInt(hour) + 12
|
|
||||||
} else if (ampm === 'am' && hour == 12) {
|
|
||||||
hour = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fromTime = `${hour}:00`
|
|
||||||
toTime = `${parseInt(hour) + 1}:00`
|
|
||||||
} else {
|
|
||||||
// 24 hour format
|
|
||||||
let [hour, minute] = time ? time.split(':') : [h, m]
|
|
||||||
|
|
||||||
fromTime = `${hour}:${minute || '00'}`
|
|
||||||
toTime = `${parseInt(hour) + 1}:${minute || '00'}`
|
|
||||||
}
|
}
|
||||||
|
const toHour = (fromHour + 1) % 24
|
||||||
return [fromTime, toTime]
|
return [
|
||||||
|
`${pad(fromHour)}:${pad(fromMinute)}`,
|
||||||
|
`${pad(toHour)}:${pad(fromMinute)}`,
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ensureParticipantContacts(participants) {
|
async function ensureParticipantContacts(participants) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user