feat(PBI-59): useJobsRealtime hook met SSE-verbinding en store-updates
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7c49ff0edd
commit
e276cac922
1 changed files with 79 additions and 0 deletions
79
hooks/use-jobs-realtime.ts
Normal file
79
hooks/use-jobs-realtime.ts
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
import { useEffect } from 'react'
|
||||
import { useJobsStore } from '@/stores/jobs-store'
|
||||
import type { ClaudeJobStatus } from '@prisma/client'
|
||||
|
||||
interface JobStatusPayload {
|
||||
job_id: string
|
||||
kind?: string
|
||||
status: string
|
||||
task_id?: string | null
|
||||
idea_id?: string | null
|
||||
sprint_run_id?: string | null
|
||||
branch?: string
|
||||
pushed_at?: string
|
||||
pr_url?: string
|
||||
verify_result?: string
|
||||
summary?: string
|
||||
error?: string
|
||||
}
|
||||
|
||||
export default function useJobsRealtime() {
|
||||
const initJobs = useJobsStore(s => s.initJobs)
|
||||
const upsertJob = useJobsStore(s => s.upsertJob)
|
||||
|
||||
useEffect(() => {
|
||||
let es: EventSource | null = null
|
||||
let reconnectTimer: ReturnType<typeof setTimeout> | null = null
|
||||
let active = true
|
||||
|
||||
function connect() {
|
||||
if (!active) return
|
||||
|
||||
es = new EventSource('/api/realtime/jobs')
|
||||
|
||||
es.addEventListener('jobs_initial', (event) => {
|
||||
try {
|
||||
const jobs = JSON.parse(event.data)
|
||||
if (Array.isArray(jobs)) {
|
||||
initJobs(jobs, useJobsStore.getState().doneJobs)
|
||||
}
|
||||
} catch {
|
||||
// malformed JSON
|
||||
}
|
||||
})
|
||||
|
||||
es.addEventListener('message', (event) => {
|
||||
try {
|
||||
const payload = JSON.parse(event.data) as JobStatusPayload
|
||||
if (!payload.job_id) return
|
||||
upsertJob({
|
||||
id: payload.job_id,
|
||||
status: payload.status as ClaudeJobStatus,
|
||||
branch: payload.branch ?? null,
|
||||
prUrl: payload.pr_url ?? null,
|
||||
error: payload.error ?? null,
|
||||
summary: payload.summary ?? null,
|
||||
})
|
||||
} catch {
|
||||
// malformed JSON
|
||||
}
|
||||
})
|
||||
|
||||
es.onerror = () => {
|
||||
es?.close()
|
||||
es = null
|
||||
if (active) {
|
||||
reconnectTimer = setTimeout(connect, 3000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
connect()
|
||||
|
||||
return () => {
|
||||
active = false
|
||||
if (reconnectTimer) clearTimeout(reconnectTimer)
|
||||
es?.close()
|
||||
}
|
||||
}, [initJobs, upsertJob])
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue