feat: notification on mentions

This commit is contained in:
Jannat Patel
2024-05-23 21:25:22 +05:30
parent f38aebbc9c
commit a748e2c2db
8 changed files with 212 additions and 40 deletions

View File

@@ -10,7 +10,7 @@
<UserDropdown class="p-2" :isCollapsed="isSidebarCollapsed" />
<div class="flex flex-col overflow-y-auto">
<SidebarLink
v-for="link in links"
v-for="link in sidebarLinks"
:link="link"
:isCollapsed="isSidebarCollapsed"
class="mx-2 my-0.5"
@@ -42,22 +42,53 @@ import UserDropdown from '@/components/UserDropdown.vue'
import CollapseSidebar from '@/components/Icons/CollapseSidebar.vue'
import SidebarLink from '@/components/SidebarLink.vue'
import { useStorage } from '@vueuse/core'
import { ref } from 'vue'
import { ref, onMounted, inject, computed } from 'vue'
import { getSidebarLinks } from '../utils'
import { sessionStore } from '@/stores/session'
import { Bell } from 'lucide-vue-next'
import { createResource } from 'frappe-ui'
const { user } = sessionStore()
const links = getSidebarLinks()
const socket = inject('$socket')
const unreadCount = ref(0)
if (user) {
links.push({
label: 'Notifications',
icon: Bell,
to: 'Notifications',
activeFor: ['Notifications'],
onMounted(() => {
socket.on('publish_lms_notifications', (data) => {
unreadNotifications.reload()
})
}
})
const unreadNotifications = createResource({
cache: 'Unread Notifications Count',
url: 'frappe.client.get_count',
makeParams(values) {
return {
doctype: 'Notification Log',
filters: {
for_user: user,
read: 0,
},
}
},
onSuccess(data) {
unreadCount.value = data
},
auto: true,
})
const sidebarLinks = computed(() => {
const links = getSidebarLinks()
if (user) {
links.push({
label: 'Notifications',
icon: Bell,
to: 'Notifications',
activeFor: ['Notifications'],
count: unreadCount.value,
})
}
return links
})
const getSidebarFromStorage = () => {
return useStorage('sidebar_is_collapsed', false)

View File

@@ -151,7 +151,16 @@ const newReplyResource = createResource({
})
const mentionUsers = computed(() => {
return allUsers.data /* [{
console.log(allUsers.data['jannat@frappe.io'])
let users = Object.values(allUsers.data).map((user) => {
return {
value: user.name,
label: user.full_name,
}
})
return users
/* [{
label: "jannat",
value: "jannat"
}, {

View File

@@ -6,7 +6,7 @@
@click="handleClick"
>
<div
class="flex items-center duration-300 ease-in-out"
class="flex items-center w-full duration-300 ease-in-out"
:class="isCollapsed ? 'p-1' : 'px-2 py-1'"
>
<Tooltip :text="link.label" placement="right">
@@ -29,6 +29,9 @@
>
{{ link.label }}
</span>
<span v-if="link.count" class="!ml-auto block text-xs text-gray-600">
{{ link.count }}
</span>
</div>
</button>
</template>

View File

@@ -57,6 +57,14 @@
v-if="tab.courses && tab.courses.value.length"
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5 my-5 mx-5"
>
<!-- <div v-for="course in tab.courses.value">
{{ course.membership }}
{{ course.current_lesson }}
<div v-if="course.current_lesson">
{{ course.current_lesson.split('-')[0] }}
{{ course.current_lesson.split('-')[1] }}
</div>
</div> -->
<router-link
v-for="course in tab.courses.value"
:to="
@@ -65,8 +73,8 @@
name: 'Lesson',
params: {
courseName: course.name,
chapterNumber: course.current_lesson.split('.')[0],
lessonNumber: course.current_lesson.split('.')[1],
chapterNumber: course.current_lesson.split('-')[0],
lessonNumber: course.current_lesson.split('-')[1],
},
}
: course.membership

View File

@@ -3,48 +3,136 @@
class="sticky top-0 z-10 flex flex-col md:flex-row md:items-center justify-between border-b bg-white px-3 py-2.5 sm:px-5"
>
<Breadcrumbs :items="breadcrumbs" />
<div class="flex items-center space-x-2">
<Button
@click="markAllAsRead.submit"
:loading="markAllAsRead.loading"
v-if="activeTab === 'Unread' && unReadNotifications.data?.length > 0"
>
{{ __('Mark all as read') }}
</Button>
<TabButtons
class="inline-block"
:buttons="[{ label: 'Unread', active: true }, { label: 'Read' }]"
v-model="activeTab"
/>
</div>
</header>
<div class="w-3/4 mx-auto">
<div class="w-3/4 mx-auto px-5 pt-6 divide-y">
<div
v-for="log in notifications.data"
class="flex items-center border-b py-2 justify-between"
v-if="notifications?.length"
v-for="log in notifications"
class="flex items-center py-2 justify-between"
>
<div class="flex items-center">
<UserAvatar :user="allUsers.data[log.from_user]" class="mr-2" />
<div class="notification" v-html="log.subject"></div>
</div>
<Link
v-if="log.link"
:to="log.link"
class="text-gray-600 font-medium text-sm hover:text-gray-700"
>
{{ __('View') }}
</Link>
<div class="flex items-center space-x-2">
<Link
v-if="log.link"
:to="log.link"
@click="markAsRead.submit({ name: log.name })"
class="text-gray-600 font-medium text-sm hover:text-gray-700"
>
{{ __('View') }}
</Link>
<Tooltip :text="__('Mark as read')">
<Button
variant="ghost"
v-if="!log.read"
@click="markAsRead.submit({ name: log.name })"
>
<template #icon>
<X class="h-4 w-4 text-gray-700 stroke-1.5" />
</template>
</Button>
</Tooltip>
</div>
</div>
<div v-else class="text-gray-600">
{{ __('Nothing to see here.') }}
</div>
</div>
</template>
<script setup>
import { createResource, Breadcrumbs, Link } from 'frappe-ui'
import { computed, inject } from 'vue'
import {
createListResource,
createResource,
Breadcrumbs,
Link,
TabButtons,
Button,
Tooltip,
} from 'frappe-ui'
import { computed, inject, ref, onMounted } from 'vue'
import UserAvatar from '@/components/UserAvatar.vue'
import { useRouter } from 'vue-router'
import { X } from 'lucide-vue-next'
const user = inject('$user')
const socket = inject('$socket')
const allUsers = inject('$allUsers')
const activeTab = ref('Unread')
const router = useRouter()
const notifications = createResource({
url: 'frappe.client.get_list',
makeParams: (values) => {
onMounted(() => {
if (!user.data) router.push({ name: 'Courses' })
socket.on('publish_lms_notifications', (data) => {
unReadNotifications.reload()
})
})
const notifications = computed(() => {
return activeTab.value === 'Unread'
? unReadNotifications.data
: readNotifications.data
})
const unReadNotifications = createListResource({
doctype: 'Notification Log',
fields: ['subject', 'from_user', 'link', 'read', 'name'],
filters: {
for_user: user.data?.name,
read: 0,
},
orderBy: 'creation desc',
auto: true,
cache: 'Unread Notifications',
})
const readNotifications = createListResource({
doctype: 'Notification Log',
fields: ['subject', 'from_user', 'link', 'read', 'name'],
filters: {
for_user: user.data?.name,
read: 1,
},
orderBy: 'creation desc',
auto: true,
cache: 'Read Notifications',
})
const markAsRead = createResource({
url: 'frappe.desk.doctype.notification_log.notification_log.mark_as_read',
makeParams(values) {
return {
doctype: 'Notification Log',
fields: ['subject', 'from_user', 'link'],
filters: {
for_user: user.data?.name,
},
order_by: 'creation desc',
docname: values.name,
}
},
auto: true,
cache: user.data?.name,
onSuccess(data) {
unReadNotifications.reload()
readNotifications.reload()
},
})
const markAllAsRead = createResource({
url: 'frappe.desk.doctype.notification_log.notification_log.mark_all_as_read',
onSuccess(data) {
unReadNotifications.reload()
readNotifications.reload()
},
})
const breadcrumbs = computed(() => {