import { Novu } from '@novu/js' interface NovuNotification { id: string content: string read: boolean seen: boolean createdAt: string payload?: Record cta?: { type: string data: { url?: string } } } let novuInstance: Novu | null = null export const useNovu = () => { const config = useRuntimeConfig() const notifications = ref([]) const unreadCount = ref(0) const isLoading = ref(false) const isInitialized = ref(false) const init = (subscriberId: string) => { if (novuInstance || !subscriberId) return const appId = config.public.novuAppId const backendUrl = config.public.novuBackendUrl const socketUrl = config.public.novuSocketUrl if (!appId || !backendUrl) { console.warn('[useNovu] Missing configuration: novuAppId or novuBackendUrl') return } novuInstance = new Novu({ subscriberId, applicationIdentifier: appId, backendUrl, socketUrl: socketUrl || undefined }) isInitialized.value = true // Subscribe to realtime updates novuInstance.on('notifications.notification_received', () => { loadNotifications() }) // Load initial data loadNotifications() } const loadNotifications = async () => { if (!novuInstance) return isLoading.value = true try { const result = await novuInstance.notifications.list({ limit: 20 }) const data = Array.isArray(result?.data) ? result.data : (result?.data?.notifications || []) notifications.value = data as NovuNotification[] unreadCount.value = notifications.value.filter((n: NovuNotification) => !n.read).length } catch (error) { console.error('[useNovu] Failed to load notifications:', error) } finally { isLoading.value = false } } const markAsRead = async (notificationId: string) => { if (!novuInstance) return try { await novuInstance.notifications.read({ notificationId }) // Update locally const notification = notifications.value.find((n: NovuNotification) => n.id === notificationId) if (notification) { notification.read = true unreadCount.value = notifications.value.filter((n: NovuNotification) => !n.read).length } } catch (error) { console.error('[useNovu] Failed to mark as read:', error) } } const markAllAsRead = async () => { if (!novuInstance) return try { await novuInstance.notifications.readAll() notifications.value.forEach((n: NovuNotification) => { n.read = true }) unreadCount.value = 0 } catch (error) { console.error('[useNovu] Failed to mark all as read:', error) } } const markAsSeen = async (notificationId: string) => { if (!novuInstance) return try { await novuInstance.notifications.archive({ notificationId }) const notification = notifications.value.find((n: NovuNotification) => n.id === notificationId) if (notification) { notification.seen = true } } catch (error) { console.error('[useNovu] Failed to mark as seen:', error) } } const destroy = () => { if (novuInstance) { novuInstance = null isInitialized.value = false notifications.value = [] unreadCount.value = 0 } } return { init, destroy, notifications, unreadCount, isLoading, isInitialized, markAsRead, markAllAsRead, markAsSeen, loadNotifications } }