fix: Add missing event routing for initiative status real-time refresh
7 of 12 initiative activity state transitions were broken due to missing event routing at three layers: SSE event arrays, live-update prefix rules, and mutation invalidation map. - Add initiative:changes_requested to ALL_EVENT_TYPES and TASK_EVENT_TYPES - Add initiative:/agent: prefix rules to initiatives list and detail pages - Add approveInitiativeReview, requestInitiativeChanges, requestPhaseChanges to INVALIDATION_MAP; add listInitiatives to approvePhase - Extract INITIATIVE_LIST_RULES constant for reuse
This commit is contained in:
@@ -70,6 +70,7 @@ export const ALL_EVENT_TYPES: DomainEventType[] = [
|
||||
'chat:session_closed',
|
||||
'initiative:pending_review',
|
||||
'initiative:review_approved',
|
||||
'initiative:changes_requested',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -102,6 +103,7 @@ export const TASK_EVENT_TYPES: DomainEventType[] = [
|
||||
'phase:merged',
|
||||
'initiative:pending_review',
|
||||
'initiative:review_approved',
|
||||
'initiative:changes_requested',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
|
||||
export { useAutoSave } from './useAutoSave.js';
|
||||
export { useDebounce, useDebounceWithImmediate } from './useDebounce.js';
|
||||
export { useLiveUpdates } from './useLiveUpdates.js';
|
||||
export { useLiveUpdates, INITIATIVE_LIST_RULES } from './useLiveUpdates.js';
|
||||
export type { LiveUpdateRule } from './useLiveUpdates.js';
|
||||
export { useRefineAgent } from './useRefineAgent.js';
|
||||
export { useConflictAgent } from './useConflictAgent.js';
|
||||
export { useSubscriptionWithErrorHandling } from './useSubscriptionWithErrorHandling.js';
|
||||
|
||||
@@ -15,6 +15,17 @@ export interface LiveUpdateRule {
|
||||
*
|
||||
* Encapsulates error toast + reconnect config so pages don't duplicate boilerplate.
|
||||
*/
|
||||
/**
|
||||
* Reusable rules for any page displaying initiative cards.
|
||||
* Covers all event prefixes that can change derived initiative activity state.
|
||||
*/
|
||||
export const INITIATIVE_LIST_RULES: LiveUpdateRule[] = [
|
||||
{ prefix: 'initiative:', invalidate: ['listInitiatives'] },
|
||||
{ prefix: 'task:', invalidate: ['listInitiatives'] },
|
||||
{ prefix: 'phase:', invalidate: ['listInitiatives'] },
|
||||
{ prefix: 'agent:', invalidate: ['listInitiatives'] },
|
||||
];
|
||||
|
||||
export function useLiveUpdates(rules: LiveUpdateRule[]) {
|
||||
const utils = trpc.useUtils();
|
||||
|
||||
|
||||
@@ -49,12 +49,15 @@ const INVALIDATION_MAP: Partial<Record<MutationName, QueryName[]>> = {
|
||||
createInitiative: ["listInitiatives"],
|
||||
updateInitiative: ["listInitiatives", "getInitiative"],
|
||||
updateInitiativeProjects: ["getInitiative"],
|
||||
approveInitiativeReview: ["listInitiatives", "getInitiative"],
|
||||
requestInitiativeChanges: ["listInitiatives", "getInitiative"],
|
||||
|
||||
// --- Phases ---
|
||||
createPhase: ["listPhases", "listInitiativePhaseDependencies"],
|
||||
deletePhase: ["listPhases", "listInitiativeTasks", "listInitiativePhaseDependencies", "listChangeSets"],
|
||||
updatePhase: ["listPhases", "getPhase"],
|
||||
approvePhase: ["listPhases", "listInitiativeTasks"],
|
||||
approvePhase: ["listPhases", "listInitiativeTasks", "listInitiatives"],
|
||||
requestPhaseChanges: ["listPhases", "listInitiativeTasks", "listPhaseTasks", "getInitiative"],
|
||||
queuePhase: ["listPhases"],
|
||||
createPhaseDependency: ["getPhaseDependencies", "listInitiativePhaseDependencies", "listPhaseTaskDependencies"],
|
||||
removePhaseDependency: ["getPhaseDependencies", "listInitiativePhaseDependencies", "listPhaseTaskDependencies"],
|
||||
|
||||
@@ -12,7 +12,7 @@ import { ExecutionTab } from "@/components/ExecutionTab";
|
||||
import { ReviewTab } from "@/components/review";
|
||||
import { PipelineTab } from "@/components/pipeline";
|
||||
import { useLiveUpdates } from "@/hooks";
|
||||
import type { LiveUpdateRule } from "@/hooks/useLiveUpdates";
|
||||
import type { LiveUpdateRule } from "@/hooks";
|
||||
|
||||
type Tab = "content" | "plan" | "execution" | "review";
|
||||
const TABS: Tab[] = ["content", "plan", "execution", "review"];
|
||||
@@ -31,6 +31,7 @@ function InitiativeDetailPage() {
|
||||
|
||||
// Single SSE stream for all live updates — memoized to avoid re-subscribe on render
|
||||
const liveUpdateRules = useMemo<LiveUpdateRule[]>(() => [
|
||||
{ prefix: 'initiative:', invalidate: ['getInitiative'] },
|
||||
{ prefix: 'task:', invalidate: ['listPhases', 'listTasks', 'listInitiativeTasks', 'getPhaseDependencies', 'listPhaseTaskDependencies'] },
|
||||
{ prefix: 'phase:', invalidate: ['listPhases', 'listTasks', 'listInitiativePhaseDependencies', 'getPhaseDependencies'] },
|
||||
{ prefix: 'agent:', invalidate: ['listAgents', 'getActiveRefineAgent'] },
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Plus } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { InitiativeList } from "@/components/InitiativeList";
|
||||
import { CreateInitiativeDialog } from "@/components/CreateInitiativeDialog";
|
||||
import { useLiveUpdates } from "@/hooks";
|
||||
import { useLiveUpdates, INITIATIVE_LIST_RULES } from "@/hooks";
|
||||
import { trpc } from "@/lib/trpc";
|
||||
|
||||
export const Route = createFileRoute("/initiatives/")({
|
||||
@@ -29,10 +29,7 @@ function DashboardPage() {
|
||||
const projectsQuery = trpc.listProjects.useQuery();
|
||||
|
||||
// Single SSE stream for live updates
|
||||
useLiveUpdates([
|
||||
{ prefix: 'task:', invalidate: ['listInitiatives'] },
|
||||
{ prefix: 'phase:', invalidate: ['listInitiatives'] },
|
||||
]);
|
||||
useLiveUpdates(INITIATIVE_LIST_RULES);
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
|
||||
Reference in New Issue
Block a user