/** * Hook: fetch origin countries from GET /parcel2go-shipping/v1/countries/origins. * Results are cached in memory for 2 days; data rarely changes. */ import { useEffect, useState, useCallback } from '@wordpress/element'; import apiFetch from '@wordpress/api-fetch'; import type { ServiceCountry, CountriesOriginsResponse } from '../../../types'; import { getApiErrorMessage } from '../../../shared/utils'; import { captureException } from '../../../shared/sentry'; const CACHE_TTL_MS = 2 * 24 * 60 * 60 * 1000; // 2 days let cache: { data: ServiceCountry[]; expiresAt: number } | null = null; function isCacheValid(): boolean { return cache !== null && Date.now() < cache.expiresAt; } export interface UseCountriesReturn { countries: ServiceCountry[]; loading: boolean; error: string | null; refetch: () => Promise; } export function useCountries(): UseCountriesReturn { const [countries, setCountries] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const refetch = useCallback(async () => { setError(null); if (isCacheValid() && cache) { setCountries(cache.data); setLoading(false); return; } setLoading(true); try { const json = (await apiFetch({ path: '/parcel2go-shipping/v1/countries/origins', })) as CountriesOriginsResponse; const list = json?.countries ?? []; cache = { data: list, expiresAt: Date.now() + CACHE_TTL_MS }; setCountries(list); } catch (e: unknown) { captureException(e instanceof Error ? e : new Error(String(e))); setError(getApiErrorMessage(e, 'Failed to load countries.')); } finally { setLoading(false); } }, []); useEffect(() => { refetch(); }, [refetch]); return { countries, loading, error, refetch }; }