Add explicit acting-in-place context
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
import { apiFetch, type ApiSettings } from "@govoplan/core-webui";
|
||||
|
||||
export type ActingContext = {
|
||||
assignment_id: string;
|
||||
acting_for_account_id: string;
|
||||
function_id: string;
|
||||
organization_unit_id: string;
|
||||
valid_from?: string | null;
|
||||
valid_until?: string | null;
|
||||
};
|
||||
|
||||
export type ActingContextList = {
|
||||
contexts: ActingContext[];
|
||||
active_assignment_id?: string | null;
|
||||
};
|
||||
|
||||
export function fetchActingContexts(settings: ApiSettings): Promise<ActingContextList> {
|
||||
return apiFetch<ActingContextList>(settings, "/api/v1/auth/acting-contexts", {
|
||||
cache: "no-store"
|
||||
});
|
||||
}
|
||||
|
||||
export function switchActingContext(
|
||||
settings: ApiSettings,
|
||||
assignmentId: string | null
|
||||
): Promise<ActingContextList> {
|
||||
return apiFetch<ActingContextList>(settings, "/api/v1/auth/switch-acting-context", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ assignment_id: assignmentId })
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
fetchMe,
|
||||
type ActingContextSelectorProps
|
||||
} from "@govoplan/core-webui";
|
||||
import {
|
||||
fetchActingContexts,
|
||||
switchActingContext,
|
||||
type ActingContext
|
||||
} from "../../api/actingContext";
|
||||
|
||||
export default function ActingContextSelector({
|
||||
settings,
|
||||
auth,
|
||||
onAuthChange
|
||||
}: ActingContextSelectorProps) {
|
||||
const [contexts, setContexts] = useState<ActingContext[]>([]);
|
||||
const [activeId, setActiveId] = useState<string>("");
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
void fetchActingContexts(settings)
|
||||
.then((response) => {
|
||||
if (!active) return;
|
||||
setContexts(response.contexts);
|
||||
setActiveId(response.active_assignment_id ?? "");
|
||||
})
|
||||
.catch((reason: unknown) => {
|
||||
if (active) setError(reason instanceof Error ? reason.message : "Acting context could not be loaded.");
|
||||
});
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
}, [settings.apiBaseUrl, settings.accessToken, settings.apiKey, auth.active_tenant?.id, auth.tenant.id]);
|
||||
|
||||
if (!contexts.length && !activeId) return null;
|
||||
|
||||
async function selectContext(assignmentId: string) {
|
||||
setBusy(true);
|
||||
setError("");
|
||||
try {
|
||||
const response = await switchActingContext(settings, assignmentId || null);
|
||||
setContexts(response.contexts);
|
||||
setActiveId(response.active_assignment_id ?? "");
|
||||
onAuthChange(await fetchMe(settings));
|
||||
} catch (reason) {
|
||||
setError(reason instanceof Error ? reason.message : "Acting context could not be changed.");
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<label className="acting-context-selector" title={error || "Select whose authority is represented by this session."}>
|
||||
<span>Acting as</span>
|
||||
<select
|
||||
aria-label="Acting context"
|
||||
value={activeId}
|
||||
disabled={busy}
|
||||
onChange={(event) => void selectContext(event.target.value)}
|
||||
>
|
||||
<option value="">Own account</option>
|
||||
{contexts.map((context) => (
|
||||
<option key={context.assignment_id} value={context.assignment_id}>
|
||||
{context.function_id} / {context.organization_unit_id}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
+6
-3
@@ -1,6 +1,7 @@
|
||||
import { createElement, lazy } from "react";
|
||||
import type { PlatformRouteContext, PlatformWebModule } from "@govoplan/core-webui";
|
||||
import type { ActingContextRuntimeUiCapability, PlatformRouteContext, PlatformWebModule } from "@govoplan/core-webui";
|
||||
import { adminReadScopes } from "@govoplan/core-webui";
|
||||
import ActingContextSelector from "./features/acting-context/ActingContextSelector";
|
||||
import { generatedTranslations } from "./i18n/generatedTranslations";
|
||||
|
||||
const AdminPage = lazy(() => import("./features/admin/AdminPage"));
|
||||
@@ -42,8 +43,10 @@ export const accessModule: PlatformWebModule = {
|
||||
{ to: "/admin", label: "i18n:govoplan-access.admin.4e7afebc", iconName: "admin", anyOf: adminReadScopes, order: 900 }],
|
||||
|
||||
routes: [
|
||||
{ path: "/admin", anyOf: adminReadScopes, order: 900, render: renderAdminRoute }]
|
||||
|
||||
{ path: "/admin", anyOf: adminReadScopes, order: 900, render: renderAdminRoute }],
|
||||
uiCapabilities: {
|
||||
"access.actingContext": { Selector: ActingContextSelector } satisfies ActingContextRuntimeUiCapability
|
||||
}
|
||||
};
|
||||
|
||||
export default accessModule;
|
||||
|
||||
Reference in New Issue
Block a user