Sync GovOPlaN module state
This commit is contained in:
@@ -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
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user