feat(approvals): tenant-scoped queue (no cross-tenant work-items)
Plumbed me.tenant_id through api.ping + loginAs into the store as tenantId. Approvals.loadQueue filters /api/ea2/work-items by tenant_id matching the signed-in user so the queue only shows actionable rows. Probe of EA2 work-items returned 73/80 in canvas's tenant (a0000000-...), 6 in hms_dev, 1 in hub-cnt-f56ce0 -- those 7 are now hidden.
This commit is contained in:
+2
-2
@@ -333,10 +333,10 @@ export const api = {
|
||||
},
|
||||
|
||||
/** Probe whether the backend is reachable. Never throws. */
|
||||
async ping(signal?: AbortSignal): Promise<{ ok: boolean; reason?: string; user?: string; user_id?: string }> {
|
||||
async ping(signal?: AbortSignal): Promise<{ ok: boolean; reason?: string; user?: string; user_id?: string; tenant_id?: string }> {
|
||||
try {
|
||||
const me = await this.me(signal);
|
||||
return { ok: true, user: me.email, user_id: me.user_id };
|
||||
return { ok: true, user: me.email, user_id: me.user_id, tenant_id: me.tenant_id };
|
||||
} catch (e) {
|
||||
return { ok: false, reason: (e as Error).message };
|
||||
}
|
||||
|
||||
@@ -35,10 +35,12 @@ export default function Approvals() {
|
||||
const [tx, setTx] = useState<RuntimeTransaction | null>(null);
|
||||
const [busy, setBusy] = useState<string | null>(null);
|
||||
|
||||
const tenantId = useApp((s) => s.tenantId);
|
||||
const loadQueue = async () => {
|
||||
try {
|
||||
const rows = await api.workItems();
|
||||
setItems(rows.map((it) => ({ ...it, age_label: ageLabel(it) })));
|
||||
const scoped = tenantId ? rows.filter((it) => (it as any).tenant_id === tenantId) : rows;
|
||||
setItems(scoped.map((it) => ({ ...it, age_label: ageLabel(it) })));
|
||||
} catch (err: any) {
|
||||
pushToast("err", `Queue failed: ${err.message}`);
|
||||
}
|
||||
|
||||
@@ -94,6 +94,7 @@ interface AppState {
|
||||
actor: Actor | null;
|
||||
userEmail: string;
|
||||
userDisplayName: string | null;
|
||||
tenantId: string | null;
|
||||
isAuthed: boolean;
|
||||
setUserEmail: (e: string) => void;
|
||||
loginAs: (email: string, password?: string, options?: { method?: "password" | "dev" }) => Promise<void>;
|
||||
@@ -141,6 +142,7 @@ async function runLiveFetch(
|
||||
set({
|
||||
actor: { mode: "direct_user", user_id: ping.user_id },
|
||||
userEmail: ping.user ?? get().userEmail,
|
||||
tenantId: ping.tenant_id ?? get().tenantId,
|
||||
});
|
||||
}
|
||||
const { scenarios, workItems, distinctDefs } = await buildLiveScenariosFromApi();
|
||||
@@ -256,6 +258,7 @@ export const useApp = create<AppState>((set, get) => {
|
||||
actor: null,
|
||||
userEmail: prefs.email ?? "dev@flow-master.ai",
|
||||
userDisplayName: null,
|
||||
tenantId: null,
|
||||
isAuthed: typeof sessionStorage !== "undefined" && !!sessionStorage.getItem("fm.mc.token.v1"),
|
||||
setUserEmail: (e) => { set({ userEmail: e }); persist(); },
|
||||
loginAs: async (email, password, options) => {
|
||||
@@ -274,6 +277,7 @@ export const useApp = create<AppState>((set, get) => {
|
||||
set({
|
||||
actor: { mode: "direct_user", user_id: me.user_id },
|
||||
userDisplayName: me.display_name ?? null,
|
||||
tenantId: me.tenant_id ?? null,
|
||||
isAuthed: true,
|
||||
});
|
||||
get().pushToast("ok", `Signed in as ${me.email}`);
|
||||
|
||||
Reference in New Issue
Block a user