Sync GovOPlaN module state

This commit is contained in:
2026-07-10 21:57:27 +02:00
parent 669647a986
commit 1f69e6b5a7
15 changed files with 485 additions and 722 deletions

View File

@@ -125,25 +125,6 @@ export type OrganizationFunctionItem = {
updated_at: string;
};
export type OrganizationFunctionAssignmentItem = {
id: string;
tenant_id: string;
identity_id: string;
account_id?: string | null;
function_id: string;
organization_unit_id: string;
applies_to_subunits: boolean;
source: string;
delegated_from_assignment_id?: string | null;
acting_for_account_id?: string | null;
valid_from?: string | null;
valid_until?: string | null;
is_active: boolean;
settings: Record<string, unknown>;
created_at: string;
updated_at: string;
};
export type OrganizationModel = {
unit_types: OrganizationUnitTypeItem[];
structures: OrganizationStructureItem[];
@@ -152,21 +133,6 @@ export type OrganizationModel = {
relations: OrganizationRelationItem[];
function_types: OrganizationFunctionTypeItem[];
functions: OrganizationFunctionItem[];
function_assignments: OrganizationFunctionAssignmentItem[];
};
export type IdentityOption = {
id: string;
display_name?: string | null;
external_subject?: string | null;
source: string;
primary_account_id?: string | null;
account_ids: string[];
status: string;
};
export type IdentityListResponse = {
identities: IdentityOption[];
};
export type OrganizationChangeRequestPayload = {
@@ -220,18 +186,6 @@ export type FunctionCreatePayload = SluggedCreatePayload & {
act_in_place_allowed?: boolean | null;
};
export type FunctionAssignmentCreatePayload = {
identity_id: string;
account_id?: string | null;
function_id: string;
applies_to_subunits: boolean;
source: string;
delegated_from_assignment_id?: string | null;
acting_for_account_id?: string | null;
is_active?: boolean;
settings?: Record<string, unknown>;
} & OrganizationChangeRequestPayload;
function post<T, P extends Record<string, unknown>>(settings: ApiSettings, path: string, payload: P): Promise<T> {
return apiFetch<T>(settings, path, { method: "POST", body: JSON.stringify(payload) });
}
@@ -244,15 +198,6 @@ export function getOrganizationModel(settings: ApiSettings): Promise<Organizatio
return apiFetch<OrganizationModel>(settings, "/api/v1/organizations/model");
}
export async function searchIdentityOptions(settings: ApiSettings, query = "", limit = 25): Promise<IdentityOption[]> {
const params = new URLSearchParams();
const trimmed = query.trim();
if (trimmed) params.set("query", trimmed);
params.set("limit", String(limit));
const response = await apiFetch<IdentityListResponse>(settings, `/api/v1/identity/identities?${params.toString()}`);
return response.identities;
}
export function getOrganizationSettings(settings: ApiSettings): Promise<OrganizationSettingsItem> {
return apiFetch<OrganizationSettingsItem>(settings, "/api/v1/organizations/settings");
}
@@ -316,11 +261,3 @@ export function createFunction(settings: ApiSettings, payload: FunctionCreatePay
export function patchFunction(settings: ApiSettings, id: string, payload: Partial<FunctionCreatePayload>): Promise<OrganizationFunctionItem> {
return patch(settings, `/api/v1/organizations/functions/${encodeURIComponent(id)}`, payload);
}
export function createFunctionAssignment(settings: ApiSettings, payload: FunctionAssignmentCreatePayload): Promise<OrganizationFunctionAssignmentItem> {
return post(settings, "/api/v1/organizations/function-assignments", payload);
}
export function patchFunctionAssignment(settings: ApiSettings, id: string, payload: Partial<FunctionAssignmentCreatePayload>): Promise<OrganizationFunctionAssignmentItem> {
return patch(settings, `/api/v1/organizations/function-assignments/${encodeURIComponent(id)}`, payload);
}

View File

@@ -0,0 +1,114 @@
import { useEffect, useMemo, useState } from "react";
import type {
OrganizationFunctionLabelContext,
OrganizationFunctionPickerContext,
OrganizationFunctionSelection
} from "@govoplan/core-webui";
import { getOrganizationModel, type OrganizationFunctionItem, type OrganizationModel, type OrganizationUnitItem } from "../../api/organizations";
type FunctionOption = OrganizationFunctionSelection & {
slug: string;
active: boolean;
};
export function OrganizationFunctionPicker({ settings, auth, value, disabled, onChange }: OrganizationFunctionPickerContext) {
const [model, setModel] = useState<OrganizationModel | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
const tenantId = (auth.active_tenant ?? auth.tenant).id;
useEffect(() => {
let cancelled = false;
setLoading(true);
setError("");
getOrganizationModel(settings)
.then((nextModel) => {
if (!cancelled) setModel(nextModel);
})
.catch((err: unknown) => {
if (!cancelled) setError(err instanceof Error ? err.message : String(err));
})
.finally(() => {
if (!cancelled) setLoading(false);
});
return () => {
cancelled = true;
};
}, [settings.accessToken, settings.apiBaseUrl, tenantId]);
const options = useMemo(() => functionOptions(model), [model]);
const selectedId = value?.functionId ?? "";
return (
<>
<select
value={selectedId}
disabled={disabled || loading || Boolean(error)}
onChange={(event) => {
const selected = options.find((option) => option.functionId === event.target.value);
onChange(selected ?? null);
}}
>
<option value="">{loading ? "i18n:govoplan-organizations.loading_functions.77e8e684" : "i18n:govoplan-organizations.select_function.4895a67d"}</option>
{options.map((option) => (
<option key={option.functionId} value={option.functionId}>
{option.label}{option.organizationUnitLabel ? ` (${option.organizationUnitLabel})` : ""}
</option>
))}
</select>
{error && <p className="muted small-note">i18n:govoplan-organizations.functions_unavailable.4fd6765d</p>}
</>
);
}
export function OrganizationFunctionLabel({ settings, auth, sourceModule, functionId, fallback }: OrganizationFunctionLabelContext) {
const [model, setModel] = useState<OrganizationModel | null>(null);
const tenantId = (auth.active_tenant ?? auth.tenant).id;
useEffect(() => {
let cancelled = false;
getOrganizationModel(settings)
.then((nextModel) => {
if (!cancelled) setModel(nextModel);
})
.catch(() => {
if (!cancelled) setModel(null);
});
return () => {
cancelled = true;
};
}, [settings.accessToken, settings.apiBaseUrl, tenantId]);
const option = useMemo(() => functionOptions(model).find((item) => item.functionId === functionId), [functionId, model]);
if (sourceModule !== "organizations" || !option) return <>{fallback ?? functionId}</>;
return (
<div>
<strong>{option.label}</strong>
<div className="muted small-note">{option.organizationUnitLabel ?? option.slug}</div>
</div>
);
}
function functionOptions(model: OrganizationModel | null): FunctionOption[] {
if (!model) return [];
const unitById = new Map<string, OrganizationUnitItem>(model.units.map((unit) => [unit.id, unit]));
return model.functions
.filter((item) => item.is_active)
.map((item) => toOption(item, unitById.get(item.organization_unit_id)))
.sort((left, right) => {
const unitDelta = (left.organizationUnitLabel ?? "").localeCompare(right.organizationUnitLabel ?? "");
return unitDelta !== 0 ? unitDelta : (left.label ?? left.functionId).localeCompare(right.label ?? right.functionId);
});
}
function toOption(item: OrganizationFunctionItem, unit: OrganizationUnitItem | undefined): FunctionOption {
return {
sourceModule: "organizations",
functionId: item.id,
label: item.name,
organizationUnitId: item.organization_unit_id,
organizationUnitLabel: unit?.name ?? null,
slug: item.slug,
active: item.is_active
};
}

View File

@@ -13,14 +13,16 @@ import {
StatusBadge,
hasScope,
useUnsavedDraftGuard,
usePlatformUiCapabilities,
type ApiSettings,
type AuthInfo,
type DataGridColumn,
type ModuleSubnavGroup
type ModuleSubnavGroup,
type OrganizationFunctionActionContribution,
type OrganizationFunctionActionsUiCapability
} from "@govoplan/core-webui";
import {
createFunction,
createFunctionAssignment,
createFunctionType,
createRelation,
createRelationType,
@@ -29,19 +31,14 @@ import {
createUnitType,
getOrganizationModel,
patchFunction,
patchFunctionAssignment,
patchFunctionType,
patchRelation,
patchRelationType,
patchStructure,
patchUnit,
patchUnitType,
searchIdentityOptions,
type FunctionAssignmentCreatePayload,
type FunctionCreatePayload,
type FunctionTypeCreatePayload,
type IdentityOption,
type OrganizationFunctionAssignmentItem,
type OrganizationFunctionItem,
type OrganizationFunctionTypeItem,
type OrganizationModel,
@@ -58,7 +55,7 @@ import {
type UnitCreatePayload
} from "../../api/organizations";
export type OrganizationSection = "model" | "units" | "relations" | "functions" | "assignments";
export type OrganizationSection = "model" | "units" | "relations" | "functions";
type OrganizationsPageMode = "workspace" | "admin";
type OrganizationsPageProps = {
@@ -116,17 +113,6 @@ type FunctionDraft = SluggedDraft & {
act_in_place_allowed: boolean;
};
type AssignmentDraft = {
identity_id: string;
account_id: string;
function_id: string;
applies_to_subunits: boolean;
source: string;
delegated_from_assignment_id: string;
acting_for_account_id: string;
is_active: boolean;
};
type ActiveItem = {
id: string;
is_active: boolean;
@@ -139,8 +125,7 @@ const EMPTY_MODEL: OrganizationModel = {
units: [],
relations: [],
function_types: [],
functions: [],
function_assignments: []
functions: []
};
const SECTION_GROUPS: ModuleSubnavGroup<OrganizationSection>[] = [
@@ -150,8 +135,7 @@ const SECTION_GROUPS: ModuleSubnavGroup<OrganizationSection>[] = [
{ id: "model", label: "i18n:govoplan-organizations.meta_model.7398487c", primary: true },
{ id: "units", label: "i18n:govoplan-organizations.units.e14d0d92" },
{ id: "relations", label: "i18n:govoplan-organizations.relations.1c796711" },
{ id: "functions", label: "i18n:govoplan-organizations.functions.805dc49b" },
{ id: "assignments", label: "i18n:govoplan-organizations.assignments.278f513e" }
{ id: "functions", label: "i18n:govoplan-organizations.functions.805dc49b" }
]
}
];
@@ -163,7 +147,7 @@ const STRUCTURE_KINDS: Array<{ value: OrganizationStructureKind; label: string }
{ value: "classification", label: "i18n:govoplan-organizations.classification.3e9f6c3a" }
];
const ALL_SECTIONS: OrganizationSection[] = ["model", "units", "relations", "functions", "assignments"];
const ALL_SECTIONS: OrganizationSection[] = ["model", "units", "relations", "functions"];
function emptySluggedDraft(): SluggedDraft {
return { name: "", slug: "", description: "", is_active: true };
@@ -200,19 +184,6 @@ function emptyFunctionDraft(): FunctionDraft {
return { ...emptySluggedDraft(), organization_unit_id: "", function_type_id: "", delegable: false, act_in_place_allowed: false };
}
function emptyAssignmentDraft(): AssignmentDraft {
return {
identity_id: "",
account_id: "",
function_id: "",
applies_to_subunits: false,
source: "direct",
delegated_from_assignment_id: "",
acting_for_account_id: "",
is_active: true
};
}
function textOrNull(value: string): string | null {
const trimmed = value.trim();
return trimmed ? trimmed : null;
@@ -270,19 +241,6 @@ function isFunctionDirty(draft: FunctionDraft): boolean {
return isSluggedDirty(draft) || Boolean(draft.organization_unit_id || draft.function_type_id) || draft.delegable || draft.act_in_place_allowed;
}
function isAssignmentDirty(draft: AssignmentDraft): boolean {
return Boolean(
draft.identity_id.trim() ||
draft.account_id.trim() ||
draft.function_id ||
draft.applies_to_subunits ||
draft.source.trim() !== "direct" ||
draft.delegated_from_assignment_id ||
draft.acting_for_account_id ||
!draft.is_active
);
}
function mapById<T extends { id: string }>(items: T[]): Map<string, T> {
return new Map(items.map((item) => [item.id, item]));
}
@@ -317,21 +275,6 @@ function activeStatus(active: boolean): JSX.Element {
return <StatusBadge status={active ? "success" : "inactive"} label={active ? "i18n:govoplan-organizations.active.a733b809" : "i18n:govoplan-organizations.inactive.09af574c"} />;
}
function identityOptionLabel(identity: IdentityOption): string {
return identity.display_name || identity.external_subject || identity.primary_account_id || identity.id;
}
function identityDisplay(identityId: string, identities: Map<string, IdentityOption>): JSX.Element {
const identity = identities.get(identityId);
if (!identity) return <span className="organizations-id">{identityId}</span>;
return (
<span className="organizations-identity">
<span>{identityOptionLabel(identity)}</span>
<span className="organizations-id">{identity.id}</span>
</span>
);
}
function ActiveToggleButton({ item, disabled, onToggle }: { item: ActiveItem; disabled: boolean; onToggle: () => void }) {
const label = item.is_active ? "i18n:govoplan-organizations.deactivate.46ab070d" : "i18n:govoplan-organizations.reactivate.58f2855d";
return (
@@ -341,13 +284,14 @@ function ActiveToggleButton({ item, disabled, onToggle }: { item: ActiveItem; di
);
}
function RowActions({ item, disabled, onEdit, onToggle }: { item: ActiveItem; disabled: boolean; onEdit: () => void; onToggle: () => void }) {
function RowActions({ item, disabled, onEdit, onToggle, extra }: { item: ActiveItem; disabled: boolean; onEdit: () => void; onToggle: () => void; extra?: JSX.Element[] }) {
return (
<div className="organizations-row-actions">
<Button type="button" variant="ghost" disabled={disabled} onClick={onEdit} title="i18n:govoplan-organizations.edit.7dce1220">
<Edit3 size={16} aria-hidden="true" /> i18n:govoplan-organizations.edit.7dce1220
</Button>
<ActiveToggleButton item={item} disabled={disabled} onToggle={onToggle} />
{extra}
</div>
);
}
@@ -382,6 +326,14 @@ function SluggedFields({
);
}
function contributionVisible(auth: AuthInfo, contribution: { anyOf?: string[]; allOf?: string[] }): boolean {
const anyOf = contribution.anyOf ?? [];
const allOf = contribution.allOf ?? [];
if (anyOf.length && !anyOf.some((scope) => hasScope(auth, scope))) return false;
if (allOf.length && !allOf.every((scope) => hasScope(auth, scope))) return false;
return true;
}
export default function OrganizationsPage({
settings,
auth,
@@ -408,7 +360,6 @@ export default function OrganizationsPage({
const [relationDraft, setRelationDraft] = useState<RelationDraft>(() => emptyRelationDraft());
const [functionTypeDraft, setFunctionTypeDraft] = useState<FunctionTypeDraft>(() => emptyFunctionTypeDraft());
const [functionDraft, setFunctionDraft] = useState<FunctionDraft>(() => emptyFunctionDraft());
const [assignmentDraft, setAssignmentDraft] = useState<AssignmentDraft>(() => emptyAssignmentDraft());
const [editingUnitTypeId, setEditingUnitTypeId] = useState<string | null>(null);
const [editingStructureId, setEditingStructureId] = useState<string | null>(null);
const [editingRelationTypeId, setEditingRelationTypeId] = useState<string | null>(null);
@@ -416,18 +367,13 @@ export default function OrganizationsPage({
const [editingRelationId, setEditingRelationId] = useState<string | null>(null);
const [editingFunctionTypeId, setEditingFunctionTypeId] = useState<string | null>(null);
const [editingFunctionId, setEditingFunctionId] = useState<string | null>(null);
const [editingAssignmentId, setEditingAssignmentId] = useState<string | null>(null);
const [selectedUnitId, setSelectedUnitId] = useState("");
const [changeRequestId, setChangeRequestId] = useState("");
const [identitySearch, setIdentitySearch] = useState("");
const [identityOptions, setIdentityOptions] = useState<IdentityOption[]>([]);
const [identityLoading, setIdentityLoading] = useState(false);
const [identityLookupAvailable, setIdentityLookupAvailable] = useState(true);
const functionActionCapabilities = usePlatformUiCapabilities<OrganizationFunctionActionsUiCapability>("organizations.functionActions");
const canWriteModel = hasScope(auth, "organizations:model:write");
const canWriteUnits = hasScope(auth, "organizations:unit:write");
const canWriteFunctions = hasScope(auth, "organizations:function:write");
const canAssignFunctions = hasScope(auth, "organizations:function:assign");
const unitTypeById = useMemo(() => mapById(model.unit_types), [model.unit_types]);
const structureById = useMemo(() => mapById(model.structures), [model.structures]);
@@ -435,7 +381,13 @@ export default function OrganizationsPage({
const unitById = useMemo(() => mapById(model.units), [model.units]);
const functionTypeById = useMemo(() => mapById(model.function_types), [model.function_types]);
const functionById = useMemo(() => mapById(model.functions), [model.functions]);
const identityOptionById = useMemo(() => mapById(identityOptions), [identityOptions]);
const functionActionContributions = useMemo(
() => functionActionCapabilities
.flatMap((capability) => capability.actions)
.filter((contribution) => contributionVisible(auth, contribution))
.sort((left, right) => (left.order ?? 100) - (right.order ?? 100)),
[auth, functionActionCapabilities]
);
const unitsByParentId = useMemo(() => {
const mapped = new Map<string, OrganizationUnitItem[]>();
for (const unit of model.units) {
@@ -452,8 +404,7 @@ export default function OrganizationsPage({
isUnitDirty(unitDraft) ||
isRelationDirty(relationDraft) ||
isFunctionTypeDirty(functionTypeDraft) ||
isFunctionDirty(functionDraft) ||
isAssignmentDirty(assignmentDraft);
isFunctionDirty(functionDraft);
const loadModel = useCallback(async () => {
setLoading(true);
@@ -484,36 +435,6 @@ export default function OrganizationsPage({
if (selectedUnitId && !unitById.has(selectedUnitId)) setSelectedUnitId("");
}, [selectedUnitId, unitById]);
useEffect(() => {
if (active !== "assignments" || !identityLookupAvailable) return undefined;
let cancelled = false;
const timeout = window.setTimeout(() => {
setIdentityLoading(true);
searchIdentityOptions(settings, identitySearch, 50).
then((items) => {
if (cancelled) return;
setIdentityOptions(items);
setIdentityLookupAvailable(true);
}).
catch((caught) => {
if (cancelled) return;
if (caught instanceof ApiError && (caught.status === 403 || caught.status === 404)) {
setIdentityLookupAvailable(false);
setIdentityOptions([]);
return;
}
setError(apiErrorMessage(caught));
}).
finally(() => {
if (!cancelled) setIdentityLoading(false);
});
}, 250);
return () => {
cancelled = true;
window.clearTimeout(timeout);
};
}, [active, identityLookupAvailable, identitySearch, settings]);
const runAction = useCallback(async (action: () => Promise<unknown>, successMessage = ""): Promise<boolean> => {
setBusy(true);
setError("");
@@ -539,7 +460,6 @@ export default function OrganizationsPage({
setRelationDraft(emptyRelationDraft());
setFunctionTypeDraft(emptyFunctionTypeDraft());
setFunctionDraft(emptyFunctionDraft());
setAssignmentDraft(emptyAssignmentDraft());
setEditingUnitTypeId(null);
setEditingStructureId(null);
setEditingRelationTypeId(null);
@@ -547,7 +467,6 @@ export default function OrganizationsPage({
setEditingRelationId(null);
setEditingFunctionTypeId(null);
setEditingFunctionId(null);
setEditingAssignmentId(null);
}, []);
function rejectMissingName(): Promise<boolean> {
@@ -728,35 +647,6 @@ export default function OrganizationsPage({
return ok;
}, [canWriteFunctions, editingFunctionId, functionDraft, runAction, settings, withChangeRequest]);
const submitAssignment = useCallback(async (event?: FormEvent): Promise<boolean> => {
event?.preventDefault();
if (!canAssignFunctions) return rejectMissingWritePermission();
if (!assignmentDraft.identity_id.trim() || !assignmentDraft.function_id) {
setError("i18n:govoplan-organizations.select_function.4895a67d");
return false;
}
const payload: FunctionAssignmentCreatePayload = {
identity_id: assignmentDraft.identity_id.trim(),
account_id: textOrNull(assignmentDraft.account_id),
function_id: assignmentDraft.function_id,
applies_to_subunits: assignmentDraft.applies_to_subunits,
source: assignmentDraft.source.trim() || "direct",
delegated_from_assignment_id: textOrNull(assignmentDraft.delegated_from_assignment_id),
acting_for_account_id: textOrNull(assignmentDraft.acting_for_account_id),
is_active: assignmentDraft.is_active,
settings: {}
};
const ok = await runAction(
() => editingAssignmentId ? patchFunctionAssignment(settings, editingAssignmentId, withChangeRequest(payload)) : createFunctionAssignment(settings, withChangeRequest(payload)),
editingAssignmentId ? "i18n:govoplan-organizations.assignment_updated.680b6e33" : "i18n:govoplan-organizations.assignment_added.94263d1b"
);
if (ok) {
setAssignmentDraft(emptyAssignmentDraft());
setEditingAssignmentId(null);
}
return ok;
}, [assignmentDraft, canAssignFunctions, editingAssignmentId, runAction, settings, withChangeRequest]);
const saveDrafts = useCallback(async (): Promise<boolean> => {
if (isSluggedDirty(unitTypeDraft) && !(await submitUnitType())) return false;
if (isSluggedDirty(structureDraft) && !(await submitStructure())) return false;
@@ -765,16 +655,13 @@ export default function OrganizationsPage({
if (isRelationDirty(relationDraft) && !(await submitRelation())) return false;
if (isFunctionTypeDirty(functionTypeDraft) && !(await submitFunctionType())) return false;
if (isFunctionDirty(functionDraft) && !(await submitFunction())) return false;
if (isAssignmentDirty(assignmentDraft) && !(await submitAssignment())) return false;
return true;
}, [
assignmentDraft,
functionDraft,
functionTypeDraft,
relationDraft,
relationTypeDraft,
structureDraft,
submitAssignment,
submitFunction,
submitFunctionType,
submitRelation,
@@ -822,10 +709,6 @@ export default function OrganizationsPage({
void runAction(() => patchFunction(settings, item.id, withChangeRequest({ is_active: !item.is_active })));
}, [runAction, settings, withChangeRequest]);
const toggleAssignment = useCallback((item: OrganizationFunctionAssignmentItem) => {
void runAction(() => patchFunctionAssignment(settings, item.id, withChangeRequest({ is_active: !item.is_active })));
}, [runAction, settings, withChangeRequest]);
const editUnitType = useCallback((item: OrganizationUnitTypeItem) => {
setEditingUnitTypeId(item.id);
setUnitTypeDraft(sluggedDraftFrom(item));
@@ -901,22 +784,6 @@ export default function OrganizationsPage({
setActive("functions");
}, []);
const editAssignment = useCallback((item: OrganizationFunctionAssignmentItem) => {
setEditingAssignmentId(item.id);
setSelectedUnitId(item.organization_unit_id);
setAssignmentDraft({
identity_id: item.identity_id,
account_id: item.account_id ?? "",
function_id: item.function_id,
applies_to_subunits: item.applies_to_subunits,
source: item.source,
delegated_from_assignment_id: item.delegated_from_assignment_id ?? "",
acting_for_account_id: item.acting_for_account_id ?? "",
is_active: item.is_active
});
setActive("assignments");
}, []);
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 },
@@ -976,20 +843,28 @@ export default function OrganizationsPage({
{ id: "type", header: "i18n:govoplan-organizations.function_type.501fe7c0", minWidth: 180, render: (row) => optionalLabel(functionTypeById.get(row.function_type_id || "")?.name) },
{ id: "delegable", header: "i18n:govoplan-organizations.delegable.b4f0137d", width: 120, render: (row) => activeStatus(row.delegable) },
{ id: "status", header: "i18n:govoplan-organizations.status.bae7d5be", width: 120, render: (row) => activeStatus(row.is_active) },
{ id: "actions", header: "", width: 220, sticky: "end", render: (row) => <RowActions item={row} disabled={!canWriteFunctions || busy} onEdit={() => editFunction(row)} onToggle={() => toggleFunction(row)} /> }
{
id: "actions",
header: "",
width: functionActionContributions.length ? 340 : 220,
sticky: "end",
render: (row) => (
<RowActions
item={row}
disabled={!canWriteFunctions || busy}
onEdit={() => editFunction(row)}
onToggle={() => toggleFunction(row)}
extra={functionActionContributions.map((contribution) => (
<span className="organizations-contributed-action" key={contribution.id}>
{contribution.render({ settings, auth, function: row })}
</span>
))}
/>
)
}
];
const assignmentColumns: DataGridColumn<OrganizationFunctionAssignmentItem>[] = [
{ id: "identity", header: "i18n:govoplan-organizations.identity.3252f35f", minWidth: 220, render: (row) => identityDisplay(row.identity_id, identityOptionById) },
{ id: "function", header: "i18n:govoplan-organizations.function.28822f3a", minWidth: 190, render: (row) => optionalLabel(functionById.get(row.function_id)?.name) },
{ id: "unit", header: "i18n:govoplan-organizations.unit.8fe4d595", minWidth: 180, render: (row) => optionalLabel(unitById.get(row.organization_unit_id)?.name) },
{ id: "subunits", header: "i18n:govoplan-organizations.applies_to_subunits.7a15f741", width: 150, render: (row) => activeStatus(row.applies_to_subunits) },
{ id: "source", header: "i18n:govoplan-organizations.source.6da13add", width: 130, value: (row) => row.source },
{ id: "status", header: "i18n:govoplan-organizations.status.bae7d5be", width: 120, render: (row) => activeStatus(row.is_active) },
{ id: "actions", header: "", width: 220, sticky: "end", render: (row) => <RowActions item={row} disabled={!canAssignFunctions || busy} onEdit={() => editAssignment(row)} onToggle={() => toggleAssignment(row)} /> }
];
const canWriteActive = active === "model" ? canWriteModel : active === "units" || active === "relations" ? canWriteUnits : active === "functions" ? canWriteFunctions : canAssignFunctions;
const canWriteActive = active === "model" ? canWriteModel : active === "units" || active === "relations" ? canWriteUnits : canWriteFunctions;
function cancelEditButton(onCancel: () => void) {
return <Button type="button" variant="ghost" onClick={onCancel} disabled={busy}>i18n:govoplan-organizations.cancel_edit.309c2a6f</Button>;
@@ -1032,7 +907,6 @@ export default function OrganizationsPage({
{active === "units" && renderUnitsSection()}
{active === "relations" && renderRelationsSection()}
{active === "functions" && renderFunctionsSection()}
{active === "assignments" && renderAssignmentsSection()}
</LoadingFrame>
</div>
);
@@ -1302,77 +1176,4 @@ export default function OrganizationsPage({
);
}
function renderAssignmentsSection() {
const selectedIdentity = identityOptionById.get(assignmentDraft.identity_id);
return (
<div className="organizations-section-grid">
<Card title="i18n:govoplan-organizations.add_assignment.6682f5f5">
<form className="organizations-form-grid" onSubmit={(event) => void submitAssignment(event)}>
{identityLookupAvailable ? (
<div className="organizations-identity-picker wide">
<FormField label="i18n:govoplan-organizations.identity_search.eff6eb16">
<input value={identitySearch} placeholder="i18n:govoplan-organizations.search_identities.004afdfd" disabled={!canAssignFunctions || busy} onChange={(event) => setIdentitySearch(event.target.value)} />
</FormField>
<FormField label="i18n:govoplan-organizations.identity.3252f35f">
<select
value={assignmentDraft.identity_id}
disabled={!canAssignFunctions || busy || identityLoading}
onChange={(event) => {
const identity = identityOptionById.get(event.target.value);
setAssignmentDraft({
...assignmentDraft,
identity_id: event.target.value,
account_id: identity?.primary_account_id ?? identity?.account_ids[0] ?? ""
});
}}
>
<option value="">{identityLoading ? "i18n:govoplan-organizations.loading_identities.9c31d2b5" : "i18n:govoplan-organizations.select_identity.510f1134"}</option>
{assignmentDraft.identity_id && !identityOptionById.has(assignmentDraft.identity_id) && <option value={assignmentDraft.identity_id}>{assignmentDraft.identity_id}</option>}
{identityOptions.map((identity) => <option key={identity.id} value={identity.id}>{identityOptionLabel(identity)}</option>)}
</select>
</FormField>
</div>
) : (
<>
<p className="organizations-muted wide">i18n:govoplan-organizations.identity_lookup_unavailable.5a3e77b2</p>
<FormField label="i18n:govoplan-organizations.identity_id.b6ef5010">
<input value={assignmentDraft.identity_id} placeholder="i18n:govoplan-organizations.identity_id_placeholder.298cbd85" disabled={!canAssignFunctions || busy} onChange={(event) => setAssignmentDraft({ ...assignmentDraft, identity_id: event.target.value })} />
</FormField>
</>
)}
<FormField label="i18n:govoplan-organizations.optional_account_id.5fcf495c">
{selectedIdentity && selectedIdentity.account_ids.length > 0 ? (
<select value={assignmentDraft.account_id} disabled={!canAssignFunctions || busy} onChange={(event) => setAssignmentDraft({ ...assignmentDraft, account_id: event.target.value })}>
<option value="">i18n:govoplan-organizations.none.334c4a4c</option>
{selectedIdentity.account_ids.map((accountId) => <option key={accountId} value={accountId}>{accountId}</option>)}
</select>
) : (
<input value={assignmentDraft.account_id} disabled={!canAssignFunctions || busy} onChange={(event) => setAssignmentDraft({ ...assignmentDraft, account_id: event.target.value })} />
)}
</FormField>
<FormField label="i18n:govoplan-organizations.function.28822f3a">
<select value={assignmentDraft.function_id} disabled={!canAssignFunctions || busy} onChange={(event) => setAssignmentDraft({ ...assignmentDraft, function_id: event.target.value })}>
<option value="">i18n:govoplan-organizations.select_function.4895a67d</option>
{model.functions.map((item) => <option key={item.id} value={item.id}>{item.name}</option>)}
</select>
</FormField>
<FormField label="i18n:govoplan-organizations.source.6da13add">
<input value={assignmentDraft.source} disabled={!canAssignFunctions || busy} onChange={(event) => setAssignmentDraft({ ...assignmentDraft, source: event.target.value })} />
</FormField>
<div className="organizations-check-list">
<label><input type="checkbox" checked={assignmentDraft.applies_to_subunits} disabled={!canAssignFunctions || busy} onChange={(event) => setAssignmentDraft({ ...assignmentDraft, applies_to_subunits: event.target.checked })} /> i18n:govoplan-organizations.applies_to_subunits.7a15f741</label>
<label><input type="checkbox" checked={assignmentDraft.is_active} disabled={!canAssignFunctions || busy} onChange={(event) => setAssignmentDraft({ ...assignmentDraft, is_active: event.target.checked })} /> i18n:govoplan-organizations.active.a733b809</label>
</div>
<div className="organizations-form-actions wide">
<Button type="submit" variant="primary" disabled={!canAssignFunctions || busy}>{editingAssignmentId ? "i18n:govoplan-organizations.update_assignment.bf130723" : "i18n:govoplan-organizations.add_assignment.6682f5f5"}</Button>
{editingAssignmentId && cancelEditButton(() => { setEditingAssignmentId(null); setAssignmentDraft(emptyAssignmentDraft()); })}
</div>
</form>
</Card>
<Card title="i18n:govoplan-organizations.function_assignments.4c5c6dae">
<DataGrid id="organizations-assignments" rows={model.function_assignments} columns={assignmentColumns} getRowKey={(row) => row.id} emptyText="i18n:govoplan-organizations.no_assignments_found.7ecc5bb7" initialFit="container" />
</Card>
</div>
);
}
}

View File

@@ -4,7 +4,6 @@ export const generatedTranslations: PlatformTranslations = {
en: {
"i18n:govoplan-organizations.act_in_place.49b942bd": "Act in place",
"i18n:govoplan-organizations.active.a733b809": "Active",
"i18n:govoplan-organizations.add_assignment.6682f5f5": "Add assignment",
"i18n:govoplan-organizations.add_function.6abafee0": "Add function",
"i18n:govoplan-organizations.add_function_type.90d793f1": "Add function type",
"i18n:govoplan-organizations.add_relation.6b6e67ea": "Add relation",
@@ -15,10 +14,6 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-organizations.add_unit.8fa12fb1": "Add unit",
"i18n:govoplan-organizations.add_unit_type.58f2c05a": "Add unit type",
"i18n:govoplan-organizations.allow_cycles.31327578": "Allow cycles",
"i18n:govoplan-organizations.applies_to_subunits.7a15f741": "Applies to subunits",
"i18n:govoplan-organizations.assignment_added.94263d1b": "Function assignment added.",
"i18n:govoplan-organizations.assignment_updated.680b6e33": "Function assignment updated.",
"i18n:govoplan-organizations.assignments.278f513e": "Assignments",
"i18n:govoplan-organizations.audit_and_retention.3ba1d2fc": "Audit and retention",
"i18n:govoplan-organizations.audit_detail_level.7397355d": "Audit detail level",
"i18n:govoplan-organizations.audit_full.0c83669c": "Full",
@@ -34,28 +29,22 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-organizations.deactivate.46ab070d": "Deactivate",
"i18n:govoplan-organizations.delegable.b4f0137d": "Delegable",
"i18n:govoplan-organizations.description.55f8ebc8": "Description",
"i18n:govoplan-organizations.direct_assignment.2a068aac": "direct",
"i18n:govoplan-organizations.discard_organization_drafts.766d5f46": "Discard organization drafts",
"i18n:govoplan-organizations.edit.7dce1220": "Edit",
"i18n:govoplan-organizations.function.28822f3a": "Function",
"i18n:govoplan-organizations.function_added.e2266702": "Function added.",
"i18n:govoplan-organizations.function_assignments.4c5c6dae": "Function assignments",
"i18n:govoplan-organizations.function_type.501fe7c0": "Function type",
"i18n:govoplan-organizations.function_type_added.2cd9e899": "Function type added.",
"i18n:govoplan-organizations.function_type_updated.1a4f29f6": "Function type updated.",
"i18n:govoplan-organizations.function_types.172c01fe": "Function types",
"i18n:govoplan-organizations.function_updated.65016009": "Function updated.",
"i18n:govoplan-organizations.functions_unavailable.4fd6765d": "Functions are unavailable.",
"i18n:govoplan-organizations.functions.805dc49b": "Functions",
"i18n:govoplan-organizations.hierarchical.8964f313": "Hierarchical",
"i18n:govoplan-organizations.hierarchy.74797f37": "Hierarchy",
"i18n:govoplan-organizations.identity.3252f35f": "Identity",
"i18n:govoplan-organizations.identity_id.b6ef5010": "Identity ID",
"i18n:govoplan-organizations.identity_id_placeholder.298cbd85": "identity UUID",
"i18n:govoplan-organizations.identity_lookup_unavailable.5a3e77b2": "Identity lookup is unavailable. Enable the identity module or enter an identity ID manually.",
"i18n:govoplan-organizations.identity_search.eff6eb16": "Identity search",
"i18n:govoplan-organizations.inactive.09af574c": "Inactive",
"i18n:govoplan-organizations.kind.794c9d9c": "Kind",
"i18n:govoplan-organizations.loading_identities.9c31d2b5": "Loading identities...",
"i18n:govoplan-organizations.loading_functions.77e8e684": "Loading functions...",
"i18n:govoplan-organizations.loading_organization_model.846aa317": "Loading organization model...",
"i18n:govoplan-organizations.loading_organization_settings.c6008db8": "Loading organization settings...",
"i18n:govoplan-organizations.membership.4531d86d": "Membership",
@@ -66,7 +55,6 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-organizations.name.709a2322": "Name",
"i18n:govoplan-organizations.name_is_required.27cd3782": "Name is required.",
"i18n:govoplan-organizations.network.a24d31c3": "Network",
"i18n:govoplan-organizations.no_assignments_found.7ecc5bb7": "No function assignments found.",
"i18n:govoplan-organizations.no_function_types_found.5eef31b1": "No function types found.",
"i18n:govoplan-organizations.no_functions_found.54e759a0": "No functions found.",
"i18n:govoplan-organizations.no_relation_types_found.6f90bb81": "No relation types found.",
@@ -76,7 +64,6 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-organizations.no_unit_types_found.c81fb2a7": "No unit types found.",
"i18n:govoplan-organizations.no_units_found.eea2dd0c": "No units found.",
"i18n:govoplan-organizations.none.334c4a4c": "None",
"i18n:govoplan-organizations.optional_account_id.5fcf495c": "Optional account ID",
"i18n:govoplan-organizations.organization_model.4f924c0e": "Organization model",
"i18n:govoplan-organizations.organization_model_admin_description.35dc9f10": "Configure tenant-local unit types, parallel structures, relation types, and function types used by the organization module.",
"i18n:govoplan-organizations.organization_settings.c9ab9829": "Organization settings",
@@ -84,7 +71,7 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-organizations.organization_settings_saved.bfbcfdfa": "Organization settings saved.",
"i18n:govoplan-organizations.organization_tree.e5bfb195": "Organization tree",
"i18n:govoplan-organizations.organizations.220edf64": "Organizations",
"i18n:govoplan-organizations.organizations_intro.4e67c4bb": "Model organization types, concrete units, structures, functions, and identity-held function assignments.",
"i18n:govoplan-organizations.organizations_intro.4e67c4bb": "Model organization types, concrete units, structures, and functions.",
"i18n:govoplan-organizations.parent_unit.1986c35e": "Parent unit",
"i18n:govoplan-organizations.reactivate.58f2855d": "Reactivate",
"i18n:govoplan-organizations.relation_added.ca90461a": "Relation added.",
@@ -99,10 +86,8 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-organizations.save_settings.913aba9f": "Save settings",
"i18n:govoplan-organizations.save_drafts.606f9401": "Save drafts",
"i18n:govoplan-organizations.saving.56a2285c": "Saving...",
"i18n:govoplan-organizations.search_identities.004afdfd": "Search by name, subject, account, or ID",
"i18n:govoplan-organizations.select_function.4895a67d": "Select function",
"i18n:govoplan-organizations.select_function_type.2f426c43": "Select function type",
"i18n:govoplan-organizations.select_identity.510f1134": "Select identity",
"i18n:govoplan-organizations.select_relation_type.73f92478": "Select relation type",
"i18n:govoplan-organizations.select_source_unit.9b5a29c8": "Select source unit",
"i18n:govoplan-organizations.select_structure.c10f551c": "Select structure",
@@ -129,7 +114,6 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-organizations.unit_types.c7afc174": "Unit types",
"i18n:govoplan-organizations.units.e14d0d92": "Units",
"i18n:govoplan-organizations.unlimited.35569464": "Unlimited",
"i18n:govoplan-organizations.update_assignment.bf130723": "Update assignment",
"i18n:govoplan-organizations.update_function.504f03e5": "Update function",
"i18n:govoplan-organizations.update_function_type.0b7e07d3": "Update function type",
"i18n:govoplan-organizations.update_relation.4c44ce83": "Update relation",
@@ -142,7 +126,6 @@ export const generatedTranslations: PlatformTranslations = {
de: {
"i18n:govoplan-organizations.act_in_place.49b942bd": "In Vertretung handeln",
"i18n:govoplan-organizations.active.a733b809": "Aktiv",
"i18n:govoplan-organizations.add_assignment.6682f5f5": "Zuordnung hinzufügen",
"i18n:govoplan-organizations.add_function.6abafee0": "Funktion hinzufügen",
"i18n:govoplan-organizations.add_function_type.90d793f1": "Funktionstyp hinzufügen",
"i18n:govoplan-organizations.add_relation.6b6e67ea": "Beziehung hinzufügen",
@@ -153,10 +136,6 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-organizations.add_unit.8fa12fb1": "Einheit hinzufügen",
"i18n:govoplan-organizations.add_unit_type.58f2c05a": "Einheitstyp hinzufügen",
"i18n:govoplan-organizations.allow_cycles.31327578": "Zyklen erlauben",
"i18n:govoplan-organizations.applies_to_subunits.7a15f741": "Gilt für Untereinheiten",
"i18n:govoplan-organizations.assignment_added.94263d1b": "Funktionszuordnung hinzugefügt.",
"i18n:govoplan-organizations.assignment_updated.680b6e33": "Funktionszuordnung aktualisiert.",
"i18n:govoplan-organizations.assignments.278f513e": "Zuordnungen",
"i18n:govoplan-organizations.audit_and_retention.3ba1d2fc": "Audit und Aufbewahrung",
"i18n:govoplan-organizations.audit_detail_level.7397355d": "Audit-Detailgrad",
"i18n:govoplan-organizations.audit_full.0c83669c": "Vollständig",
@@ -172,28 +151,22 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-organizations.deactivate.46ab070d": "Deaktivieren",
"i18n:govoplan-organizations.delegable.b4f0137d": "Delegierbar",
"i18n:govoplan-organizations.description.55f8ebc8": "Beschreibung",
"i18n:govoplan-organizations.direct_assignment.2a068aac": "direkt",
"i18n:govoplan-organizations.discard_organization_drafts.766d5f46": "Organisationsentwürfe verwerfen",
"i18n:govoplan-organizations.edit.7dce1220": "Bearbeiten",
"i18n:govoplan-organizations.function.28822f3a": "Funktion",
"i18n:govoplan-organizations.function_added.e2266702": "Funktion hinzugefügt.",
"i18n:govoplan-organizations.function_assignments.4c5c6dae": "Funktionszuordnungen",
"i18n:govoplan-organizations.function_type.501fe7c0": "Funktionstyp",
"i18n:govoplan-organizations.function_type_added.2cd9e899": "Funktionstyp hinzugefügt.",
"i18n:govoplan-organizations.function_type_updated.1a4f29f6": "Funktionstyp aktualisiert.",
"i18n:govoplan-organizations.function_types.172c01fe": "Funktionstypen",
"i18n:govoplan-organizations.function_updated.65016009": "Funktion aktualisiert.",
"i18n:govoplan-organizations.functions_unavailable.4fd6765d": "Funktionen sind nicht verfügbar.",
"i18n:govoplan-organizations.functions.805dc49b": "Funktionen",
"i18n:govoplan-organizations.hierarchical.8964f313": "Hierarchisch",
"i18n:govoplan-organizations.hierarchy.74797f37": "Hierarchie",
"i18n:govoplan-organizations.identity.3252f35f": "Identität",
"i18n:govoplan-organizations.identity_id.b6ef5010": "Identitäts-ID",
"i18n:govoplan-organizations.identity_id_placeholder.298cbd85": "Identitäts-UUID",
"i18n:govoplan-organizations.identity_lookup_unavailable.5a3e77b2": "Die Identitätssuche ist nicht verfügbar. Aktiviere das Identitätsmodul oder trage eine Identitäts-ID manuell ein.",
"i18n:govoplan-organizations.identity_search.eff6eb16": "Identitätssuche",
"i18n:govoplan-organizations.inactive.09af574c": "Inaktiv",
"i18n:govoplan-organizations.kind.794c9d9c": "Art",
"i18n:govoplan-organizations.loading_identities.9c31d2b5": "Identitäten werden geladen...",
"i18n:govoplan-organizations.loading_functions.77e8e684": "Funktionen werden geladen...",
"i18n:govoplan-organizations.loading_organization_model.846aa317": "Organisationsmodell wird geladen...",
"i18n:govoplan-organizations.loading_organization_settings.c6008db8": "Organisationseinstellungen werden geladen...",
"i18n:govoplan-organizations.membership.4531d86d": "Mitgliedschaft",
@@ -204,7 +177,6 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-organizations.name.709a2322": "Name",
"i18n:govoplan-organizations.name_is_required.27cd3782": "Name ist erforderlich.",
"i18n:govoplan-organizations.network.a24d31c3": "Netzwerk",
"i18n:govoplan-organizations.no_assignments_found.7ecc5bb7": "Keine Funktionszuordnungen gefunden.",
"i18n:govoplan-organizations.no_function_types_found.5eef31b1": "Keine Funktionstypen gefunden.",
"i18n:govoplan-organizations.no_functions_found.54e759a0": "Keine Funktionen gefunden.",
"i18n:govoplan-organizations.no_relation_types_found.6f90bb81": "Keine Beziehungstypen gefunden.",
@@ -214,7 +186,6 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-organizations.no_unit_types_found.c81fb2a7": "Keine Einheitstypen gefunden.",
"i18n:govoplan-organizations.no_units_found.eea2dd0c": "Keine Einheiten gefunden.",
"i18n:govoplan-organizations.none.334c4a4c": "Keine",
"i18n:govoplan-organizations.optional_account_id.5fcf495c": "Optionale Konto-ID",
"i18n:govoplan-organizations.organization_model.4f924c0e": "Organisationsmodell",
"i18n:govoplan-organizations.organization_model_admin_description.35dc9f10": "Konfiguriere mandantenbezogene Einheitstypen, parallele Strukturen, Beziehungstypen und Funktionstypen des Organisationsmoduls.",
"i18n:govoplan-organizations.organization_settings.c9ab9829": "Organisationseinstellungen",
@@ -222,7 +193,7 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-organizations.organization_settings_saved.bfbcfdfa": "Organisationseinstellungen gespeichert.",
"i18n:govoplan-organizations.organization_tree.e5bfb195": "Organisationsbaum",
"i18n:govoplan-organizations.organizations.220edf64": "Organisationen",
"i18n:govoplan-organizations.organizations_intro.4e67c4bb": "Modelliere Organisationstypen, konkrete Einheiten, Strukturen, Funktionen und identitätsbezogene Funktionszuordnungen.",
"i18n:govoplan-organizations.organizations_intro.4e67c4bb": "Modelliere Organisationstypen, konkrete Einheiten, Strukturen und Funktionen.",
"i18n:govoplan-organizations.parent_unit.1986c35e": "Übergeordnete Einheit",
"i18n:govoplan-organizations.reactivate.58f2855d": "Reaktivieren",
"i18n:govoplan-organizations.relation_added.ca90461a": "Beziehung hinzugefügt.",
@@ -237,10 +208,8 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-organizations.save_settings.913aba9f": "Einstellungen speichern",
"i18n:govoplan-organizations.save_drafts.606f9401": "Entwürfe speichern",
"i18n:govoplan-organizations.saving.56a2285c": "Speichern...",
"i18n:govoplan-organizations.search_identities.004afdfd": "Nach Name, Subject, Konto oder ID suchen",
"i18n:govoplan-organizations.select_function.4895a67d": "Funktion auswählen",
"i18n:govoplan-organizations.select_function_type.2f426c43": "Funktionstyp auswählen",
"i18n:govoplan-organizations.select_identity.510f1134": "Identität auswählen",
"i18n:govoplan-organizations.select_relation_type.73f92478": "Beziehungstyp auswählen",
"i18n:govoplan-organizations.select_source_unit.9b5a29c8": "Quell-Einheit auswählen",
"i18n:govoplan-organizations.select_structure.c10f551c": "Struktur auswählen",
@@ -267,7 +236,6 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-organizations.unit_types.c7afc174": "Einheitstypen",
"i18n:govoplan-organizations.units.e14d0d92": "Einheiten",
"i18n:govoplan-organizations.unlimited.35569464": "Unbegrenzt",
"i18n:govoplan-organizations.update_assignment.bf130723": "Zuordnung aktualisieren",
"i18n:govoplan-organizations.update_function.504f03e5": "Funktion aktualisieren",
"i18n:govoplan-organizations.update_function_type.0b7e07d3": "Funktionstyp aktualisieren",
"i18n:govoplan-organizations.update_relation.4c44ce83": "Beziehung aktualisieren",

View File

@@ -1,6 +1,7 @@
import { createElement, lazy } from "react";
import type { AdminSectionsUiCapability, PlatformWebModule } from "@govoplan/core-webui";
import type { AdminSectionsUiCapability, OrganizationFunctionPickerUiCapability, PlatformWebModule } from "@govoplan/core-webui";
import { generatedTranslations } from "./i18n/generatedTranslations";
import { OrganizationFunctionLabel, OrganizationFunctionPicker } from "./features/organizations/OrganizationFunctionPicker";
import "./styles/organizations.css";
const OrganizationsPage = lazy(() => import("./features/organizations/OrganizationsPage"));
@@ -31,12 +32,18 @@ const organizationAdminSections: AdminSectionsUiCapability = {
]
};
const organizationFunctionPicker: OrganizationFunctionPickerUiCapability = {
sourceModule: "organizations",
renderPicker: (context) => createElement(OrganizationFunctionPicker, context),
renderLabel: (context) => createElement(OrganizationFunctionLabel, context)
};
export const organizationsModule: PlatformWebModule = {
id: "organizations",
label: "i18n:govoplan-organizations.organizations.220edf64",
version: "1.0.0",
dependencies: ["access"],
optionalDependencies: ["identity", "admin"],
optionalDependencies: ["admin"],
translations,
navItems: [
{
@@ -56,7 +63,8 @@ export const organizationsModule: PlatformWebModule = {
}
],
uiCapabilities: {
"admin.sections": organizationAdminSections
"admin.sections": organizationAdminSections,
"organizations.functionPicker": organizationFunctionPicker
}
};

View File

@@ -90,6 +90,10 @@
justify-content: flex-end;
}
.organizations-contributed-action {
display: inline-flex;
}
.organizations-check-list {
display: grid;
gap: 10px;
@@ -167,18 +171,6 @@
font-size: 13px;
}
.organizations-identity-picker {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 12px;
}
.organizations-identity {
display: grid;
gap: 2px;
min-width: 0;
}
.organizations-id {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 12px;