import { useCallback, useEffect, useState } from "react"; import type { FieldConfig, FKFieldConfig } from "../types"; import { useAppContext } from "../context/AppContext"; import { getApi } from "./useApi"; export interface FkOption { value: any; label: string; } function applyFormat(obj: any, format: string): string { if (!obj || typeof obj !== "object") return String(obj ?? ""); return format.replace(/\{(\w+)\}/g, (_, key) => String(obj[key] ?? "")); } /** * Loads select options for FK-annotated fields of a field set. * Option values bind to `fk.value` when set (e.g. account names), * otherwise to the target resource's primary key. */ export function useFkFieldOptions(fields: FieldConfig[]) { const { resources } = useAppContext(); const [fkOptions, setFkOptions] = useState>({}); const [fkLoading, setFkLoading] = useState>({}); const loadFkOptions = useCallback( async (fieldName: string, fk: FKFieldConfig) => { setFkLoading((prev) => ({ ...prev, [fieldName]: true })); try { const targetRes = resources.find((r) => r.name === fk.resource); if (!targetRes) return; const api = getApi(); const params: Record = {}; if (targetRes.pagination) params.limit = 0; const res = await api.get(targetRes.path, { params }); let items: any[]; if (targetRes.pagination) { if (!res.data || typeof res.data !== "object" || !Array.isArray(res.data.items)) { throw new Error(`Expected paginated response from ${targetRes.path}`); } items = res.data.items; } else { if (!Array.isArray(res.data)) { throw new Error(`Expected array response from ${targetRes.path}`); } items = res.data; } const opts: FkOption[] = items.map((item: any) => ({ value: item[fk.value ?? targetRes.primaryKey], label: applyFormat(item, targetRes.displayFormat), })); setFkOptions((prev) => ({ ...prev, [fieldName]: opts })); } catch { // leave options empty; the field renders without suggestions } finally { setFkLoading((prev) => ({ ...prev, [fieldName]: false })); } }, [resources] ); useEffect(() => { fields.forEach((f) => { if (f.fk?.prefetch) loadFkOptions(f.name, f.fk); }); }, [fields, loadFkOptions]); const loadFkOnOpen = useCallback( (fieldName: string) => { const field = fields.find((f) => f.name === fieldName); if (field?.fk && !field.fk.prefetch && !fkOptions[fieldName]) { loadFkOptions(fieldName, field.fk); } }, [fields, fkOptions, loadFkOptions] ); return { fkOptions, fkLoading, loadFkOnOpen }; }