/** * Billing Store (Zustand) * Manages credit balance and usage tracking with graceful error state */ import { create } from 'zustand'; import { getCreditBalance, getCreditUsageLimits, getCreditUsageSummary, type CreditBalance, } from '../services/billing.api'; interface BillingState { balance: CreditBalance | null; usageSummary: any | null; usageLimits: any | null; loading: boolean; error: string | null; lastUpdated?: string | null; // Actions loadBalance: () => Promise; loadUsageSummary: (startDate?: string, endDate?: string) => Promise; loadUsageLimits: () => Promise; reset: () => void; } export const useBillingStore = create((set, get) => ({ balance: null, usageSummary: null, usageLimits: null, loading: false, error: null, lastUpdated: null, loadBalance: async () => { // keep existing balance while retrying set({ loading: true, error: null }); try { const balance = await getCreditBalance(); set({ balance, loading: false, error: null, lastUpdated: new Date().toISOString() }); } catch (error: any) { set({ error: error.message || 'Balance unavailable', loading: false, balance: null }); } }, loadUsageSummary: async (startDate?: string, endDate?: string) => { set({ loading: true, error: null }); try { const summary = await getCreditUsageSummary({ start_date: startDate, end_date: endDate } as any); set({ usageSummary: summary, loading: false }); } catch (error: any) { set({ error: error.message, loading: false }); } }, loadUsageLimits: async () => { set({ loading: true, error: null }); try { const limits = await getCreditUsageLimits(); set({ usageLimits: limits, loading: false }); } catch (error: any) { // If limits endpoint is not available (404), keep going without hard error if (error?.status === 404) { set({ usageLimits: null, loading: false }); return; } set({ error: error.message || 'Usage limits unavailable', loading: false }); } }, reset: () => { set({ balance: null, usageSummary: null, usageLimits: null, loading: false, error: null, }); }, }));