import { useCallback, useEffect, useState } from 'react'; import type { ApiRespBoxStatus } from '@/app/infra/entities/api'; import { httpClient } from '@/app/infra/http/HttpClient'; /** * Shared hook for Box runtime status — used by every UI surface that needs * to gate behaviour on whether the sandbox is available. Returns: * * - status: full payload (or null while loading / on error) * - available: convenience flag (status?.available === true) * - disabled: true iff Box is explicitly disabled by config * (status.enabled === false), distinguishing it from * "configured but currently failed" * - hint: a single i18n-key choice for the banner message — * 'boxDisabled' / 'boxUnavailable' / null * - refresh: imperative re-fetch * * Polls every ``refreshMs`` (default 30s) so a flapping runtime is picked * up without a page reload. */ export function useBoxStatus(refreshMs = 30_000) { const [status, setStatus] = useState(null); const [loading, setLoading] = useState(true); const refresh = useCallback(async () => { try { const data = await httpClient.getBoxStatus(); setStatus(data); } catch { // Keep last-known status; the dashboard polls separately so a // transient failure here should not blank the UI for sandbox // consumers. } finally { setLoading(false); } }, []); useEffect(() => { void refresh(); const id = setInterval(() => void refresh(), refreshMs); return () => clearInterval(id); }, [refresh, refreshMs]); const available = status?.available === true; const disabled = status?.available === false && status?.enabled === false; const hint: 'boxDisabled' | 'boxUnavailable' | null = available ? null : disabled ? 'boxDisabled' : status ? 'boxUnavailable' : null; // Specific reason from the backend (e.g. // ``Configured sandbox backend "nsjail" is unavailable`` or // ``docker daemon not running``). Surface this in the failed-state // banner so the user sees WHY instead of a generic "unavailable". // For the disabled-by-config case the boxDisabled i18n string already // carries the reason, so we suppress the duplicate. const reason = hint === 'boxUnavailable' ? status?.connector_error?.trim() || null : null; return { status, loading, available, disabled, hint, reason, refresh }; }