diff --git a/artifacts/api-server/src/lib/realtime.ts b/artifacts/api-server/src/lib/realtime.ts index 1dfb2276..d8548704 100644 --- a/artifacts/api-server/src/lib/realtime.ts +++ b/artifacts/api-server/src/lib/realtime.ts @@ -51,6 +51,22 @@ export async function emitAppsChangedToRoleHolders(roleId: number): Promise { + if (typeof date !== "string" || date.length === 0) return; + const { io } = await import("../index.js"); + io.emit("executive_meetings_changed", { date }); +} + /** * Notify every holder of `roleId` that the role's permission set has been * updated, so the client can invalidate cached `/api/auth/me` and diff --git a/artifacts/api-server/src/routes/executive-meetings.ts b/artifacts/api-server/src/routes/executive-meetings.ts index 9a438e6a..09ccda3c 100644 --- a/artifacts/api-server/src/routes/executive-meetings.ts +++ b/artifacts/api-server/src/routes/executive-meetings.ts @@ -33,6 +33,7 @@ import { getEffectiveRoleIds, } from "../middlewares/auth"; import { sanitizeRichText } from "../lib/sanitize"; +import { emitExecutiveMeetingsDayChanged } from "../lib/realtime"; import { ExecutiveMeetingsReorderBody } from "@workspace/api-zod"; import type { Request, Response, NextFunction } from "express"; @@ -780,6 +781,9 @@ router.patch( }); }); const updated = await fetchMeetingWithAttendees(id); + void emitExecutiveMeetingsDayChanged( + updated?.meetingDate ?? existing.meetingDate, + ); res.json(updated); } catch (err) { const msg = err instanceof Error ? err.message : String(err); diff --git a/artifacts/tx-os/src/hooks/use-notifications-socket.ts b/artifacts/tx-os/src/hooks/use-notifications-socket.ts index 9ed1baf0..ce448522 100644 --- a/artifacts/tx-os/src/hooks/use-notifications-socket.ts +++ b/artifacts/tx-os/src/hooks/use-notifications-socket.ts @@ -71,6 +71,21 @@ export function useNotificationsSocket() { queryClient.invalidateQueries({ queryKey: getGetMeQueryKey() }); }); + socket.on( + "executive_meetings_changed", + (payload?: { date?: string }) => { + if (typeof payload?.date === "string" && payload.date.length > 0) { + queryClient.invalidateQueries({ + queryKey: ["/api/executive-meetings", payload.date], + }); + } else { + queryClient.invalidateQueries({ + queryKey: ["/api/executive-meetings"], + }); + } + }, + ); + socket.on( "role_permissions_changed", (payload?: { roleId?: number }) => { diff --git a/artifacts/tx-os/src/pages/executive-meetings.tsx b/artifacts/tx-os/src/pages/executive-meetings.tsx index a31da44a..d6c160cb 100644 --- a/artifacts/tx-os/src/pages/executive-meetings.tsx +++ b/artifacts/tx-os/src/pages/executive-meetings.tsx @@ -1879,7 +1879,15 @@ function MeetingRow({
{canMutate && ( @@ -2155,7 +2163,7 @@ function MergeMenu({