chore(debug): add Layer 2 — mini Zustand-store + dispatch toggles
Test of SSE-event → store → render-pipeline werkt buiten de Solo Paneel context. Mirrort het patroon van solo-store maar minimaal. - debug-store.ts: kleine Zustand-store met tasks + applyEvent + applyCount/skipCount-tellers - store-panel.tsx: rendert store-state in een tabel met statuskleuring - client.tsx: drie layer-toggles (dispatch / flushSync / startView- Transition) + lift dispatch in onmessage. Zo kunnen we elke combinatie isoleren Bevestigd: alle drie de toggles werken op het bare /debug-realtime endpoint. Volgende laag is Server Action revalidation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
dbbd20f3a9
commit
6c2a75a1dd
3 changed files with 289 additions and 0 deletions
71
app/debug-realtime/debug-store.ts
Normal file
71
app/debug-realtime/debug-store.ts
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// Mini Zustand-store voor de debug-pagina. Mirrort het patroon van
|
||||
// stores/solo-store.ts maar dan minimaal: alleen tasks-record + applyEvent.
|
||||
// Doel: testen of de SSE-event → store → component-render keten werkt
|
||||
// zonder de complexiteit van de echte Solo Paneel.
|
||||
|
||||
import { create } from 'zustand'
|
||||
|
||||
export interface DebugTask {
|
||||
id: string
|
||||
status: string
|
||||
title: string
|
||||
story_id: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface DebugRealtimeEvent {
|
||||
op: 'I' | 'U' | 'D'
|
||||
entity: 'task' | 'story'
|
||||
id: string
|
||||
story_id?: string
|
||||
task_status?: string
|
||||
task_title?: string
|
||||
debug?: boolean
|
||||
emitted_at?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
interface DebugStore {
|
||||
tasks: Record<string, DebugTask>
|
||||
applyCount: number
|
||||
skipCount: number
|
||||
applyEvent: (event: DebugRealtimeEvent) => void
|
||||
reset: () => void
|
||||
}
|
||||
|
||||
export const useDebugStore = create<DebugStore>((set, get) => ({
|
||||
tasks: {},
|
||||
applyCount: 0,
|
||||
skipCount: 0,
|
||||
|
||||
applyEvent: (event) => {
|
||||
if (event.entity !== 'task') {
|
||||
set((s) => ({ skipCount: s.skipCount + 1 }))
|
||||
return
|
||||
}
|
||||
if (event.op === 'D') {
|
||||
set((s) => {
|
||||
const next = { ...s.tasks }
|
||||
delete next[event.id]
|
||||
return { tasks: next, applyCount: s.applyCount + 1 }
|
||||
})
|
||||
return
|
||||
}
|
||||
// INSERT/UPDATE — schrijf altijd, ongeacht of de task al bestond
|
||||
set((s) => ({
|
||||
tasks: {
|
||||
...s.tasks,
|
||||
[event.id]: {
|
||||
id: event.id,
|
||||
status: event.task_status ?? get().tasks[event.id]?.status ?? '?',
|
||||
title: event.task_title ?? get().tasks[event.id]?.title ?? '(no title)',
|
||||
story_id: event.story_id ?? get().tasks[event.id]?.story_id ?? '?',
|
||||
updated_at: new Date().toISOString(),
|
||||
},
|
||||
},
|
||||
applyCount: s.applyCount + 1,
|
||||
}))
|
||||
},
|
||||
|
||||
reset: () => set({ tasks: {}, applyCount: 0, skipCount: 0 }),
|
||||
}))
|
||||
Loading…
Add table
Add a link
Reference in a new issue