Wire de SSE-events uit /api/realtime/solo door naar de Zustand-store
zodat het Solo Paneel zonder refresh meebeweegt met DB-mutaties uit
welke bron dan ook (web, REST, MCP).
Migratie 20260427000216_extend_realtime_payload: voegt new-state
velden aan de pg_notify-payload toe (task_status, task_sort_order,
task_title, story_status, story_sort_order, story_title, story_code)
zodat de client geen extra fetch nodig heeft per event.
Store-uitbreiding (stores/solo-store.ts):
- pendingOps: Set<task-id> die optimistic-writes markeert; realtime
echos voor die ids worden onderdrukt zodat eigen UI-mutaties niet
twee keer toegepast worden of door een latere echo overschreven
- handleRealtimeEvent: dispatch op entity + op
- task UPDATE/INSERT: bestaande tasks krijgen status/title/sort_order
bijgewerkt; onbekende tasks worden genegeerd (story-context
ontbreekt — gebruiker ziet ze pas na refresh)
- task DELETE: verwijdert uit store
- story UPDATE: werkt story_title/story_code bij op alle child-tasks
in de store
- story DELETE: verwijdert alle child-tasks (cascade reflectie)
Unit-test: 7 scenario's (status update, pendingOps echo-suppression,
DELETE, story-rename cascade, story-delete cascade, unknown task
no-op).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
111 lines
2.8 KiB
PL/PgSQL
111 lines
2.8 KiB
PL/PgSQL
-- ST-804 prereq: extend the realtime trigger payload with the new-state
|
|
-- fields the Solo Paneel needs for in-place rendering, so the client doesn't
|
|
-- have to refetch on every update.
|
|
--
|
|
-- Added fields:
|
|
-- task → task_status, task_sort_order, task_title
|
|
-- story → story_status, story_sort_order, story_title, story_code
|
|
--
|
|
-- Description and implementation_plan stay out of the payload — they can
|
|
-- be large and aren't needed for kanban-board rendering. UI fetches them
|
|
-- on demand when the detail dialog opens.
|
|
|
|
CREATE OR REPLACE FUNCTION notify_task_change() RETURNS trigger AS $$
|
|
DECLARE
|
|
rec record;
|
|
story_row record;
|
|
payload jsonb;
|
|
BEGIN
|
|
IF TG_OP = 'DELETE' THEN
|
|
rec := OLD;
|
|
ELSE
|
|
rec := NEW;
|
|
END IF;
|
|
|
|
SELECT product_id, sprint_id, assignee_id
|
|
INTO story_row
|
|
FROM stories
|
|
WHERE id = rec.story_id;
|
|
|
|
IF NOT FOUND THEN
|
|
RETURN rec;
|
|
END IF;
|
|
|
|
payload := jsonb_build_object(
|
|
'op', CASE TG_OP
|
|
WHEN 'INSERT' THEN 'I'
|
|
WHEN 'UPDATE' THEN 'U'
|
|
WHEN 'DELETE' THEN 'D'
|
|
END,
|
|
'entity', 'task',
|
|
'id', rec.id,
|
|
'story_id', rec.story_id,
|
|
'product_id', story_row.product_id,
|
|
'sprint_id', story_row.sprint_id,
|
|
'assignee_id', story_row.assignee_id,
|
|
'task_status', rec.status,
|
|
'task_sort_order', rec.sort_order,
|
|
'task_title', rec.title
|
|
);
|
|
|
|
IF TG_OP = 'UPDATE' THEN
|
|
payload := payload || jsonb_build_object(
|
|
'changed_fields',
|
|
COALESCE((
|
|
SELECT jsonb_agg(n.key)
|
|
FROM jsonb_each(to_jsonb(NEW)) n
|
|
JOIN jsonb_each(to_jsonb(OLD)) o USING (key)
|
|
WHERE n.value IS DISTINCT FROM o.value
|
|
), '[]'::jsonb)
|
|
);
|
|
END IF;
|
|
|
|
PERFORM pg_notify('scrum4me_changes', payload::text);
|
|
RETURN rec;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE OR REPLACE FUNCTION notify_story_change() RETURNS trigger AS $$
|
|
DECLARE
|
|
rec record;
|
|
payload jsonb;
|
|
BEGIN
|
|
IF TG_OP = 'DELETE' THEN
|
|
rec := OLD;
|
|
ELSE
|
|
rec := NEW;
|
|
END IF;
|
|
|
|
payload := jsonb_build_object(
|
|
'op', CASE TG_OP
|
|
WHEN 'INSERT' THEN 'I'
|
|
WHEN 'UPDATE' THEN 'U'
|
|
WHEN 'DELETE' THEN 'D'
|
|
END,
|
|
'entity', 'story',
|
|
'id', rec.id,
|
|
'product_id', rec.product_id,
|
|
'sprint_id', rec.sprint_id,
|
|
'assignee_id', rec.assignee_id,
|
|
'story_status', rec.status,
|
|
'story_sort_order', rec.sort_order,
|
|
'story_title', rec.title,
|
|
'story_code', rec.code
|
|
);
|
|
|
|
IF TG_OP = 'UPDATE' THEN
|
|
payload := payload || jsonb_build_object(
|
|
'changed_fields',
|
|
COALESCE((
|
|
SELECT jsonb_agg(n.key)
|
|
FROM jsonb_each(to_jsonb(NEW)) n
|
|
JOIN jsonb_each(to_jsonb(OLD)) o USING (key)
|
|
WHERE n.value IS DISTINCT FROM o.value
|
|
), '[]'::jsonb)
|
|
);
|
|
END IF;
|
|
|
|
PERFORM pg_notify('scrum4me_changes', payload::text);
|
|
RETURN rec;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|