import { useEffect, useState } from "react"; import { apiClient, ApiError } from "../api/client"; // Cold start retry configuration (matches useSegmentation.ts) const MAX_COLD_START_RETRIES = 5; const INITIAL_RETRY_DELAY = 2000; const MAX_RETRY_DELAY = 30000; interface CaseSelectorProps { selectedCase: string | null; onSelectCase: (caseId: string) => void; } export function CaseSelector({ selectedCase, onSelectCase, }: CaseSelectorProps) { const [cases, setCases] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [retryCount, setRetryCount] = useState(0); const [isWakingUp, setIsWakingUp] = useState(false); // Fetch cases on mount with cold-start retry logic // Using inline async function pattern recommended by React docs for data fetching useEffect(() => { let isActive = true; const abortController = new AbortController(); async function fetchCases() { let attempts = 0; while (attempts <= MAX_COLD_START_RETRIES && isActive) { try { const data = await apiClient.getCases(abortController.signal); if (!isActive) return; setCases(data.cases); setIsWakingUp(false); setRetryCount(0); setIsLoading(false); return; // Success } catch (err) { if (!isActive) return; if (err instanceof Error && err.name === "AbortError") return; const is503 = err instanceof ApiError && err.status === 503; const isNetworkError = err instanceof TypeError && err.message.toLowerCase().includes("fetch"); // Retry on cold start (503) or network errors if ((is503 || isNetworkError) && attempts < MAX_COLD_START_RETRIES) { attempts++; setRetryCount(attempts); setIsWakingUp(true); // Exponential backoff const delay = Math.min( INITIAL_RETRY_DELAY * Math.pow(2, attempts - 1), MAX_RETRY_DELAY, ); await new Promise((resolve) => setTimeout(resolve, delay)); continue; } // Max retries exceeded or non-retryable error const message = is503 || isNetworkError ? "Backend failed to wake up. Please refresh the page." : err instanceof Error ? err.message : "Unknown error"; setError(`Failed to load cases: ${message}`); setIsWakingUp(false); setIsLoading(false); return; } } } fetchCases(); return () => { isActive = false; abortController.abort(); }; }, []); if (isLoading) { return (
{isWakingUp ? (

Backend waking up... Retry {retryCount}/{MAX_COLD_START_RETRIES}

) : (

Loading cases...

)}
); } if (error) { return (

{error}

); } return (
); }