Support deep links into IDM assignments

This commit is contained in:
2026-07-11 00:55:23 +02:00
parent b09c1449b9
commit 05a10846a8

View File

@@ -1,4 +1,4 @@
import { useCallback, useEffect, useMemo, useState, type FormEvent } from "react";
import { useCallback, useEffect, useMemo, useRef, useState, type FormEvent } from "react";
import { Edit3, RefreshCw, Save, XCircle } from "lucide-react";
import {
ApiError,
@@ -219,6 +219,15 @@ function sourceLabel(value: string): string {
return SOURCE_OPTIONS.find((item) => item.value === value)?.label ?? value;
}
function idmInitialQuery(): { assignmentId: string; functionId: string } {
if (typeof window === "undefined") return { assignmentId: "", functionId: "" };
const params = new URLSearchParams(window.location.search);
return {
assignmentId: params.get("assignment_id") || "",
functionId: params.get("function_id") || ""
};
}
export default function IdmPage({ settings, auth }: IdmPageProps) {
const [model, setModel] = useState<OrganizationModel>(EMPTY_MODEL);
const [assignments, setAssignments] = useState<OrganizationFunctionAssignmentItem[]>([]);
@@ -237,6 +246,8 @@ export default function IdmPage({ settings, auth }: IdmPageProps) {
const [busy, setBusy] = useState(false);
const [error, setError] = useState("");
const [success, setSuccess] = useState("");
const initialQuery = useMemo(() => idmInitialQuery(), []);
const appliedInitialQueryRef = useRef(false);
const canManage = hasScope(auth, "idm:organization_assignment:write") || hasScope(auth, "organizations:function:assign");
const canSearchIdentities = canManage || hasScope(auth, "idm:organization_identity:read") || hasScope(auth, "admin:users:read");
@@ -286,10 +297,7 @@ export default function IdmPage({ settings, auth }: IdmPageProps) {
if (assignmentDraft.acting_for_account_id) values.add(assignmentDraft.acting_for_account_id);
return Array.from(values).sort((left, right) => left.localeCompare(right));
}, [actingForOptionById, actingForOptions, assignmentDraft.acting_for_account_id, identityOptionById, sourceAssignment]);
const initialFunctionFilter = useMemo(() => {
if (typeof window === "undefined") return "";
return new URLSearchParams(window.location.search).get("function_id") || "";
}, []);
const initialFunctionFilter = initialQuery.functionId;
const hasDirtyAssignmentDraft = isAssignmentDirty(assignmentDraft);
const hasDirtySettingsDraft = canReadSettings && isSettingsDirty(settingsDraft, idmSettings);
const hasDirtyDraft = hasDirtyAssignmentDraft || hasDirtySettingsDraft;
@@ -307,7 +315,16 @@ export default function IdmPage({ settings, auth }: IdmPageProps) {
setAssignments(nextAssignments.assignments);
setIdmSettings(nextSettings);
if (nextSettings) setSettingsDraft(settingsDraftFrom(nextSettings));
if (initialFunctionFilter && nextModel.functions.some((item) => item.id === initialFunctionFilter)) {
if (!appliedInitialQueryRef.current && initialQuery.assignmentId) {
const initialAssignment = nextAssignments.assignments.find((item) => item.id === initialQuery.assignmentId);
if (initialAssignment) {
appliedInitialQueryRef.current = true;
setEditingAssignmentId(initialAssignment.id);
setAssignmentDraft(assignmentDraftFrom(initialAssignment));
}
}
if (!appliedInitialQueryRef.current && initialFunctionFilter && nextModel.functions.some((item) => item.id === initialFunctionFilter)) {
appliedInitialQueryRef.current = true;
setAssignmentDraft((draft) => draft.function_id ? draft : { ...draft, function_id: initialFunctionFilter });
}
if (canSearchIdentities) {
@@ -326,7 +343,7 @@ export default function IdmPage({ settings, auth }: IdmPageProps) {
} finally {
setLoading(false);
}
}, [canReadSettings, canSearchIdentities, initialFunctionFilter, settings]);
}, [canReadSettings, canSearchIdentities, initialFunctionFilter, initialQuery.assignmentId, settings]);
useEffect(() => {
void loadData();