Push role permission changes to signed-in users in real time
Original task (#101): When an admin saves the role editor, role holders need their permission-driven UI (admin nav items, permission-gated action buttons, etc.) to refresh without waiting for a page reload. The server already emitted `apps_changed`, but that event is semantically about visible apps, not the broader cached permission state, and the client only invalidated the apps list and `/api/auth/me`. Changes: - artifacts/api-server/src/lib/realtime.ts - Extracted role-holder resolution (direct user_roles + indirect via group_roles -> user_groups) into a private helper. - Added `emitRolePermissionsChangedToHolders(roleId)` that emits a new `role_permissions_changed` socket event with `{ roleId }` to every holder of the role. - artifacts/api-server/src/routes/roles.ts - After `PUT /api/roles/:id/permissions` succeeds, call the new emitter alongside the existing `emitAppsChangedToRoleHolders`. - artifacts/tx-os/src/hooks/use-notifications-socket.ts - Subscribed to `role_permissions_changed`. Invalidates `getMe`, `listRoles`, `listPermissions`, and—when the payload includes the role id—`getRolePermissions`, the role permission audit, and role usage queries so admin-only UI re-evaluates without a refresh. Scope notes / deviations: - The task spec called out `PUT /api/roles/:id/permissions` only, so the matching POST/DELETE single-permission endpoints still emit only `apps_changed`. Filed as follow-up #150 to keep them consistent. - No new automated tests added (filed as follow-up #151). Pre-existing typecheck errors in artifacts/api-server/src/routes/executive-meetings.ts are unrelated and untouched. - `replit.md` not updated; this is a behavioral refinement of an existing socket flow, not an architectural change. Replit-Task-Id: bd31a9a9-dbf1-497a-8505-96672f92bc79
This commit is contained in:
@@ -8,6 +8,11 @@ import {
|
||||
getListIncomingServiceOrdersQueryKey,
|
||||
getListAppsQueryKey,
|
||||
getGetMeQueryKey,
|
||||
getListRolesQueryKey,
|
||||
getListPermissionsQueryKey,
|
||||
getGetRolePermissionsQueryKey,
|
||||
getGetRolePermissionAuditQueryKey,
|
||||
getGetRoleUsageQueryKey,
|
||||
} from "@workspace/api-client-react";
|
||||
import { useAuth } from "@/contexts/AuthContext";
|
||||
|
||||
@@ -66,6 +71,27 @@ export function useNotificationsSocket() {
|
||||
queryClient.invalidateQueries({ queryKey: getGetMeQueryKey() });
|
||||
});
|
||||
|
||||
socket.on(
|
||||
"role_permissions_changed",
|
||||
(payload?: { roleId?: number }) => {
|
||||
queryClient.invalidateQueries({ queryKey: getGetMeQueryKey() });
|
||||
queryClient.invalidateQueries({ queryKey: getListPermissionsQueryKey() });
|
||||
queryClient.invalidateQueries({ queryKey: getListRolesQueryKey() });
|
||||
const roleId = payload?.roleId;
|
||||
if (typeof roleId === "number") {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: getGetRolePermissionsQueryKey(roleId),
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: getGetRolePermissionAuditQueryKey(roleId),
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: getGetRoleUsageQueryKey(roleId),
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return () => {
|
||||
socket.disconnect();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user