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" />
|
||||
<span>{{ __('Loading...') }}</span>
|
||||
</div>
|
||||
<div v-else-if="title == 'Events'" class="activity">
|
||||
<EventArea :doctype="doctype" :docname="docname" />
|
||||
</div>
|
||||
<div
|
||||
v-else-if="
|
||||
activities?.length ||
|
||||
(events.data?.length && title == 'Events') ||
|
||||
(whatsappMessages.data?.length && title == 'WhatsApp')
|
||||
"
|
||||
class="activities"
|
||||
@ -37,33 +39,6 @@
|
||||
:messages="whatsappMessages.data"
|
||||
/>
|
||||
</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
|
||||
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"
|
||||
@ -529,7 +504,7 @@ import { usersStore } from '@/stores/users'
|
||||
import { whatsappEnabled, callEnabled } from '@/composables/settings'
|
||||
import { useDocument } from '@/data/document'
|
||||
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 {
|
||||
ref,
|
||||
@ -610,47 +585,6 @@ const whatsappMessages = createResource({
|
||||
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(() => {
|
||||
$socket.off('whatsapp_message')
|
||||
})
|
||||
|
||||
@ -1,57 +1,67 @@
|
||||
<template>
|
||||
<div class="mb-5">
|
||||
<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 v-for="(event, i) in events" :key="event.name">
|
||||
<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)"
|
||||
class="activity grid grid-cols-[30px_minmax(auto,_1fr)] gap-4 px-3 sm:px-10"
|
||||
>
|
||||
<div
|
||||
class="flex w-[2px] rounded-lg"
|
||||
:style="{ backgroundColor: event.color || '#30A66D' }"
|
||||
/>
|
||||
<div class="flex-1 flex flex-col gap-1 text-base">
|
||||
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.length - 1 ? 'before:h-full' : 'before:h-4'"
|
||||
>
|
||||
<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>
|
||||
<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"
|
||||
/>
|
||||
<CalendarIcon class="h-4 w-4" />
|
||||
</div>
|
||||
<div class="flex justify-between gap-2 items-center text-ink-gray-6">
|
||||
<div>{{ formattedDateTime }}</div>
|
||||
<div>{{ formattedDate }}</div>
|
||||
</div>
|
||||
<div class="mb-5">
|
||||
<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>
|
||||
@ -59,24 +69,22 @@
|
||||
<EventModal
|
||||
v-if="showEventModal"
|
||||
v-model="showEventModal"
|
||||
v-model:events="events"
|
||||
v-model:events="eventsResource"
|
||||
:event="event"
|
||||
:doctype="doctype"
|
||||
:docname="docname"
|
||||
/>
|
||||
</template>
|
||||
<script setup>
|
||||
import CalendarIcon from '@/components/Icons/CalendarIcon.vue'
|
||||
import EventModal from '@/components/Modals/EventModal.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 { Tooltip, Avatar, dayjs, createListResource } from 'frappe-ui'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
event: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
doctype: {
|
||||
type: String,
|
||||
default: '',
|
||||
@ -87,27 +95,103 @@ const props = defineProps({
|
||||
},
|
||||
})
|
||||
|
||||
const events = defineModel()
|
||||
const { getUser } = usersStore()
|
||||
|
||||
const formattedDateTime = computed(() => {
|
||||
const start = dayjs(props.event.starts_on)
|
||||
const end = dayjs(props.event.ends_on)
|
||||
const eventsResource = 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: {
|
||||
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 `${start.format('h:mm a')} - ${end.format('h:mm a')}`
|
||||
})
|
||||
}
|
||||
|
||||
const formattedDate = computed(() => {
|
||||
const start = dayjs(props.event.starts_on)
|
||||
const formattedDate = (e) => {
|
||||
const start = dayjs(e.starts_on)
|
||||
return start.format('ddd, D MMM YYYY')
|
||||
})
|
||||
}
|
||||
|
||||
const showEventModal = ref(false)
|
||||
const event = ref(null)
|
||||
|
||||
function showEvent() {
|
||||
function showEvent(e) {
|
||||
showEventModal.value = true
|
||||
event.value = e
|
||||
}
|
||||
</script>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user