import { useState, useCallback, useEffect } from "react"; import { fetchClaudeOrgs, fetchClaudeUsage } from "@/lib/api/claudeApi"; import { loadClaudeSessionKey, saveClaudeSessionKey, clearClaudeSessionKey, } from "@/lib/storage"; import type { ClaudeUsageResponse } from "@/types/claude"; type Status = "idle" | "loading" | "success" | "error"; async function doFetch( sessionKey: string, set: { sessionKey: (v: string | null) => void; usage: (v: ClaudeUsageResponse | null) => void; status: (v: Status) => void; error: (v: string | null) => void; } ) { set.status("loading"); set.error(null); try { const orgs = await fetchClaudeOrgs(sessionKey); if (!orgs.length) throw new Error("NO_ORGS_FOUND"); const orgUuid = orgs[0].uuid; const data = await fetchClaudeUsage(sessionKey, orgUuid); set.usage(data); set.status("success"); } catch (e: unknown) { const msg = e instanceof Error ? e.message : "UNKNOWN_ERROR"; set.error(msg); set.status("error"); if (msg === "TOKEN_EXPIRED") { await clearClaudeSessionKey(); set.sessionKey(null); } } } export function useClaudeUsage() { const [sessionKey, setSessionKey] = useState(null); const [pendingKey, setPendingKey] = useState(""); const [usage, setUsage] = useState(null); const [status, setStatus] = useState("idle"); const [error, setError] = useState(null); const setters = { sessionKey: setSessionKey, usage: setUsage, status: setStatus, error: setError }; useEffect(() => { loadClaudeSessionKey().then((stored) => { if (stored) setSessionKey(stored); }); }, []); useEffect(() => { if (sessionKey) void doFetch(sessionKey, setters); }, [sessionKey]); const saveKey = useCallback(async () => { const trimmed = pendingKey.trim(); if (!trimmed.startsWith("sk-ant-")) { setError("Session key must start with sk-ant-"); return; } await saveClaudeSessionKey(trimmed); setSessionKey(trimmed); setPendingKey(""); setError(null); }, [pendingKey]); const refresh = useCallback(async () => { if (!sessionKey) return; await doFetch(sessionKey, setters); }, [sessionKey]); // Re-reads the session key from storage and fetches fresh data. Call from // useFocusEffect so the dashboard stays current when the key is saved from // another tab. If the string value is unchanged, React bails out on setState // so we trigger the fetch directly. const reload = useCallback(async () => { const stored = await loadClaudeSessionKey(); if (!stored) { setSessionKey(null); setUsage(null); setStatus("idle"); return; } // If already set to the same string, setState bails out and [sessionKey] // effect won't fire, so call the fetch directly. setSessionKey(stored); void doFetch(stored, setters); }, []); const clearKey = useCallback(async () => { await clearClaudeSessionKey(); setSessionKey(null); setUsage(null); setStatus("idle"); setError(null); }, []); return { sessionKey, pendingKey, setPendingKey, usage, status, error, saveKey, refresh, reload, clearKey, }; }