feat(v2): Pinia stores (agents/tasks/chat) + live backend integration, remove mock data
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* Task Store – V2 Dashboard
|
||||
*
|
||||
* Fetches tasks from /api/dashboard/tasks and maps them into
|
||||
* TaskItem[] format for the TaskStrip component.
|
||||
*
|
||||
* Auto-refresh: every 30 seconds.
|
||||
*/
|
||||
import { defineStore } from 'pinia'
|
||||
import { apiFetch } from '../services/api'
|
||||
import type { TaskItem } from '../components/dashboard/v2/types'
|
||||
|
||||
/* ── API Response Shapes ──────────────────────────── */
|
||||
|
||||
interface DashboardTaskDto {
|
||||
id: string
|
||||
title: string
|
||||
detail: string | null
|
||||
source: string
|
||||
state: string
|
||||
priority: string
|
||||
assignedTo: string | null
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
/* ── State Mapping ────────────────────────────────── */
|
||||
|
||||
function mapPriority(priority: string): TaskItem['priority'] {
|
||||
const p = priority.toLowerCase()
|
||||
if (p === 'high' || p === 'critical' || p === 'urgent') return 'high'
|
||||
if (p === 'low' || p === 'minor') return 'low'
|
||||
return 'medium'
|
||||
}
|
||||
|
||||
function mapState(state: string): TaskItem['status'] {
|
||||
const s = state.toLowerCase()
|
||||
if (s === 'in progress' || s === 'active' || s === 'working') return 'active'
|
||||
if (s === 'blocked' || s === 'block') return 'blocked'
|
||||
return 'pending'
|
||||
}
|
||||
|
||||
function mapProgress(state: string): number {
|
||||
const s = state.toLowerCase()
|
||||
if (s === 'in progress' || s === 'active' || s === 'working') return 50
|
||||
if (s === 'done') return 100
|
||||
if (s === 'blocked') return 30
|
||||
return 0
|
||||
}
|
||||
|
||||
function mapTask(t: DashboardTaskDto): TaskItem {
|
||||
return {
|
||||
id: t.id,
|
||||
title: t.title,
|
||||
agent: t.assignedTo ?? '—',
|
||||
priority: mapPriority(t.priority),
|
||||
status: mapState(t.state),
|
||||
progress: mapProgress(t.state),
|
||||
}
|
||||
}
|
||||
|
||||
export const useTaskStore = defineStore('tasks', {
|
||||
state: () => ({
|
||||
tasks: [] as TaskItem[],
|
||||
loading: false,
|
||||
error: null as string | null,
|
||||
refreshInterval: null as ReturnType<typeof setInterval> | null,
|
||||
}),
|
||||
|
||||
getters: {
|
||||
taskList: (state) => state.tasks,
|
||||
},
|
||||
|
||||
actions: {
|
||||
/* ── API: Fetch tasks ───────────────────────── */
|
||||
async fetchTasks() {
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await apiFetch('/api/dashboard/tasks')
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`)
|
||||
const data: DashboardTaskDto[] = await res.json()
|
||||
this.tasks = data.map(mapTask)
|
||||
this.error = null
|
||||
} catch (err) {
|
||||
console.warn('[TaskStore] fetchTasks failed', err)
|
||||
this.error = 'Tasks could not be loaded'
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
/* ── API: Add task ──────────────────────────── */
|
||||
async addTask(title: string, detail?: string, priority?: string, assignedTo?: string) {
|
||||
try {
|
||||
const res = await apiFetch('/api/dashboard/tasks', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
title,
|
||||
detail: detail ?? null,
|
||||
priority: priority ?? null,
|
||||
assignedTo: assignedTo ?? null,
|
||||
source: 'bao',
|
||||
}),
|
||||
})
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`)
|
||||
// Refresh task list
|
||||
await this.fetchTasks()
|
||||
} catch (err) {
|
||||
console.warn('[TaskStore] addTask failed', err)
|
||||
}
|
||||
},
|
||||
|
||||
/* ── API: Update task ───────────────────────── */
|
||||
async updateTask(id: string, updates: { title?: string; detail?: string; priority?: string; assignedTo?: string }) {
|
||||
try {
|
||||
const res = await apiFetch(`/api/dashboard/tasks/${id}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(updates),
|
||||
})
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`)
|
||||
await this.fetchTasks()
|
||||
} catch (err) {
|
||||
console.warn('[TaskStore] updateTask failed', err)
|
||||
}
|
||||
},
|
||||
|
||||
/* ── Polling ─────────────────────────────────── */
|
||||
startPolling() {
|
||||
if (this.refreshInterval) return
|
||||
this.fetchTasks()
|
||||
this.refreshInterval = setInterval(() => {
|
||||
this.fetchTasks()
|
||||
}, 30000)
|
||||
},
|
||||
|
||||
stopPolling() {
|
||||
if (this.refreshInterval) {
|
||||
clearInterval(this.refreshInterval)
|
||||
this.refreshInterval = null
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user