Files
nexus/frontend/src/stores/notifications.ts
T
devops dcc8450c62
CI - Build & Test / Backend (.NET) (push) Successful in 37s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 24s
CI - Build & Test / Security Check (push) Successful in 4s
feat: Phase 2 — Delegated State, Auth, Review-Gate, Notifications, Zombie-Reset
2026-06-18 23:47:41 +02:00

124 lines
3.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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<typeof setInterval> | null,
listRefreshInterval: null as ReturnType<typeof setInterval> | 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') {
// Unread count polling every 30s (for sidebar badge)
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
}
},
},
})