fix: refactor event handling and validation logic in CalendarEventPanel and EventModal components
This commit is contained in:
parent
8031964d3d
commit
1e99192448
@ -351,9 +351,14 @@ import Link from '@/components/Controls/Link.vue'
|
|||||||
import EditIcon from '@/components/Icons/EditIcon.vue'
|
import EditIcon from '@/components/Icons/EditIcon.vue'
|
||||||
import DescriptionIcon from '@/components/Icons/DescriptionIcon.vue'
|
import DescriptionIcon from '@/components/Icons/DescriptionIcon.vue'
|
||||||
import { globalStore } from '@/stores/global'
|
import { globalStore } from '@/stores/global'
|
||||||
import { usersStore } from '@/stores/users'
|
|
||||||
import { validateEmail } from '@/utils'
|
import { validateEmail } from '@/utils'
|
||||||
import { allTimeSlots } from '@/components/Calendar/utils'
|
import {
|
||||||
|
normalizeParticipants,
|
||||||
|
buildEndTimeOptions,
|
||||||
|
computeAutoToTime,
|
||||||
|
validateTimeRange,
|
||||||
|
parseEventDoc,
|
||||||
|
} from '@/composables/event'
|
||||||
import {
|
import {
|
||||||
TextInput,
|
TextInput,
|
||||||
Switch,
|
Switch,
|
||||||
@ -389,7 +394,6 @@ const emit = defineEmits([
|
|||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { $dialog } = globalStore()
|
const { $dialog } = globalStore()
|
||||||
const { getUser } = usersStore()
|
|
||||||
|
|
||||||
const show = defineModel()
|
const show = defineModel()
|
||||||
const event = defineModel('event')
|
const event = defineModel('event')
|
||||||
@ -401,18 +405,7 @@ const peoples = computed({
|
|||||||
return _event.value.event_participants || []
|
return _event.value.event_participants || []
|
||||||
},
|
},
|
||||||
set(list) {
|
set(list) {
|
||||||
const seen = new Set()
|
_event.value.event_participants = normalizeParticipants(list)
|
||||||
const out = []
|
|
||||||
for (const a of list || []) {
|
|
||||||
if (!a?.email || seen.has(a.email)) continue
|
|
||||||
seen.add(a.email)
|
|
||||||
out.push({
|
|
||||||
email: a.email,
|
|
||||||
reference_doctype: a.reference_doctype || 'Contact',
|
|
||||||
reference_docname: a.reference_docname || '',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
_event.value.event_participants = out
|
|
||||||
sync()
|
sync()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -461,12 +454,12 @@ function fetchEvent() {
|
|||||||
name: event.value.id,
|
name: event.value.id,
|
||||||
fields: ['*'],
|
fields: ['*'],
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
_event.value = parseEvent(data)
|
_event.value = parseEventDoc(data)
|
||||||
oldEvent.value = { ..._event.value }
|
oldEvent.value = { ..._event.value }
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if (eventResource.value.doc && !event.value.reloadEvent) {
|
if (eventResource.value.doc && !event.value.reloadEvent) {
|
||||||
_event.value = parseEvent(eventResource.value.doc)
|
_event.value = parseEventDoc(eventResource.value.doc)
|
||||||
oldEvent.value = { ..._event.value }
|
oldEvent.value = { ..._event.value }
|
||||||
} else {
|
} else {
|
||||||
eventResource.value.reload()
|
eventResource.value.reload()
|
||||||
@ -478,30 +471,6 @@ function fetchEvent() {
|
|||||||
showAllParticipants.value = false
|
showAllParticipants.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseEvent(_e) {
|
|
||||||
return {
|
|
||||||
id: _e.name,
|
|
||||||
title: _e.subject,
|
|
||||||
description: _e.description,
|
|
||||||
status: _e.status,
|
|
||||||
fromDate: dayjs(_e.starts_on).format('YYYY-MM-DD'),
|
|
||||||
toDate: dayjs(_e.ends_on).format('YYYY-MM-DD'),
|
|
||||||
fromTime: dayjs(_e.starts_on).format('HH:mm'),
|
|
||||||
toTime: dayjs(_e.ends_on).format('HH:mm'),
|
|
||||||
isFullDay: _e.all_day,
|
|
||||||
eventType: _e.event_type,
|
|
||||||
color: _e.color,
|
|
||||||
referenceDoctype: _e.reference_doctype,
|
|
||||||
referenceDocname: _e.reference_docname,
|
|
||||||
event_participants: _e.event_participants || [],
|
|
||||||
owner: {
|
|
||||||
label: getUser(_e.owner).full_name,
|
|
||||||
image: getUser(_e.owner).user_image,
|
|
||||||
value: _e.owner,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function focusOnTitle() {
|
function focusOnTitle() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (['edit', 'new', 'duplicate'].includes(props.mode)) {
|
if (['edit', 'new', 'duplicate'].includes(props.mode)) {
|
||||||
@ -523,63 +492,27 @@ function updateDate(d) {
|
|||||||
|
|
||||||
function updateTime(t, fromTime = false) {
|
function updateTime(t, fromTime = false) {
|
||||||
error.value = null
|
error.value = null
|
||||||
|
const prevTo = _event.value.toTime
|
||||||
if (fromTime) {
|
if (fromTime) {
|
||||||
_event.value.fromTime = t
|
_event.value.fromTime = t
|
||||||
const hour = parseInt(t.split(':')[0])
|
if (!_event.value.toTime || _event.value.toTime <= t) {
|
||||||
const minute = parseInt(t.split(':')[1])
|
_event.value.toTime = computeAutoToTime(t)
|
||||||
|
|
||||||
const computePlusHour = () => {
|
|
||||||
let nh = hour + 1
|
|
||||||
let nm = minute
|
|
||||||
if (nh >= 24) {
|
|
||||||
nh = 23
|
|
||||||
nm = 59
|
|
||||||
}
|
|
||||||
return `${String(nh).padStart(2, '0')}:${String(nm).padStart(2, '0')}`
|
|
||||||
}
|
|
||||||
if (!_event.value.toTime) {
|
|
||||||
_event.value.toTime = computePlusHour()
|
|
||||||
} else if (_event.value.toTime <= t) {
|
|
||||||
_event.value.toTime = computePlusHour()
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_event.value.toTime = t
|
_event.value.toTime = t
|
||||||
}
|
}
|
||||||
|
const { valid, error: err } = validateTimeRange({
|
||||||
validateFromToTime() && sync()
|
fromDate: _event.value.fromDate,
|
||||||
}
|
fromTime: _event.value.fromTime,
|
||||||
|
toTime: _event.value.toTime,
|
||||||
function validateFromToTime() {
|
isFullDay: _event.value.isFullDay,
|
||||||
// Generic validator for start/end times before saving.
|
})
|
||||||
// Returns true if valid, else sets error message and returns false.
|
if (!valid) {
|
||||||
error.value = null
|
error.value = err
|
||||||
// Full day events don't require time validation
|
_event.value.toTime = prevTo
|
||||||
if (_event.value.isFullDay) return true
|
} else {
|
||||||
|
sync()
|
||||||
// Only validate within the single start date; ignore any separate end date.
|
|
||||||
const fromDate = _event.value.fromDate
|
|
||||||
const fromTime = _event.value.fromTime
|
|
||||||
const toTime = _event.value.toTime
|
|
||||||
|
|
||||||
if (!fromTime || !toTime) {
|
|
||||||
error.value = __('Start and end time are required')
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const start = dayjs(fromDate + ' ' + fromTime)
|
|
||||||
const end = dayjs(fromDate + ' ' + toTime)
|
|
||||||
|
|
||||||
if (!start.isValid() || !end.isValid()) {
|
|
||||||
error.value = __('Invalid start or end time')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (end.diff(start, 'minute') <= 0) {
|
|
||||||
error.value = __('End time should be after start time')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveEvent() {
|
function saveEvent() {
|
||||||
@ -590,7 +523,16 @@ function saveEvent() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!validateFromToTime()) return
|
const { valid, error: err } = validateTimeRange({
|
||||||
|
fromDate: _event.value.fromDate,
|
||||||
|
fromTime: _event.value.fromTime,
|
||||||
|
toTime: _event.value.toTime,
|
||||||
|
isFullDay: _event.value.isFullDay,
|
||||||
|
})
|
||||||
|
if (!valid) {
|
||||||
|
error.value = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
oldEvent.value = { ..._event.value }
|
oldEvent.value = { ..._event.value }
|
||||||
emit('save', _event.value)
|
emit('save', _event.value)
|
||||||
@ -716,42 +658,7 @@ function getTooltip(m) {
|
|||||||
return parts.length ? parts.join(': ') : email
|
return parts.length ? parts.join(': ') : email
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatDuration(mins) {
|
const toOptions = computed(() => buildEndTimeOptions(_event.value.fromTime))
|
||||||
// For < 1 hour show minutes, else show hours (with decimal for 15/30/45 mins)
|
|
||||||
if (mins < 60) return __('{0} mins', [mins])
|
|
||||||
let hours = mins / 60
|
|
||||||
|
|
||||||
// keep hours decimal to 2 only if decimal is not 0
|
|
||||||
if (hours % 1 !== 0 && hours % 1 !== 0.5) {
|
|
||||||
hours = hours.toFixed(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Number.isInteger(hours)) {
|
|
||||||
return hours === 1 ? __('1 hr') : __('{0} hrs', [hours])
|
|
||||||
}
|
|
||||||
// Keep decimal representation for > 1 hour fractional durations
|
|
||||||
return `${hours} hrs`
|
|
||||||
}
|
|
||||||
|
|
||||||
const toOptions = computed(() => {
|
|
||||||
const fromTime = _event.value.fromTime
|
|
||||||
const timeSlots = allTimeSlots()
|
|
||||||
if (!fromTime) return timeSlots
|
|
||||||
const [fh, fm] = fromTime.split(':').map((n) => parseInt(n))
|
|
||||||
const fromTotal = fh * 60 + fm
|
|
||||||
// find first slot strictly after fromTime (even if fromTime not exactly a slot)
|
|
||||||
const startIndex = timeSlots.findIndex((o) => o.value > fromTime)
|
|
||||||
if (startIndex === -1) return []
|
|
||||||
return timeSlots.slice(startIndex).map((o) => {
|
|
||||||
const [th, tm] = o.value.split(':').map((n) => parseInt(n))
|
|
||||||
const toTotal = th * 60 + tm
|
|
||||||
const duration = toTotal - fromTotal
|
|
||||||
return {
|
|
||||||
...o,
|
|
||||||
label: `${o.label} (${formatDuration(duration)})`,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
function updateEvent(_e) {
|
function updateEvent(_e) {
|
||||||
Object.assign(_event.value, _e)
|
Object.assign(_event.value, _e)
|
||||||
|
|||||||
@ -215,8 +215,13 @@ import {
|
|||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { globalStore } from '@/stores/global'
|
import { globalStore } from '@/stores/global'
|
||||||
import { validateEmail } from '@/utils'
|
import { validateEmail } from '@/utils'
|
||||||
import { allTimeSlots } from '@/components/Calendar/utils'
|
import {
|
||||||
import { useEvent } from '@/composables/event'
|
useEvent,
|
||||||
|
normalizeParticipants,
|
||||||
|
buildEndTimeOptions,
|
||||||
|
computeAutoToTime,
|
||||||
|
validateTimeRange,
|
||||||
|
} from '@/composables/event'
|
||||||
import { CalendarColorMap as colorMap } from 'frappe-ui'
|
import { CalendarColorMap as colorMap } from 'frappe-ui'
|
||||||
import { onMounted, ref, computed, h } from 'vue'
|
import { onMounted, ref, computed, h } from 'vue'
|
||||||
|
|
||||||
@ -271,18 +276,7 @@ const peoples = computed({
|
|||||||
return _event.value.event_participants || []
|
return _event.value.event_participants || []
|
||||||
},
|
},
|
||||||
set(list) {
|
set(list) {
|
||||||
const seen = new Set()
|
_event.value.event_participants = normalizeParticipants(list)
|
||||||
const out = []
|
|
||||||
for (const a of list || []) {
|
|
||||||
if (!a?.email || seen.has(a.email)) continue
|
|
||||||
seen.add(a.email)
|
|
||||||
out.push({
|
|
||||||
email: a.email,
|
|
||||||
reference_doctype: a.reference_doctype || 'Contact',
|
|
||||||
reference_docname: a.reference_docname || '',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
_event.value.event_participants = out
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -323,42 +317,25 @@ function updateDate(d) {
|
|||||||
|
|
||||||
function updateTime(t, fromTime = false) {
|
function updateTime(t, fromTime = false) {
|
||||||
error.value = null
|
error.value = null
|
||||||
let oldTo = _event.value.toTime || _event.value.fromTime
|
const prevTo = _event.value.toTime
|
||||||
|
|
||||||
if (fromTime) {
|
if (fromTime) {
|
||||||
_event.value.fromTime = t
|
_event.value.fromTime = t
|
||||||
if (!_event.value.toTime) {
|
if (!_event.value.toTime || _event.value.toTime <= t) {
|
||||||
const hour = parseInt(t.split(':')[0])
|
_event.value.toTime = computeAutoToTime(t)
|
||||||
const minute = parseInt(t.split(':')[1])
|
|
||||||
let nh = hour + 1
|
|
||||||
let nm = minute
|
|
||||||
if (nh >= 24) {
|
|
||||||
nh = 23
|
|
||||||
nm = 59
|
|
||||||
}
|
|
||||||
_event.value.toTime = `${String(nh).padStart(2, '0')}:${String(nm).padStart(2, '0')}`
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_event.value.toTime = t
|
_event.value.toTime = t
|
||||||
}
|
}
|
||||||
|
const { valid, error: err } = validateTimeRange({
|
||||||
validateFromToTime(oldTo)
|
fromDate: _event.value.fromDate,
|
||||||
}
|
fromTime: _event.value.fromTime,
|
||||||
|
toTime: _event.value.toTime,
|
||||||
function validateFromToTime(oldTo) {
|
isFullDay: _event.value.isFullDay,
|
||||||
if (_event.value.isFullDay) return true
|
})
|
||||||
if (_event.value.toTime && _event.value.fromTime) {
|
if (!valid) {
|
||||||
const diff = dayjs(_event.value.fromDate + ' ' + _event.value.toTime).diff(
|
error.value = err
|
||||||
dayjs(_event.value.fromDate + ' ' + _event.value.fromTime),
|
_event.value.toTime = prevTo
|
||||||
'minute',
|
|
||||||
)
|
|
||||||
if (diff <= 0) {
|
|
||||||
_event.value.toTime = oldTo
|
|
||||||
error.value = __('End time should be after start time')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function update() {
|
function update() {
|
||||||
@ -369,7 +346,16 @@ function update() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
validateFromToTime(_event.value.toTime)
|
const { valid, error: err } = validateTimeRange({
|
||||||
|
fromDate: _event.value.fromDate,
|
||||||
|
fromTime: _event.value.fromTime,
|
||||||
|
toTime: _event.value.toTime,
|
||||||
|
isFullDay: _event.value.isFullDay,
|
||||||
|
})
|
||||||
|
if (!valid) {
|
||||||
|
error.value = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (_event.value.id && _event.value.id !== 'duplicate') {
|
if (_event.value.id && _event.value.id !== 'duplicate') {
|
||||||
updateEvent()
|
updateEvent()
|
||||||
@ -467,42 +453,7 @@ function deleteEvent() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatDuration(mins) {
|
const toOptions = computed(() => buildEndTimeOptions(_event.value.fromTime))
|
||||||
// For < 1 hour show minutes, else show hours (with decimal for 15/30/45 mins)
|
|
||||||
if (mins < 60) return __('{0} mins', [mins])
|
|
||||||
let hours = mins / 60
|
|
||||||
|
|
||||||
// keep hours decimal to 2 only if decimal is not 0
|
|
||||||
if (hours % 1 !== 0 && hours % 1 !== 0.5) {
|
|
||||||
hours = hours.toFixed(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Number.isInteger(hours)) {
|
|
||||||
return hours === 1 ? __('1 hr') : __('{0} hrs', [hours])
|
|
||||||
}
|
|
||||||
// Keep decimal representation for > 1 hour fractional durations
|
|
||||||
return `${hours} hrs`
|
|
||||||
}
|
|
||||||
|
|
||||||
const toOptions = computed(() => {
|
|
||||||
const fromTime = _event.value.fromTime
|
|
||||||
const timeSlots = allTimeSlots()
|
|
||||||
if (!fromTime) return timeSlots
|
|
||||||
const [fh, fm] = fromTime.split(':').map((n) => parseInt(n))
|
|
||||||
const fromTotal = fh * 60 + fm
|
|
||||||
// find first slot strictly after fromTime (even if fromTime not exactly a slot)
|
|
||||||
const startIndex = timeSlots.findIndex((o) => o.value > fromTime)
|
|
||||||
if (startIndex === -1) return []
|
|
||||||
return timeSlots.slice(startIndex).map((o) => {
|
|
||||||
const [th, tm] = o.value.split(':').map((n) => parseInt(n))
|
|
||||||
const toTotal = th * 60 + tm
|
|
||||||
const duration = toTotal - fromTotal
|
|
||||||
return {
|
|
||||||
...o,
|
|
||||||
label: `${o.label} (${formatDuration(duration)})`,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const linkDoctypeOptions = [
|
const linkDoctypeOptions = [
|
||||||
{ label: '', value: '' },
|
{ label: '', value: '' },
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { usersStore } from '@/stores/users'
|
|||||||
import { dayjs, createListResource } from 'frappe-ui'
|
import { dayjs, createListResource } from 'frappe-ui'
|
||||||
import { sameArrayContents } from '@/utils'
|
import { sameArrayContents } from '@/utils'
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
|
import { allTimeSlots } from '@/components/Calendar/utils'
|
||||||
|
|
||||||
export const showEventModal = ref(false)
|
export const showEventModal = ref(false)
|
||||||
export const activeEvent = ref(null)
|
export const activeEvent = ref(null)
|
||||||
@ -136,3 +137,101 @@ export function useEvent(doctype, docname) {
|
|||||||
startDate,
|
startDate,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function normalizeParticipants(list = []) {
|
||||||
|
const seen = new Set()
|
||||||
|
const out = []
|
||||||
|
for (const a of list || []) {
|
||||||
|
if (!a?.email || seen.has(a.email)) continue
|
||||||
|
seen.add(a.email)
|
||||||
|
out.push({
|
||||||
|
email: a.email,
|
||||||
|
reference_doctype: a.reference_doctype || 'Contact',
|
||||||
|
reference_docname: a.reference_docname || '',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatDuration(mins) {
|
||||||
|
if (mins < 60) return __('{0} mins', [mins])
|
||||||
|
let hours = mins / 60
|
||||||
|
if (hours % 1 !== 0 && hours % 1 !== 0.5) {
|
||||||
|
hours = hours.toFixed(2)
|
||||||
|
}
|
||||||
|
if (Number.isInteger(hours)) {
|
||||||
|
return hours === 1 ? __('1 hr') : __('{0} hrs', [hours])
|
||||||
|
}
|
||||||
|
return `${hours} hrs`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildEndTimeOptions(fromTime) {
|
||||||
|
const timeSlots = allTimeSlots()
|
||||||
|
if (!fromTime) return timeSlots
|
||||||
|
const startIndex = timeSlots.findIndex((o) => o.value > fromTime)
|
||||||
|
if (startIndex === -1) return []
|
||||||
|
const [fh, fm] = fromTime.split(':').map((n) => parseInt(n))
|
||||||
|
const fromTotal = fh * 60 + fm
|
||||||
|
return timeSlots.slice(startIndex).map((o) => {
|
||||||
|
const [th, tm] = o.value.split(':').map((n) => parseInt(n))
|
||||||
|
const toTotal = th * 60 + tm
|
||||||
|
const duration = toTotal - fromTotal
|
||||||
|
return { ...o, label: `${o.label} (${formatDuration(duration)})` }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function computeAutoToTime(fromTime) {
|
||||||
|
if (!fromTime) return ''
|
||||||
|
const [hour, minute] = fromTime.split(':').map((n) => parseInt(n))
|
||||||
|
let nh = hour + 1
|
||||||
|
let nm = minute
|
||||||
|
if (nh >= 24) {
|
||||||
|
nh = 23
|
||||||
|
nm = 59
|
||||||
|
}
|
||||||
|
return `${String(nh).padStart(2, '0')}:${String(nm).padStart(2, '0')}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function validateTimeRange({ fromDate, fromTime, toTime, isFullDay }) {
|
||||||
|
if (isFullDay) return { valid: true, error: null }
|
||||||
|
if (!fromTime || !toTime) {
|
||||||
|
return { valid: false, error: __('Start and end time are required') }
|
||||||
|
}
|
||||||
|
const start = dayjs(fromDate + ' ' + fromTime)
|
||||||
|
const end = dayjs(fromDate + ' ' + toTime)
|
||||||
|
if (!start.isValid() || !end.isValid()) {
|
||||||
|
return { valid: false, error: __('Invalid start or end time') }
|
||||||
|
}
|
||||||
|
if (end.diff(start, 'minute') <= 0) {
|
||||||
|
return { valid: false, error: __('End time should be after start time') }
|
||||||
|
}
|
||||||
|
return { valid: true, error: null }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseEventDoc(doc) {
|
||||||
|
if (!doc) return {}
|
||||||
|
const { getUser } = usersStore()
|
||||||
|
return {
|
||||||
|
id: doc.name,
|
||||||
|
title: doc.subject,
|
||||||
|
description: doc.description,
|
||||||
|
status: doc.status,
|
||||||
|
fromDate: dayjs(doc.starts_on).format('YYYY-MM-DD'),
|
||||||
|
toDate: dayjs(doc.ends_on).format('YYYY-MM-DD'),
|
||||||
|
fromTime: dayjs(doc.starts_on).format('HH:mm'),
|
||||||
|
toTime: dayjs(doc.ends_on).format('HH:mm'),
|
||||||
|
isFullDay: doc.all_day,
|
||||||
|
eventType: doc.event_type,
|
||||||
|
color: doc.color,
|
||||||
|
referenceDoctype: doc.reference_doctype,
|
||||||
|
referenceDocname: doc.reference_docname,
|
||||||
|
event_participants: doc.event_participants || [],
|
||||||
|
owner: doc.owner
|
||||||
|
? {
|
||||||
|
label: getUser(doc.owner).full_name,
|
||||||
|
image: getUser(doc.owner).user_image,
|
||||||
|
value: doc.owner,
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user