fix(PBI-59): map jobs_initial SSE payload by job_id, not id

De server-route stuurt JobPayload[] (met `job_id`), maar de client deed
`initJobs(jobs, ...)` waardoor alle entries in activeJobs `id: undefined`
kregen — wat React-key warnings opleverde:

  Each child in a list should have a unique "key" prop.

Fix: SSE jobs_initial niet meer als overwrite gebruiken; SSR-fetch heeft
de volledige JobWithRelations al in de store gezet. We reconcileren nu
per job met upsertJob (status/branch/error/summary updaten van bekende
jobs, onbekende jobs als partials toevoegen — zelfde gedrag als gewone
'message' SSE events).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Janpeter Visser 2026-05-07 20:21:13 +02:00
parent 00dbbb4f94
commit 74efdff7c9

View file

@ -18,7 +18,6 @@ interface JobStatusPayload {
}
export default function useJobsRealtime() {
const initJobs = useJobsStore(s => s.initJobs)
const upsertJob = useJobsStore(s => s.upsertJob)
useEffect(() => {
@ -32,10 +31,23 @@ export default function useJobsRealtime() {
es = new EventSource('/api/realtime/jobs')
es.addEventListener('jobs_initial', (event) => {
// De server stuurt JobPayload[] (met `job_id`), niet JobWithRelations[].
// Daarom geen initJobs-overwrite — de SSR-fetch heeft de volledige
// shape al in de store geplaatst. We reconcileren alleen status/branch
// van bekende jobs en pushen onbekende jobs (nieuw aangemaakt tussen
// SSR en SSE-connect) als partials.
try {
const jobs = JSON.parse(event.data)
if (Array.isArray(jobs)) {
initJobs(jobs, useJobsStore.getState().doneJobs)
const payload = JSON.parse(event.data)
if (!Array.isArray(payload)) return
for (const p of payload as JobStatusPayload[]) {
if (!p.job_id) continue
upsertJob({
id: p.job_id,
status: p.status as ClaudeJobStatus,
branch: p.branch ?? null,
error: p.error ?? null,
summary: p.summary ?? null,
})
}
} catch {
// malformed JSON
@ -75,5 +87,5 @@ export default function useJobsRealtime() {
if (reconnectTimer) clearTimeout(reconnectTimer)
es?.close()
}
}, [initJobs, upsertJob])
}, [upsertJob])
}