diff --git a/frontend/src/components/Calendar/CalendarEventPanel.vue b/frontend/src/components/Calendar/CalendarEventPanel.vue
index 0113a141..fd1b4ac3 100644
--- a/frontend/src/components/Calendar/CalendarEventPanel.vue
+++ b/frontend/src/components/Calendar/CalendarEventPanel.vue
@@ -77,7 +77,7 @@
{{ __('Time') }}
updateTime(time, true)"
>
@@ -153,7 +153,7 @@
updateTime(time)"
>
@@ -173,8 +173,8 @@
(_event.description = val)"
+ :content="event.description"
+ @change="(val) => (event.description = val)"
:placeholder="__('Add description')"
/>
@@ -200,6 +200,7 @@
import EditIcon from '@/components/Icons/EditIcon.vue'
import DescriptionIcon from '@/components/Icons/DescriptionIcon.vue'
import TimePicker from './TimePicker.vue'
+import { globalStore } from '@/stores/global'
import { getFormat } from '@/utils'
import {
TextInput,
@@ -212,7 +213,7 @@ import {
CalendarColorMap as colorMap,
CalendarActiveEvent as activeEvent,
} from 'frappe-ui'
-import { ref, computed, watch, nextTick, h } from 'vue'
+import { ref, computed, watch, h } from 'vue'
const props = defineProps({
event: {
@@ -227,6 +228,8 @@ const props = defineProps({
const emit = defineEmits(['save', 'edit', 'delete', 'details', 'close'])
+const { $dialog } = globalStore()
+
const show = defineModel()
const title = computed(() => {
@@ -236,59 +239,59 @@ const title = computed(() => {
return __('Duplicate event')
})
-const _event = ref({})
-
const eventTitle = ref(null)
const error = ref(null)
-watch(
- () => props.event,
- (newEvent) => {
- error.value = null
+const oldEvent = ref(null)
+const dirty = computed(() => {
+ return JSON.stringify(oldEvent.value) !== JSON.stringify(props.event)
+})
- nextTick(() => {
- if (props.mode === 'create' && _event.value.id === 'new-event') {
- _event.value.fromDate = newEvent.fromDate
- _event.value.toDate = newEvent.toDate
- _event.value.fromTime = newEvent.fromTime
- _event.value.toTime = newEvent.toTime
- } else {
- _event.value = { ...newEvent }
- }
- })
- setTimeout(() => eventTitle.value?.el?.focus(), 100)
+watch(
+ [() => props.mode, () => props.event],
+ () => {
+ focusOnTitle()
+ oldEvent.value = { ...props.event }
},
{ immediate: true },
)
+function focusOnTitle() {
+ setTimeout(() => {
+ if (['edit', 'create', 'duplicate'].includes(props.mode)) {
+ eventTitle.value?.el?.focus()
+ }
+ }, 100)
+}
+
function updateDate(d) {
- _event.value.fromDate = d
- _event.value.toDate = d
+ props.event.fromDate = d
+ props.event.toDate = d
}
function updateTime(t, fromTime = false) {
error.value = null
- let oldTo = _event.value.toTime || _event.value.fromTime
+ let oldTo = props.event.toTime || props.event.fromTime
if (fromTime) {
- _event.value.fromTime = t
- if (!_event.value.toTime) {
+ props.event.fromTime = t
+ if (!props.event.toTime) {
const hour = parseInt(t.split(':')[0])
const minute = parseInt(t.split(':')[1])
- _event.value.toTime = `${hour + 1}:${minute}`
+ props.event.toTime = `${hour + 1}:${minute}`
}
} else {
- _event.value.toTime = t
+ props.event.toTime = t
}
- if (_event.value.toTime && _event.value.fromTime) {
- const diff = dayjs(_event.value.toDate + ' ' + _event.value.toTime).diff(
- dayjs(_event.value.fromDate + ' ' + _event.value.fromTime),
+ if (props.event.toTime && props.event.fromTime) {
+ const diff = dayjs(props.event.toDate + ' ' + props.event.toTime).diff(
+ dayjs(props.event.fromDate + ' ' + props.event.fromTime),
'minute',
)
if (diff < 0) {
- _event.value.toTime = oldTo
+ props.event.toTime = oldTo
error.value = __('End time should be after start time')
return
}
@@ -297,49 +300,99 @@ function updateTime(t, fromTime = false) {
function saveEvent() {
error.value = null
- if (!_event.value.title) {
+ if (!props.event.title) {
error.value = __('Title is required')
eventTitle.value.el.focus()
return
}
- _event.value.fromDateTime =
- _event.value.fromDate + ' ' + _event.value.fromTime
- _event.value.toDateTime = _event.value.toDate + ' ' + _event.value.toTime
-
- emit('save', _event.value)
+ oldEvent.value = { ...props.event }
+ emit('save', props.event)
}
function editDetails() {
- emit('edit', _event.value)
+ emit('edit', props.event)
}
function duplicateEvent() {
- emit('duplicate', _event.value)
+ if (dirty.value) {
+ showDiscardChangesModal(() => reset())
+ } else {
+ emit('duplicate', props.event)
+ }
}
function deleteEvent() {
- emit('delete', _event.value.id)
+ emit('delete', props.event.id)
}
function details() {
- emit('details', _event.value)
+ if (dirty.value) {
+ showDiscardChangesModal(() => reset())
+ } else {
+ emit('details', props.event)
+ }
}
function close() {
- show.value = false
- activeEvent.value = ''
- emit('close', _event.value)
+ const _close = () => {
+ show.value = false
+ activeEvent.value = ''
+ emit('close', props.event)
+ }
+
+ if (dirty.value) {
+ showDiscardChangesModal(() => {
+ reset()
+ if (props.event.id === 'new-event') _close()
+ })
+ } else {
+ if (props.event.id === 'duplicate-event')
+ showDiscardChangesModal(() => _close())
+ else _close()
+ }
+}
+
+function reset() {
+ Object.assign(props.event, oldEvent.value)
+}
+
+function showDiscardChangesModal(action) {
+ $dialog({
+ title: __('Discard unsaved changes?'),
+ message: __(
+ 'Are you sure you want to discard unsaved changes to this event?',
+ ),
+ actions: [
+ {
+ label: __('Cancel'),
+ onClick: (close) => {
+ close()
+ },
+ },
+ {
+ label: __('Discard'),
+ variant: 'solid',
+ onClick: (close) => {
+ action()
+ close()
+ },
+ },
+ ],
+ })
}
const formattedDateTime = computed(() => {
+ const date = dayjs(props.event.fromDate)
+
if (props.event.isFullDay) {
- return `${__('All day')} - ${dayjs(props.event.fromDateTime).format('ddd, D MMM YYYY')}`
+ return `${__('All day')} - ${date.format('ddd, D MMM YYYY')}`
}
- const start = dayjs(props.event.fromDateTime)
- const end = dayjs(props.event.toDateTime)
- return `${start.format('h:mm a')} - ${end.format('h:mm a')} ${start.format('ddd, D MMM YYYY')}`
+ const start = dayjs(props.event.fromDate + ' ' + props.event.fromTime)
+ const end = dayjs(props.event.toDate + ' ' + props.event.toTime)
+
+ return `${start.format('h:mm a')} - ${end.format('h:mm a')} ${date.format('ddd, D MMM YYYY')}`
})
const colors = Object.keys(colorMap).map((color) => ({
@@ -350,7 +403,7 @@ const colors = Object.keys(colorMap).map((color) => ({
style: { backgroundColor: colorMap[color].color },
}),
onClick: () => {
- _event.value.color = colorMap[color].color
+ props.event.color = colorMap[color].color
},
}))
diff --git a/frontend/src/pages/Calendar.vue b/frontend/src/pages/Calendar.vue
index e502dcac..e1863092 100644
--- a/frontend/src/pages/Calendar.vue
+++ b/frontend/src/pages/Calendar.vue
@@ -133,17 +133,26 @@ const events = createListResource({
filters: { status: 'Open', owner: user },
auto: true,
transform: (data) => {
- return data.map((event) => ({
- id: event.name,
- title: event.subject,
- description: event.description,
- status: event.status,
- fromDate: event.starts_on,
- toDate: event.ends_on,
- isFullDay: event.all_day,
- eventType: event.event_type,
- color: event.color,
- }))
+ return data.map((event) => {
+ let fromDate = dayjs(event.starts_on).format('YYYY-MM-DD')
+ let toDate = dayjs(event.ends_on).format('YYYY-MM-DD')
+ let fromTime = dayjs(event.starts_on).format('HH:mm')
+ let toTime = dayjs(event.ends_on).format('HH:mm')
+
+ return {
+ id: event.name,
+ title: event.subject,
+ description: event.description,
+ status: event.status,
+ fromDate,
+ toDate,
+ fromTime,
+ toTime,
+ isFullDay: event.all_day,
+ eventType: event.event_type,
+ color: event.color,
+ }
+ })
},
insert: {
onSuccess: () => events.reload(),
@@ -175,21 +184,13 @@ function createEvent(_event) {
{
subject: _event.title,
description: _event.description,
- starts_on: _event.fromDateTime,
- ends_on: _event.toDateTime,
- all_day: _event.isFullDay,
+ starts_on: _event.fromDate + ' ' + _event.fromTime,
+ ends_on: _event.toDate + ' ' + _event.toTime,
+ all_day: _event.isFullDay || false,
event_type: _event.eventType,
color: _event.color,
},
- {
- onSuccess: (e) => {
- _event.id = e.name
- event.value = _event
- showEventPanel.value = true
- activeEvent.value = e.name
- mode.value = 'details'
- },
- },
+ { onSuccess: (e) => showDetails({ id: e.name }) },
)
}
@@ -202,15 +203,15 @@ function updateEvent(_event) {
name: _event.id,
subject: _event.title,
description: _event.description,
- starts_on: _event.fromDateTime,
- ends_on: _event.toDateTime,
+ starts_on: _event.fromDate + ' ' + _event.fromTime,
+ ends_on: _event.toDate + ' ' + _event.toTime,
all_day: _event.isFullDay,
event_type: _event.eventType,
color: _event.color,
},
{
- onSuccess: () => {
- mode.value = 'details'
+ onSuccess: (e) => {
+ showEventPanel.value && showDetails({ id: e.name })
},
},
)
@@ -225,8 +226,6 @@ function deleteEvent(eventID) {
$dialog({
title: __('Delete'),
message: __('Are you sure you want to delete this event?'),
- variant: 'solid',
- theme: 'red',
actions: [
{
label: __('Delete'),
@@ -264,7 +263,7 @@ function showDetails(e) {
)
showEventPanel.value = true
- event.value = { ..._e }
+ event.value = events.data.find((ev) => ev.id === _e.id) || _e
activeEvent.value = _e.id
mode.value = 'details'
}
@@ -278,7 +277,7 @@ function editDetails(e) {
)
showEventPanel.value = true
- event.value = { ..._e }
+ event.value = events.data.find((ev) => ev.id === _e.id) || _e
activeEvent.value = _e.id
mode.value = 'edit'
}
@@ -291,43 +290,34 @@ function newEvent(e, duplicate = false) {
let fromTime = e.fromTime
let toTime = e.toTime
let fromDate = e.fromDate
+ let toDate = e.toDate
if (!duplicate) {
let t = getFromToTime(e.time)
fromTime = t[0]
toTime = t[1]
fromDate = dayjs(e.date).format('YYYY-MM-DD')
- e = { fromDate, fromTime, toTime }
+ toDate = fromDate
+ e = { fromDate, toDate, fromTime, toTime }
}
- showEventPanel.value = true
-
event.value = {
id: duplicate ? 'duplicate-event' : 'new-event',
title: duplicate ? `${e.title} (Copy)` : '',
description: e.description || '',
date: fromDate,
- fromDate: fromDate,
- toDate: fromDate,
+ fromDate,
+ toDate,
fromTime,
toTime,
- isFullDay: e.isFullDay,
+ isFullDay: e.isFullDay || false,
eventType: e.eventType || 'Public',
color: e.color || 'green',
}
- events.data.push({
- id: duplicate ? 'duplicate-event' : 'new-event',
- title: duplicate ? `${e.title} (Copy)` : '',
- description: e.description || '',
- status: 'Open',
- eventType: e.eventType || 'Public',
- fromDate: fromDate + ' ' + fromTime,
- toDate: fromDate + ' ' + toTime,
- color: e.color || 'green',
- isFullDay: e.isFullDay,
- })
+ events.data.push(event.value)
+ showEventPanel.value = true
activeEvent.value = duplicate ? 'duplicate-event' : 'new-event'
mode.value = duplicate ? 'duplicate' : 'create'
}