import { useEffect, useState, useCallback } from 'react'; import { usePatientServiceContext, useGoalServiceContext } from '../services'; import { describeFetchFailure, isRefusedRead } from '../utils/fetchFailureMessage'; interface UseFhirGoalsOptions { autoLoad?: boolean; errorMessage?: string; forceReload?: boolean; } interface UseFhirGoalsReturn { loading: boolean; error: string | undefined; /** The last load was answered with a 403: refused, not lost. */ refused: boolean; /** The read for this patient has answered, one way or the other. */ attempted: boolean; loadGoals: () => Promise; clearError: () => void; goalsBundle: ReturnType['goalsBundle']; goalsList: ReturnType['goalsList']; /** Goals that changed but could not be read again; they stay shown until a read answers. */ uncheckedGoals: string[]; recheckGoal: (goalId: string) => void; } export const useFhirGoals = (options: UseFhirGoalsOptions = {}): UseFhirGoalsReturn => { const { autoLoad = true, errorMessage = "Failed to load goals", forceReload = false } = options; const { patientState } = usePatientServiceContext(); const { cachedForPatientId, fetchGoalsWithPolling, goalsBundle, goalsList, uncheckedGoals, recheckGoal } = useGoalServiceContext(); const [loading, setLoading] = useState(false); const [error, setError] = useState(); const [refused, setRefused] = useState(false); const loadGoals = useCallback(async () => { if (!patientState) { return; } setLoading(true); setError(undefined); setRefused(false); try { await fetchGoalsWithPolling(patientState); } catch (err) { console.error('Failed to load goals:', err); setError(describeFetchFailure(err, errorMessage)); setRefused(isRefusedRead(err)); } finally { setLoading(false); } }, [patientState, fetchGoalsWithPolling, errorMessage]); const clearError = useCallback(() => { setError(undefined); }, []); useEffect(() => { if (!autoLoad) return; if (!patientState) return; // A bundle belonging to SOMEBODY ELSE is not a reason not to ask. This used to fire only when // there was no bundle at all, so moving between two charts left the previous patient's list in // place, with no request and nothing on screen to say it was not theirs. // NOT a refetch on every navigation: same patient, bundle loaded, no request. if (!goalsBundle || cachedForPatientId !== patientState.id || forceReload) { loadGoals(); } }, [autoLoad, patientState, goalsBundle, cachedForPatientId, forceReload, loadGoals]); return { loading, error, refused, attempted: Boolean(error) || (Boolean(goalsBundle) && cachedForPatientId === patientState?.id), loadGoals, clearError, goalsBundle, goalsList, uncheckedGoals, recheckGoal: (goalId: string) => { void recheckGoal(goalId); } }; };