/** * Notification Store – Polls unread count and notifications from the API. */ import { defineStore } from 'pinia' import { apiFetch } from '../services/api' export interface NotificationItem { id: string type: string // "task_assigned", "task_review", "task_blocked" title: string message: string | null forUser: string taskId: string | null isRead: boolean createdAt: string } export interface UnreadCount { count: number } export const useNotificationStore = defineStore('notifications', { state: () => ({ notifications: [] as NotificationItem[], unreadCount: 0, loading: false, error: null as string | null, countRefreshInterval: null as ReturnType | null, listRefreshInterval: null as ReturnType | null, }), actions: { async fetchNotifications(forUser = 'bao', limit = 50, unreadOnly = false) { this.loading = true try { const params = new URLSearchParams({ forUser, limit: String(limit), unreadOnly: String(unreadOnly) }) const res = await apiFetch(`/api/dashboard/notifications?${params}`) if (!res.ok) throw new Error(`HTTP ${res.status}`) this.notifications = await res.json() this.error = null } catch (err) { console.warn('[NotificationStore] fetchNotifications failed', err) this.error = 'Notifications could not be loaded' } finally { this.loading = false } }, async fetchUnreadCount(forUser = 'bao') { try { const params = new URLSearchParams({ forUser }) const res = await apiFetch(`/api/dashboard/notifications/unread-count?${params}`) if (!res.ok) throw new Error(`HTTP ${res.status}`) const data: UnreadCount = await res.json() this.unreadCount = data.count } catch (err) { console.warn('[NotificationStore] fetchUnreadCount failed', err) } }, async markAsRead(id: string) { try { const res = await apiFetch(`/api/dashboard/notifications/${id}/read`, { method: 'PATCH' }) if (!res.ok) throw new Error(`HTTP ${res.status}`) // Update local state const n = this.notifications.find(n => n.id === id) if (n) n.isRead = true this.unreadCount = Math.max(0, this.unreadCount - 1) } catch (err) { console.warn('[NotificationStore] markAsRead failed', err) } }, async markAllAsRead(forUser = 'bao') { try { const params = new URLSearchParams({ forUser }) const res = await apiFetch(`/api/dashboard/notifications/read-all?${params}`, { method: 'PATCH' }) if (!res.ok) throw new Error(`HTTP ${res.status}`) this.notifications.forEach(n => { n.isRead = true }) this.unreadCount = 0 } catch (err) { console.warn('[NotificationStore] markAllAsRead failed', err) } }, startPolling(forUser = 'bao') { if (!this.countRefreshInterval) { this.fetchUnreadCount(forUser) this.countRefreshInterval = setInterval(() => { this.fetchUnreadCount(forUser) }, 30000) } }, stopPolling() { if (this.countRefreshInterval) { clearInterval(this.countRefreshInterval) this.countRefreshInterval = null } if (this.listRefreshInterval) { clearInterval(this.listRefreshInterval) this.listRefreshInterval = null } }, startListPolling(forUser = 'bao') { if (!this.listRefreshInterval) { this.fetchNotifications(forUser) this.listRefreshInterval = setInterval(() => { this.fetchNotifications(forUser) }, 30000) } }, stopListPolling() { if (this.listRefreshInterval) { clearInterval(this.listRefreshInterval) this.listRefreshInterval = null } }, }, })