From 8031964d3df09921599ebc39840fa9f9490d44d6 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 2 Sep 2025 20:39:51 +0530 Subject: [PATCH] fix: added attendee, color, link in event modal and fixed existing bug --- .../src/components/Activities/AllModals.vue | 19 +- .../src/components/Activities/EventArea.vue | 124 ++--------- frontend/src/components/Modals/EventModal.vue | 201 ++++++++++++++---- frontend/src/composables/event.js | 138 ++++++++++++ frontend/src/utils/index.js | 30 +++ 5 files changed, 345 insertions(+), 167 deletions(-) create mode 100644 frontend/src/composables/event.js diff --git a/frontend/src/components/Activities/AllModals.vue b/frontend/src/components/Activities/AllModals.vue index 37da04a0..fff3e02f 100644 --- a/frontend/src/components/Activities/AllModals.vue +++ b/frontend/src/components/Activities/AllModals.vue @@ -25,8 +25,7 @@ @@ -36,6 +35,7 @@ import TaskModal from '@/components/Modals/TaskModal.vue' import NoteModal from '@/components/Modals/NoteModal.vue' import CallLogModal from '@/components/Modals/CallLogModal.vue' import EventModal from '@/components/Modals/EventModal.vue' +import { showEventModal, activeEvent } from '@/composables/event' import { call } from 'frappe-ui' import { ref } from 'vue' import { useRoute, useRouter } from 'vue-router' @@ -46,22 +46,11 @@ const props = defineProps({ }) const activities = defineModel() -const events = defineModel('events') - -const showEventModal = ref(false) -const event = ref({}) +// Event function showEvent(e) { - event.value = e || { - subject: '', - description: '', - starts_on: '', - ends_on: '', - all_day: false, - event_type: 'Public', - color: 'green', - } showEventModal.value = true + activeEvent.value = e } // Tasks diff --git a/frontend/src/components/Activities/EventArea.vue b/frontend/src/components/Activities/EventArea.vue index 296b5e62..ca3ee152 100644 --- a/frontend/src/components/Activities/EventArea.vue +++ b/frontend/src/components/Activities/EventArea.vue @@ -58,31 +58,25 @@
-
{{ formattedDateTime(event) }}
-
{{ formattedDate(event) }}
+
+ {{ + startEndTime(event.starts_on, event.ends_on, event.all_day) + }} +
+
{{ startDate(event.starts_on) }}
- diff --git a/frontend/src/components/Modals/EventModal.vue b/frontend/src/components/Modals/EventModal.vue index ec4bfe4f..f525d1cd 100644 --- a/frontend/src/components/Modals/EventModal.vue +++ b/frontend/src/components/Modals/EventModal.vue @@ -42,15 +42,29 @@
{{ __('Title') }}
- +
+ +
+
+
+ + +
@@ -83,7 +97,7 @@
+
+
+ {{ __('Link') }} +
+
+ + +
+
+
+
+ {{ __('Attendees') }} +
+
+ +
+
{{ __('Description') }} @@ -119,7 +177,7 @@
diff --git a/frontend/src/composables/event.js b/frontend/src/composables/event.js new file mode 100644 index 00000000..2d1e3949 --- /dev/null +++ b/frontend/src/composables/event.js @@ -0,0 +1,138 @@ +import { usersStore } from '@/stores/users' +import { dayjs, createListResource } from 'frappe-ui' +import { sameArrayContents } from '@/utils' +import { computed, ref } from 'vue' + +export const showEventModal = ref(false) +export const activeEvent = ref(null) + +export function useEvent(doctype, docname) { + const { getUser } = usersStore() + + const eventsResource = createListResource({ + doctype: 'Event', + cache: ['calendar', docname], + fields: [ + 'name', + 'status', + 'subject', + 'description', + 'starts_on', + 'ends_on', + 'all_day', + 'event_type', + 'color', + 'owner', + 'reference_doctype', + 'reference_docname', + 'creation', + ], + filters: { + reference_doctype: doctype, + reference_docname: docname, + }, + auto: true, + orderBy: 'creation desc', + onSuccess: (d) => { + console.log(d) + }, + }) + + const eventParticipantsResource = createListResource({ + doctype: 'Event Participants', + fields: ['*'], + parent: 'Event', + }) + + const events = computed(() => { + if (!eventsResource.data) return [] + const eventNames = eventsResource.data.map((e) => e.name) + if ( + !eventParticipantsResource.data?.length || + eventsParticipantIsUpdated(eventNames) + ) { + eventParticipantsResource.update({ + filters: { + parenttype: 'Event', + parentfield: 'event_participants', + parent: ['in', eventNames], + }, + }) + !eventParticipantsResource.list.loading && + eventParticipantsResource.reload() + } else { + eventsResource.data.forEach((event) => { + if (typeof event.owner !== 'object') { + event.owner = { + label: getUser(event.owner).full_name, + image: getUser(event.owner).user_image, + name: event.owner, + } + } + + event.event_participants = [ + ...eventParticipantsResource.data.filter( + (participant) => participant.parent === event.name, + ), + ] + + event.participants = [ + event.owner, + ...eventParticipantsResource.data + .filter((participant) => participant.parent === event.name) + .map((participant) => ({ + label: getUser(participant.email).full_name || participant.email, + image: getUser(participant.email).user_image || '', + name: participant.email, + })), + ] + }) + } + + return eventsResource.data + }) + + function eventsParticipantIsUpdated(eventNames) { + const parentFilter = eventParticipantsResource.filters?.parent?.[1] + + if (eventNames.length && !sameArrayContents(parentFilter, eventNames)) + return true + + let d = eventsResource.setValue.data + if (!d) return false + + let newParticipants = d.event_participants.map((p) => p.name) + let oldParticipants = eventParticipantsResource.data + .filter((p) => p.parent === d.name) + .map((p) => p.name) + + return !sameArrayContents(newParticipants, oldParticipants) + } + + const startEndTime = ( + startTime, + endTime, + isFullDay = false, + format = 'h:mm a', + ) => { + const start = dayjs(startTime) + const end = dayjs(endTime) + + if (isFullDay) return __('All day') + + return `${start.format(format)} - ${end.format(format)}` + } + + const startDate = (startTime, format = 'ddd, D MMM YYYY') => { + const start = dayjs(startTime) + return start.format(format) + } + + return { + eventsResource, + eventParticipantsResource, + events, + startEndTime, + startDate, + } +} diff --git a/frontend/src/utils/index.js b/frontend/src/utils/index.js index c6af2af9..775a63a4 100644 --- a/frontend/src/utils/index.js +++ b/frontend/src/utils/index.js @@ -689,3 +689,33 @@ export function validateConditions(conditions) { return conditions.length > 0 } + +// sameArrayContents: returns true if both arrays have exactly the same elements +// (including duplicate counts) irrespective of order. +// Non-arrays or arrays of different length return false. +export function sameArrayContents(a, b) { + if (a === b) return true + if (!Array.isArray(a) || !Array.isArray(b)) return false + if (a.length !== b.length) return false + if (a.length === 0) return true + const counts = new Map() + for (const v of a) { + counts.set(v, (counts.get(v) || 0) + 1) + } + for (const v of b) { + const c = counts.get(v) + if (!c) return false + if (c === 1) counts.delete(v) + else counts.set(v, c - 1) + } + return counts.size === 0 +} + +// orderSensitiveEqual: returns true only if arrays are strictly equal index-wise +export function orderSensitiveEqual(a, b) { + if (a === b) return true + if (!Array.isArray(a) || !Array.isArray(b)) return false + if (a.length !== b.length) return false + for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false + return true +}