194 lines
5.5 KiB
TypeScript
194 lines
5.5 KiB
TypeScript
import { defineStore } from 'pinia'
|
|
import type { NotificationDto } from '~~/.api-client'
|
|
import { useNotificationApi } from '~/composables/notification/useNotificationApi'
|
|
import { useLogger } from '~/composables/useLogger'
|
|
import { useUserStore } from './useUserStore'
|
|
|
|
export const useNotificationStore = defineStore('Notification', () => {
|
|
const logger = useLogger().withTag('notificationStore')
|
|
const userStore = useUserStore()
|
|
const { user } = useUserSession()
|
|
|
|
const {
|
|
getNotifications,
|
|
getUnreadNotifications,
|
|
getUnreadNotificationCount,
|
|
markAllNotificationsAsRead,
|
|
markNotificationAsRead,
|
|
clearAllNotifications,
|
|
deleteNotification
|
|
} = useNotificationApi()
|
|
|
|
// State
|
|
const notifications = ref<NotificationDto[]>([])
|
|
const unreadNotifications = ref<NotificationDto[]>([])
|
|
const unreadCount = ref<number>(0)
|
|
const isLoading = ref(false)
|
|
|
|
// Getters
|
|
const hasUnread = computed(() => unreadCount.value > 0)
|
|
const organizationId = computed(() => userStore.selectedOrganization?.id)
|
|
const userId = computed(() => user.value?.keycloakId)
|
|
|
|
// Actions
|
|
async function fetchNotifications(page: number = 0, size: number = 20) {
|
|
if (!organizationId.value) {
|
|
logger.warn('No organization selected')
|
|
return
|
|
}
|
|
isLoading.value = true
|
|
try {
|
|
const response = await getNotifications(organizationId.value, page, size)
|
|
notifications.value = response.content || []
|
|
return response
|
|
} catch (error) {
|
|
logger.error('Failed to fetch notifications:', error)
|
|
throw error
|
|
} finally {
|
|
isLoading.value = false
|
|
}
|
|
}
|
|
|
|
async function fetchUnreadNotifications() {
|
|
if (!organizationId.value) {
|
|
logger.warn('No organization selected')
|
|
return
|
|
}
|
|
try {
|
|
const response = await getUnreadNotifications(organizationId.value)
|
|
unreadNotifications.value = response || []
|
|
return response
|
|
} catch (error) {
|
|
logger.error('Failed to fetch unread notifications:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
async function fetchUnreadCount() {
|
|
if (!userId.value || !organizationId.value) {
|
|
logger.warn('No user or organization selected')
|
|
return
|
|
}
|
|
try {
|
|
const count = await getUnreadNotificationCount(userId.value, organizationId.value)
|
|
unreadCount.value = count || 0
|
|
return count
|
|
} catch (error) {
|
|
logger.error('Failed to fetch unread count:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
async function markAllAsRead() {
|
|
if (!organizationId.value) {
|
|
logger.warn('No organization selected')
|
|
return
|
|
}
|
|
try {
|
|
await markAllNotificationsAsRead(organizationId.value)
|
|
unreadCount.value = 0
|
|
unreadNotifications.value = []
|
|
notifications.value = notifications.value.map((n) => ({ ...n, isRead: true }))
|
|
} catch (error) {
|
|
logger.error('Failed to mark all as read:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
async function markAsRead(notificationId: string) {
|
|
if (!organizationId.value) {
|
|
logger.warn('No organization selected')
|
|
return
|
|
}
|
|
try {
|
|
await markNotificationAsRead(notificationId, organizationId.value)
|
|
const index = notifications.value.findIndex((n) => n.id === notificationId)
|
|
if (index !== -1 && notifications.value[index]) {
|
|
notifications.value[index].isRead = true
|
|
}
|
|
// Remove from unread notifications
|
|
unreadNotifications.value = unreadNotifications.value.filter((n) => n.id !== notificationId)
|
|
|
|
if (unreadCount.value > 0) {
|
|
unreadCount.value--
|
|
}
|
|
} catch (error) {
|
|
logger.error('Failed to mark notification as read:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
async function handleNotificationClick(notification: NotificationDto) {
|
|
if (!notification.isRead) {
|
|
await markAsRead(notification.id)
|
|
}
|
|
if (notification.clickTarget) {
|
|
await navigateTo(notification.clickTarget)
|
|
}
|
|
}
|
|
|
|
function startPeriodicRefresh(intervalMs: number = 30000) {
|
|
const interval = setInterval(() => {
|
|
void fetchUnreadCount()
|
|
}, intervalMs)
|
|
|
|
onUnmounted(() => {
|
|
clearInterval(interval)
|
|
})
|
|
|
|
return interval
|
|
}
|
|
|
|
async function deleteAllNotifications() {
|
|
try {
|
|
await clearAllNotifications()
|
|
notifications.value = []
|
|
unreadNotifications.value = []
|
|
unreadCount.value = 0
|
|
} catch (error) {
|
|
logger.error('Failed to delete all notifications:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
async function deleteSingleNotification(notificationId: string) {
|
|
try {
|
|
// Check if notification was unread before deleting
|
|
const notification = notifications.value.find((n) => n.id === notificationId)
|
|
const wasUnread = notification && !notification.isRead
|
|
|
|
await deleteNotification(notificationId)
|
|
notifications.value = notifications.value.filter((n) => n.id !== notificationId)
|
|
unreadNotifications.value = unreadNotifications.value.filter((n) => n.id !== notificationId)
|
|
|
|
// Update unread count if the deleted notification was unread
|
|
if (wasUnread && unreadCount.value > 0) {
|
|
unreadCount.value--
|
|
}
|
|
} catch (error) {
|
|
logger.error('Failed to delete notification:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
return {
|
|
// State
|
|
notifications,
|
|
unreadNotifications,
|
|
unreadCount,
|
|
isLoading,
|
|
// Getters
|
|
hasUnread,
|
|
// Actions
|
|
fetchNotifications,
|
|
fetchUnreadNotifications,
|
|
fetchUnreadCount,
|
|
markAllAsRead,
|
|
markAsRead,
|
|
handleNotificationClick,
|
|
startPeriodicRefresh,
|
|
deleteAllNotifications,
|
|
deleteSingleNotification
|
|
}
|
|
})
|