Support deep links into organization editor

This commit is contained in:
2026-07-11 00:55:22 +02:00
parent 1f69e6b5a7
commit f1ae795917

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, Plus, RefreshCw, Save, XCircle } from "lucide-react";
import {
ApiError,
@@ -118,6 +118,26 @@ type ActiveItem = {
is_active: boolean;
};
type OrganizationsInitialQuery = {
section: OrganizationSection | "";
unitId: string;
functionId: string;
};
function organizationSectionFromQuery(value: string | null): OrganizationSection | "" {
return value === "model" || value === "units" || value === "relations" || value === "functions" ? value : "";
}
function organizationsInitialQuery(): OrganizationsInitialQuery {
if (typeof window === "undefined") return { section: "", unitId: "", functionId: "" };
const params = new URLSearchParams(window.location.search);
return {
section: organizationSectionFromQuery(params.get("section")),
unitId: params.get("unit_id") || "",
functionId: params.get("function_id") || ""
};
}
const EMPTY_MODEL: OrganizationModel = {
unit_types: [],
structures: [],
@@ -345,7 +365,10 @@ export default function OrganizationsPage({
}: OrganizationsPageProps) {
const sectionSet = useMemo(() => new Set(availableSections.length ? availableSections : ALL_SECTIONS), [availableSections]);
const firstAvailableSection = availableSections.find((section) => sectionSet.has(section)) ?? "model";
const [active, setActive] = useState<OrganizationSection>(sectionSet.has(initialSection) ? initialSection : firstAvailableSection);
const initialQuery = useMemo(() => organizationsInitialQuery(), []);
const requestedInitialSection = initialQuery.section && sectionSet.has(initialQuery.section) ? initialQuery.section : initialSection;
const [active, setActive] = useState<OrganizationSection>(sectionSet.has(requestedInitialSection) ? requestedInitialSection : firstAvailableSection);
const appliedInitialQueryRef = useRef(false);
const visibleSectionGroups = useMemo(() => sectionGroupsFor(sectionSet), [sectionSet]);
const [model, setModel] = useState<OrganizationModel>(EMPTY_MODEL);
const [loading, setLoading] = useState(true);
@@ -784,6 +807,30 @@ export default function OrganizationsPage({
setActive("functions");
}, []);
useEffect(() => {
if (appliedInitialQueryRef.current || loading) return;
if (initialQuery.functionId) {
const item = functionById.get(initialQuery.functionId);
if (item) {
appliedInitialQueryRef.current = true;
editFunction(item);
return;
}
}
if (initialQuery.unitId) {
const item = unitById.get(initialQuery.unitId);
if (item) {
appliedInitialQueryRef.current = true;
editUnit(item);
return;
}
}
if (initialQuery.section && sectionSet.has(initialQuery.section)) {
appliedInitialQueryRef.current = true;
setActive(initialQuery.section);
}
}, [editFunction, editUnit, functionById, initialQuery, loading, sectionSet, unitById]);
const unitTypeColumns: DataGridColumn<OrganizationUnitTypeItem>[] = [
{ id: "name", header: "i18n:govoplan-organizations.name.709a2322", minWidth: 180, sortable: true, filterable: true, value: (row) => row.name },
{ id: "slug", header: "i18n:govoplan-organizations.slug.094da9b9", width: 160, sortable: true, filterable: true, value: (row) => row.slug },