fix: refactor EventArea to improve event rendering and participant handling
This commit is contained in:
parent
ae5a1ceae5
commit
da7ee0926f
@ -21,10 +21,12 @@
|
|||||||
<LoadingIndicator class="h-6 w-6" />
|
<LoadingIndicator class="h-6 w-6" />
|
||||||
<span>{{ __('Loading...') }}</span>
|
<span>{{ __('Loading...') }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else-if="title == 'Events'" class="activity">
|
||||||
|
<EventArea :doctype="doctype" :docname="docname" />
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
v-else-if="
|
v-else-if="
|
||||||
activities?.length ||
|
activities?.length ||
|
||||||
(events.data?.length && title == 'Events') ||
|
|
||||||
(whatsappMessages.data?.length && title == 'WhatsApp')
|
(whatsappMessages.data?.length && title == 'WhatsApp')
|
||||||
"
|
"
|
||||||
class="activities"
|
class="activities"
|
||||||
@ -37,33 +39,6 @@
|
|||||||
:messages="whatsappMessages.data"
|
:messages="whatsappMessages.data"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="title == 'Events'" class="activity">
|
|
||||||
<div v-for="(event, i) in events.data" :key="event.name">
|
|
||||||
<div
|
|
||||||
class="activity grid grid-cols-[30px_minmax(auto,_1fr)] gap-4 px-3 sm:px-10"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="z-0 relative flex justify-center before:absolute before:left-[50%] before:-z-[1] before:top-0 before:border-l before:border-outline-gray-modals"
|
|
||||||
:class="
|
|
||||||
i != events.data.length - 1 ? 'before:h-full' : 'before:h-4'
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="flex h-8 w-7 items-center justify-center bg-surface-white text-ink-gray-8"
|
|
||||||
>
|
|
||||||
<CalendarIcon class="h-4 w-4" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<EventArea
|
|
||||||
class="mb-4"
|
|
||||||
v-model="events"
|
|
||||||
:event="event"
|
|
||||||
:doctype="doctype"
|
|
||||||
:docname="doc?.name"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
v-else-if="title == 'Notes'"
|
v-else-if="title == 'Notes'"
|
||||||
class="grid grid-cols-1 gap-4 px-3 pb-3 sm:px-10 sm:pb-5 lg:grid-cols-2 xl:grid-cols-3"
|
class="grid grid-cols-1 gap-4 px-3 pb-3 sm:px-10 sm:pb-5 lg:grid-cols-2 xl:grid-cols-3"
|
||||||
@ -529,7 +504,7 @@ import { usersStore } from '@/stores/users'
|
|||||||
import { whatsappEnabled, callEnabled } from '@/composables/settings'
|
import { whatsappEnabled, callEnabled } from '@/composables/settings'
|
||||||
import { useDocument } from '@/data/document'
|
import { useDocument } from '@/data/document'
|
||||||
import { capture } from '@/telemetry'
|
import { capture } from '@/telemetry'
|
||||||
import { Button, Tooltip, createResource, createListResource } from 'frappe-ui'
|
import { Button, Tooltip, createResource } from 'frappe-ui'
|
||||||
import { useElementVisibility } from '@vueuse/core'
|
import { useElementVisibility } from '@vueuse/core'
|
||||||
import {
|
import {
|
||||||
ref,
|
ref,
|
||||||
@ -610,47 +585,6 @@ const whatsappMessages = createResource({
|
|||||||
onSuccess: () => nextTick(() => scroll()),
|
onSuccess: () => nextTick(() => scroll()),
|
||||||
})
|
})
|
||||||
|
|
||||||
const events = createListResource({
|
|
||||||
doctype: 'Event',
|
|
||||||
cache: ['calendar', props.docname],
|
|
||||||
fields: [
|
|
||||||
'name',
|
|
||||||
'status',
|
|
||||||
'subject',
|
|
||||||
'description',
|
|
||||||
'starts_on',
|
|
||||||
'ends_on',
|
|
||||||
'all_day',
|
|
||||||
'event_type',
|
|
||||||
'color',
|
|
||||||
'owner',
|
|
||||||
'reference_doctype',
|
|
||||||
'reference_docname',
|
|
||||||
'creation',
|
|
||||||
],
|
|
||||||
filters: {
|
|
||||||
status: 'Open',
|
|
||||||
reference_doctype: props.doctype,
|
|
||||||
reference_docname: props.docname,
|
|
||||||
},
|
|
||||||
orderBy: 'creation desc',
|
|
||||||
auto: title.value == 'Events',
|
|
||||||
transform: (data) => {
|
|
||||||
return data.map((event) => {
|
|
||||||
if (typeof event.owner !== 'object') {
|
|
||||||
event.owner = {
|
|
||||||
label: getUser(event.owner).full_name,
|
|
||||||
image: getUser(event.owner).image,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return event
|
|
||||||
})
|
|
||||||
},
|
|
||||||
onSuccess: (d) => {
|
|
||||||
console.log(d)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
$socket.off('whatsapp_message')
|
$socket.off('whatsapp_message')
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,57 +1,67 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="mb-5">
|
<div v-for="(event, i) in events" :key="event.name">
|
||||||
<div class="mb-1 flex items-center justify-stretch gap-2 py-1 text-base">
|
|
||||||
<div class="inline-flex items-center flex-wrap gap-1 text-ink-gray-5">
|
|
||||||
<Avatar
|
|
||||||
:image="event.owner.image"
|
|
||||||
:label="event.owner.label"
|
|
||||||
size="md"
|
|
||||||
/>
|
|
||||||
<span class="font-medium text-ink-gray-8 ml-1">
|
|
||||||
{{ event.owner.label }}
|
|
||||||
</span>
|
|
||||||
<span>{{ 'has created an event' }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="ml-auto whitespace-nowrap">
|
|
||||||
<Tooltip :text="formatDate(event.creation)">
|
|
||||||
<div class="text-sm text-ink-gray-5">
|
|
||||||
{{ __(timeAgo(event.creation)) }}
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
class="flex gap-2 border cursor-pointer border-outline-gray-modals rounded-lg bg-surface-cards px-2.5 py-2.5 text-ink-gray-9"
|
class="activity grid grid-cols-[30px_minmax(auto,_1fr)] gap-4 px-3 sm:px-10"
|
||||||
@click="showEvent(event)"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="flex w-[2px] rounded-lg"
|
class="z-0 relative flex justify-center before:absolute before:left-[50%] before:-z-[1] before:top-0 before:border-l before:border-outline-gray-modals"
|
||||||
:style="{ backgroundColor: event.color || '#30A66D' }"
|
:class="i != events.length - 1 ? 'before:h-full' : 'before:h-4'"
|
||||||
/>
|
>
|
||||||
<div class="flex-1 flex flex-col gap-1 text-base">
|
|
||||||
<div
|
<div
|
||||||
class="flex items-center justify-between gap-2 font-medium text-ink-gray-7"
|
class="flex h-8 w-7 items-center justify-center bg-surface-white text-ink-gray-8"
|
||||||
>
|
>
|
||||||
<div>{{ event.subject }}</div>
|
<CalendarIcon class="h-4 w-4" />
|
||||||
<MultipleAvatar
|
|
||||||
:avatars="[
|
|
||||||
{
|
|
||||||
image: event.owner.image,
|
|
||||||
label: event.owner.label,
|
|
||||||
name: event.owner.label,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
image: event.owner.image,
|
|
||||||
label: event.owner.label,
|
|
||||||
name: event.owner.label,
|
|
||||||
},
|
|
||||||
]"
|
|
||||||
size="sm"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between gap-2 items-center text-ink-gray-6">
|
</div>
|
||||||
<div>{{ formattedDateTime }}</div>
|
<div class="mb-5">
|
||||||
<div>{{ formattedDate }}</div>
|
<div
|
||||||
|
class="mb-1 flex items-center justify-stretch gap-2 py-1 text-base"
|
||||||
|
>
|
||||||
|
<div class="inline-flex items-center flex-wrap gap-1 text-ink-gray-5">
|
||||||
|
<Avatar
|
||||||
|
:image="event.owner.image"
|
||||||
|
:label="event.owner.label"
|
||||||
|
size="md"
|
||||||
|
/>
|
||||||
|
<span class="font-medium text-ink-gray-8 ml-1">
|
||||||
|
{{ event.owner.label }}
|
||||||
|
</span>
|
||||||
|
<span>{{ 'has created an event' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="ml-auto whitespace-nowrap">
|
||||||
|
<Tooltip :text="formatDate(event.creation)">
|
||||||
|
<div class="text-sm text-ink-gray-5">
|
||||||
|
{{ __(timeAgo(event.creation)) }}
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="flex gap-2 border cursor-pointer border-outline-gray-modals rounded-lg bg-surface-cards px-2.5 py-2.5 text-ink-gray-9"
|
||||||
|
@click="showEvent(event)"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex w-[2px] rounded-lg"
|
||||||
|
:style="{ backgroundColor: event.color || '#30A66D' }"
|
||||||
|
/>
|
||||||
|
<div class="flex-1 flex flex-col gap-1 text-base">
|
||||||
|
<div
|
||||||
|
class="flex items-center justify-between gap-2 font-medium text-ink-gray-7"
|
||||||
|
>
|
||||||
|
<div>{{ event.subject }}</div>
|
||||||
|
<MultipleAvatar
|
||||||
|
v-if="event.participants?.length > 1"
|
||||||
|
:avatars="event.participants"
|
||||||
|
size="sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="flex justify-between gap-2 items-center text-ink-gray-6"
|
||||||
|
>
|
||||||
|
<div>{{ formattedDateTime(event) }}</div>
|
||||||
|
<div>{{ formattedDate(event) }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -59,24 +69,22 @@
|
|||||||
<EventModal
|
<EventModal
|
||||||
v-if="showEventModal"
|
v-if="showEventModal"
|
||||||
v-model="showEventModal"
|
v-model="showEventModal"
|
||||||
v-model:events="events"
|
v-model:events="eventsResource"
|
||||||
:event="event"
|
:event="event"
|
||||||
:doctype="doctype"
|
:doctype="doctype"
|
||||||
:docname="docname"
|
:docname="docname"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import CalendarIcon from '@/components/Icons/CalendarIcon.vue'
|
||||||
import EventModal from '@/components/Modals/EventModal.vue'
|
import EventModal from '@/components/Modals/EventModal.vue'
|
||||||
import MultipleAvatar from '@/components/MultipleAvatar.vue'
|
import MultipleAvatar from '@/components/MultipleAvatar.vue'
|
||||||
import { Tooltip, Avatar, dayjs } from 'frappe-ui'
|
import { usersStore } from '@/stores/users'
|
||||||
import { formatDate, timeAgo } from '@/utils'
|
import { formatDate, timeAgo } from '@/utils'
|
||||||
|
import { Tooltip, Avatar, dayjs, createListResource } from 'frappe-ui'
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
event: {
|
|
||||||
type: Object,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
doctype: {
|
doctype: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
@ -87,27 +95,103 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const events = defineModel()
|
const { getUser } = usersStore()
|
||||||
|
|
||||||
const formattedDateTime = computed(() => {
|
const eventsResource = createListResource({
|
||||||
const start = dayjs(props.event.starts_on)
|
doctype: 'Event',
|
||||||
const end = dayjs(props.event.ends_on)
|
cache: ['calendar', props.docname],
|
||||||
|
fields: [
|
||||||
|
'name',
|
||||||
|
'status',
|
||||||
|
'subject',
|
||||||
|
'description',
|
||||||
|
'starts_on',
|
||||||
|
'ends_on',
|
||||||
|
'all_day',
|
||||||
|
'event_type',
|
||||||
|
'color',
|
||||||
|
'owner',
|
||||||
|
'reference_doctype',
|
||||||
|
'reference_docname',
|
||||||
|
'creation',
|
||||||
|
],
|
||||||
|
filters: {
|
||||||
|
reference_doctype: props.doctype,
|
||||||
|
reference_docname: props.docname,
|
||||||
|
},
|
||||||
|
auto: true,
|
||||||
|
orderBy: 'creation desc',
|
||||||
|
onSuccess: (d) => {
|
||||||
|
console.log(d)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
if (props.event.all_day) {
|
const eventParticipants = createListResource({
|
||||||
|
doctype: 'Event Participants',
|
||||||
|
cache: ['Event Participants', props.docname],
|
||||||
|
fields: ['*'],
|
||||||
|
parent: 'Event',
|
||||||
|
})
|
||||||
|
|
||||||
|
const events = computed(() => {
|
||||||
|
if (!eventsResource.data) return []
|
||||||
|
|
||||||
|
if (!eventParticipants.data?.length) {
|
||||||
|
eventParticipants.update({
|
||||||
|
filters: {
|
||||||
|
parenttype: 'Event',
|
||||||
|
parentfield: 'event_participants',
|
||||||
|
parent: ['in', eventsResource.data.map((e) => e.name)],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
!eventParticipants.list.loading && eventParticipants.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.participants = [
|
||||||
|
event.owner,
|
||||||
|
...eventParticipants.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
|
||||||
|
})
|
||||||
|
|
||||||
|
const formattedDateTime = (e) => {
|
||||||
|
const start = dayjs(e.starts_on)
|
||||||
|
const end = dayjs(e.ends_on)
|
||||||
|
|
||||||
|
if (e.all_day) {
|
||||||
return __('All day')
|
return __('All day')
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${start.format('h:mm a')} - ${end.format('h:mm a')}`
|
return `${start.format('h:mm a')} - ${end.format('h:mm a')}`
|
||||||
})
|
}
|
||||||
|
|
||||||
const formattedDate = computed(() => {
|
const formattedDate = (e) => {
|
||||||
const start = dayjs(props.event.starts_on)
|
const start = dayjs(e.starts_on)
|
||||||
return start.format('ddd, D MMM YYYY')
|
return start.format('ddd, D MMM YYYY')
|
||||||
})
|
}
|
||||||
|
|
||||||
const showEventModal = ref(false)
|
const showEventModal = ref(false)
|
||||||
|
const event = ref(null)
|
||||||
|
|
||||||
function showEvent() {
|
function showEvent(e) {
|
||||||
showEventModal.value = true
|
showEventModal.value = true
|
||||||
|
event.value = e
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user