Files
nexus/frontend/src/stores/notifications.ts
T
devops df94ed3cd4
CI - Build & Test / Backend (.NET) (push) Successful in 1m19s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 18s
CI - Build & Test / Security Check (push) Successful in 3s
feat: board-first orchestration with Gateway Bridge, live-update, and flow-board
- GatewayBridgeController: MCP-artiger Kommando-Adapter für Agent-zu-Backend
- TaskBridgeService + LiveUpdateService: SSE Live-Sync + Bridge-Kommandos
- FlowBoard.vue: Board-first orchestration dashboard panel
- live-sync.ts store + live.ts service: SSE-basierte Live-Updates
- Nullability-Warnung in HealthController.cs gefixt
- nginx.conf: SSE-Proxy + CORS für Bridge-Endpunkte
- .gitignore: pnpm/corepack local caches ausgeschlossen
- docs: architecture-board-first-orchestration.md hinzugefügt
- README: Backend Bridge API dokumentiert
2026-06-22 19:56:45 +02:00

123 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') {
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
}
},
},
})