From 74efdff7c9cd60b0e2c0f893032d530aa03fecbc Mon Sep 17 00:00:00 2001 From: Madhura68 Date: Thu, 7 May 2026 20:21:13 +0200 Subject: [PATCH] fix(PBI-59): map jobs_initial SSE payload by job_id, not id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- hooks/use-jobs-realtime.ts | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/hooks/use-jobs-realtime.ts b/hooks/use-jobs-realtime.ts index f85b5c5..b15cd0a 100644 --- a/hooks/use-jobs-realtime.ts +++ b/hooks/use-jobs-realtime.ts @@ -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]) }