/** * React hook for loading and updating global configuration. */ import { useCallback, useEffect, useRef, useState } from "react"; import { fetchGlobalConfig, updateGlobalConfig } from "../api/config"; import { handleFetchError, createStringErrorAdapter } from "../utils/fetchError"; import type { GlobalConfig, GlobalConfigUpdate } from "../types/config"; export interface UseGlobalConfigResult { config: GlobalConfig | null; loading: boolean; error: string | null; refresh: () => void; updateConfig: (update: GlobalConfigUpdate) => Promise; } /** * Load global configuration and expose update operations. */ export function useGlobalConfig(): UseGlobalConfigResult { const [config, setConfig] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const abortRef = useRef(null); const load = useCallback((): void => { abortRef.current?.abort(); const ctrl = new AbortController(); abortRef.current = ctrl; setLoading(true); setError(null); fetchGlobalConfig() .then((resp) => { if (!ctrl.signal.aborted) { setConfig(resp); } }) .catch((err: unknown) => { if (!ctrl.signal.aborted) { handleFetchError(err, createStringErrorAdapter(setError), "Failed to fetch global config"); } }) .finally(() => { if (!ctrl.signal.aborted) { setLoading(false); } }); }, []); useEffect(() => { load(); return (): void => { abortRef.current?.abort(); }; }, [load]); const updateConfig = useCallback( async (update: GlobalConfigUpdate): Promise => { await updateGlobalConfig(update); load(); }, [load], ); return { config, loading, error, refresh: load, updateConfig }; }