Merge pull request #433 from shariquerik/notification-refactor
This commit is contained in:
commit
0698fd6d78
@ -18,15 +18,12 @@
|
|||||||
>
|
>
|
||||||
<template #right>
|
<template #right>
|
||||||
<Badge
|
<Badge
|
||||||
v-if="
|
v-if="!isSidebarCollapsed && unreadNotificationsCount"
|
||||||
!isSidebarCollapsed &&
|
:label="unreadNotificationsCount"
|
||||||
notificationsStore().unreadNotificationsCount
|
|
||||||
"
|
|
||||||
:label="notificationsStore().unreadNotificationsCount"
|
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-else-if="notificationsStore().unreadNotificationsCount"
|
v-else-if="unreadNotificationsCount"
|
||||||
class="absolute -left-1.5 top-1 z-20 h-[5px] w-[5px] translate-x-6 translate-y-1 rounded-full bg-gray-800 ring-1 ring-white"
|
class="absolute -left-1.5 top-1 z-20 h-[5px] w-[5px] translate-x-6 translate-y-1 rounded-full bg-gray-800 ring-1 ring-white"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
@ -112,7 +109,10 @@ import NotificationsIcon from '@/components/Icons/NotificationsIcon.vue'
|
|||||||
import SidebarLink from '@/components/SidebarLink.vue'
|
import SidebarLink from '@/components/SidebarLink.vue'
|
||||||
import Notifications from '@/components/Notifications.vue'
|
import Notifications from '@/components/Notifications.vue'
|
||||||
import { viewsStore } from '@/stores/views'
|
import { viewsStore } from '@/stores/views'
|
||||||
import { notificationsStore } from '@/stores/notifications'
|
import {
|
||||||
|
unreadNotificationsCount,
|
||||||
|
notificationsStore,
|
||||||
|
} from '@/stores/notifications'
|
||||||
import { FeatherIcon } from 'frappe-ui'
|
import { FeatherIcon } from 'frappe-ui'
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
import { computed, h } from 'vue'
|
import { computed, h } from 'vue'
|
||||||
|
|||||||
@ -25,8 +25,8 @@
|
|||||||
>
|
>
|
||||||
<template #right>
|
<template #right>
|
||||||
<Badge
|
<Badge
|
||||||
v-if="notificationsStore().unreadNotificationsCount"
|
v-if="unreadNotificationsCount"
|
||||||
:label="notificationsStore().unreadNotificationsCount"
|
:label="unreadNotificationsCount"
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
@ -101,7 +101,7 @@ import PhoneIcon from '@/components/Icons/PhoneIcon.vue'
|
|||||||
import NotificationsIcon from '@/components/Icons/NotificationsIcon.vue'
|
import NotificationsIcon from '@/components/Icons/NotificationsIcon.vue'
|
||||||
import SidebarLink from '@/components/SidebarLink.vue'
|
import SidebarLink from '@/components/SidebarLink.vue'
|
||||||
import { viewsStore } from '@/stores/views'
|
import { viewsStore } from '@/stores/views'
|
||||||
import { notificationsStore } from '@/stores/notifications'
|
import { unreadNotificationsCount } from '@/stores/notifications'
|
||||||
import { computed, h } from 'vue'
|
import { computed, h } from 'vue'
|
||||||
import { mobileSidebarOpened as sidebarOpened } from '@/composables/settings'
|
import { mobileSidebarOpened as sidebarOpened } from '@/composables/settings'
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-if="notificationsStore().visible"
|
v-if="visible"
|
||||||
ref="target"
|
ref="target"
|
||||||
class="absolute z-20 h-screen bg-white transition-all duration-300 ease-in-out"
|
class="absolute z-20 h-screen bg-white transition-all duration-300 ease-in-out"
|
||||||
:style="{
|
:style="{
|
||||||
@ -27,7 +27,7 @@
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip :text="__('Close')">
|
<Tooltip :text="__('Close')">
|
||||||
<div>
|
<div>
|
||||||
<Button variant="ghost" @click="() => toggleNotificationPanel()">
|
<Button variant="ghost" @click="() => toggle()">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<FeatherIcon name="x" class="h-4 w-4" />
|
<FeatherIcon name="x" class="h-4 w-4" />
|
||||||
</template>
|
</template>
|
||||||
@ -37,11 +37,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="notificationsStore().allNotifications?.length"
|
v-if="notifications.data?.length"
|
||||||
class="divide-y overflow-auto text-base"
|
class="divide-y overflow-auto text-base"
|
||||||
>
|
>
|
||||||
<RouterLink
|
<RouterLink
|
||||||
v-for="n in notificationsStore().allNotifications"
|
v-for="n in notifications.data"
|
||||||
:key="n.comment"
|
:key="n.comment"
|
||||||
:to="getRoute(n)"
|
:to="getRoute(n)"
|
||||||
class="flex cursor-pointer items-start gap-2.5 px-4 py-2.5 hover:bg-gray-100"
|
class="flex cursor-pointer items-start gap-2.5 px-4 py-2.5 hover:bg-gray-100"
|
||||||
@ -91,7 +91,11 @@ import WhatsAppIcon from '@/components/Icons/WhatsAppIcon.vue'
|
|||||||
import MarkAsDoneIcon from '@/components/Icons/MarkAsDoneIcon.vue'
|
import MarkAsDoneIcon from '@/components/Icons/MarkAsDoneIcon.vue'
|
||||||
import NotificationsIcon from '@/components/Icons/NotificationsIcon.vue'
|
import NotificationsIcon from '@/components/Icons/NotificationsIcon.vue'
|
||||||
import UserAvatar from '@/components/UserAvatar.vue'
|
import UserAvatar from '@/components/UserAvatar.vue'
|
||||||
import { notificationsStore } from '@/stores/notifications'
|
import {
|
||||||
|
visible,
|
||||||
|
notifications,
|
||||||
|
notificationsStore,
|
||||||
|
} from '@/stores/notifications'
|
||||||
import { globalStore } from '@/stores/global'
|
import { globalStore } from '@/stores/global'
|
||||||
import { timeAgo } from '@/utils'
|
import { timeAgo } from '@/utils'
|
||||||
import { onClickOutside } from '@vueuse/core'
|
import { onClickOutside } from '@vueuse/core'
|
||||||
@ -100,32 +104,27 @@ import { Tooltip } from 'frappe-ui'
|
|||||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||||
|
|
||||||
const { $socket } = globalStore()
|
const { $socket } = globalStore()
|
||||||
|
const { mark_as_read, toggle, mark_doc_as_read } = notificationsStore()
|
||||||
|
|
||||||
const target = ref(null)
|
const target = ref(null)
|
||||||
onClickOutside(
|
onClickOutside(
|
||||||
target,
|
target,
|
||||||
() => {
|
() => {
|
||||||
if (notificationsStore().visible) {
|
if (visible.value) toggle()
|
||||||
toggleNotificationPanel()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ignore: ['#notifications-btn'],
|
ignore: ['#notifications-btn'],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
function toggleNotificationPanel() {
|
|
||||||
notificationsStore().toggle()
|
|
||||||
}
|
|
||||||
|
|
||||||
function markAsRead(doc) {
|
function markAsRead(doc) {
|
||||||
capture('notification_mark_as_read')
|
capture('notification_mark_as_read')
|
||||||
notificationsStore().mark_doc_as_read(doc)
|
mark_doc_as_read(doc)
|
||||||
}
|
}
|
||||||
|
|
||||||
function markAllAsRead() {
|
function markAllAsRead() {
|
||||||
capture('notification_mark_all_as_read')
|
capture('notification_mark_all_as_read')
|
||||||
notificationsStore().mark_as_read.reload()
|
mark_as_read.reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
@ -134,7 +133,7 @@ onBeforeUnmount(() => {
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
$socket.on('crm_notification', () => {
|
$socket.on('crm_notification', () => {
|
||||||
notificationsStore().notifications.reload()
|
notifications.reload()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -154,6 +153,4 @@ function getRoute(notification) {
|
|||||||
hash: notification.hash,
|
hash: notification.hash,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {})
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
:label="__('Mark all as read')"
|
:label="__('Mark all as read')"
|
||||||
@click="() => notificationsStore().mark_as_read.reload()"
|
@click="() => mark_as_read.reload()"
|
||||||
>
|
>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<MarkAsDoneIcon class="h-4 w-4" />
|
<MarkAsDoneIcon class="h-4 w-4" />
|
||||||
@ -24,15 +24,15 @@
|
|||||||
</LayoutHeader>
|
</LayoutHeader>
|
||||||
<div class="flex flex-col overflow-hidden">
|
<div class="flex flex-col overflow-hidden">
|
||||||
<div
|
<div
|
||||||
v-if="notificationsStore().allNotifications?.length"
|
v-if="notifications.data?.length"
|
||||||
class="divide-y overflow-y-auto text-base"
|
class="divide-y overflow-y-auto text-base"
|
||||||
>
|
>
|
||||||
<RouterLink
|
<RouterLink
|
||||||
v-for="n in notificationsStore().allNotifications"
|
v-for="n in notifications.data"
|
||||||
:key="n.comment"
|
:key="n.comment"
|
||||||
:to="getRoute(n)"
|
:to="getRoute(n)"
|
||||||
class="flex cursor-pointer items-start gap-3 px-2.5 py-3 hover:bg-gray-100"
|
class="flex cursor-pointer items-start gap-3 px-2.5 py-3 hover:bg-gray-100"
|
||||||
@click="mark_as_read(n.comment || n.notification_type_doc)"
|
@click="mark_doc_as_read(n.comment || n.notification_type_doc)"
|
||||||
>
|
>
|
||||||
<div class="mt-1 flex items-center gap-2.5">
|
<div class="mt-1 flex items-center gap-2.5">
|
||||||
<div
|
<div
|
||||||
@ -75,17 +75,14 @@ import WhatsAppIcon from '@/components/Icons/WhatsAppIcon.vue'
|
|||||||
import MarkAsDoneIcon from '@/components/Icons/MarkAsDoneIcon.vue'
|
import MarkAsDoneIcon from '@/components/Icons/MarkAsDoneIcon.vue'
|
||||||
import NotificationsIcon from '@/components/Icons/NotificationsIcon.vue'
|
import NotificationsIcon from '@/components/Icons/NotificationsIcon.vue'
|
||||||
import UserAvatar from '@/components/UserAvatar.vue'
|
import UserAvatar from '@/components/UserAvatar.vue'
|
||||||
import { notificationsStore } from '@/stores/notifications'
|
import { notifications, notificationsStore } from '@/stores/notifications'
|
||||||
import { globalStore } from '@/stores/global'
|
import { globalStore } from '@/stores/global'
|
||||||
import { timeAgo } from '@/utils'
|
import { timeAgo } from '@/utils'
|
||||||
import { Breadcrumbs, Tooltip } from 'frappe-ui'
|
import { Breadcrumbs, Tooltip } from 'frappe-ui'
|
||||||
import { onMounted, onBeforeUnmount } from 'vue'
|
import { onMounted, onBeforeUnmount } from 'vue'
|
||||||
|
|
||||||
const { $socket } = globalStore()
|
const { $socket } = globalStore()
|
||||||
|
const { mark_as_read, mark_doc_as_read } = notificationsStore()
|
||||||
function mark_as_read(doc) {
|
|
||||||
notificationsStore().mark_doc_as_read(doc)
|
|
||||||
}
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
$socket.off('crm_notification')
|
$socket.off('crm_notification')
|
||||||
@ -93,7 +90,7 @@ onBeforeUnmount(() => {
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
$socket.on('crm_notification', () => {
|
$socket.on('crm_notification', () => {
|
||||||
notificationsStore().notifications.reload()
|
notifications.reload()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -2,18 +2,21 @@ import { defineStore } from 'pinia'
|
|||||||
import { createResource } from 'frappe-ui'
|
import { createResource } from 'frappe-ui'
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
|
|
||||||
|
export const visible = ref(false)
|
||||||
|
|
||||||
|
export const notifications = createResource({
|
||||||
|
url: 'crm.api.notifications.get_notifications',
|
||||||
|
initialData: [],
|
||||||
|
auto: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const unreadNotificationsCount = computed(
|
||||||
|
() => notifications.data?.filter((n) => !n.read).length || 0,
|
||||||
|
)
|
||||||
|
|
||||||
export const notificationsStore = defineStore('crm-notifications', () => {
|
export const notificationsStore = defineStore('crm-notifications', () => {
|
||||||
let visible = ref(false)
|
|
||||||
|
|
||||||
const notifications = createResource({
|
|
||||||
url: 'crm.api.notifications.get_notifications',
|
|
||||||
initialData: [],
|
|
||||||
auto: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
const mark_as_read = createResource({
|
const mark_as_read = createResource({
|
||||||
url: 'crm.api.notifications.mark_as_read',
|
url: 'crm.api.notifications.mark_as_read',
|
||||||
auto: false,
|
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
mark_as_read.params = {}
|
mark_as_read.params = {}
|
||||||
notifications.reload()
|
notifications.reload()
|
||||||
@ -24,11 +27,6 @@ export const notificationsStore = defineStore('crm-notifications', () => {
|
|||||||
visible.value = !visible.value
|
visible.value = !visible.value
|
||||||
}
|
}
|
||||||
|
|
||||||
const allNotifications = computed(() => notifications.data || [])
|
|
||||||
const unreadNotificationsCount = computed(
|
|
||||||
() => notifications.data?.filter((n) => !n.read).length || 0
|
|
||||||
)
|
|
||||||
|
|
||||||
function mark_doc_as_read(doc) {
|
function mark_doc_as_read(doc) {
|
||||||
mark_as_read.params = { doc: doc }
|
mark_as_read.params = { doc: doc }
|
||||||
mark_as_read.reload()
|
mark_as_read.reload()
|
||||||
@ -36,9 +34,6 @@ export const notificationsStore = defineStore('crm-notifications', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
notifications,
|
|
||||||
visible,
|
|
||||||
allNotifications,
|
|
||||||
unreadNotificationsCount,
|
unreadNotificationsCount,
|
||||||
mark_as_read,
|
mark_as_read,
|
||||||
mark_doc_as_read,
|
mark_doc_as_read,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user