115 lines
4.1 KiB
TypeScript
115 lines
4.1 KiB
TypeScript
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
|
|
};
|
|
}
|