import * as React from 'react'; import { useAuth } from '@/lib/auth'; import { TokenManager } from '@sentryagent/idp-sdk'; /** Shape of the GET /api/v1/billing/usage response. */ interface UsageResponse { tenantId: string; date: string; apiCalls: number; agentCount: number; subscriptionStatus: string; currentPeriodEnd: string | null; stripeSubscriptionId: string | null; } type LoadState = 'idle' | 'loading' | 'success' | 'error'; interface UsageState { loadState: LoadState; data: UsageResponse | null; errorMessage: string | null; } const initialState: UsageState = { loadState: 'idle', data: null, errorMessage: null, }; /** * Fetches the current usage summary from the API using the stored credentials. * * @param baseUrl - The API base URL. * @param clientId - The agent client ID. * @param clientSecret - The agent client secret. * @returns The usage response from the server. */ async function fetchUsage( baseUrl: string, clientId: string, clientSecret: string, ): Promise { const tokenManager = new TokenManager( baseUrl, clientId, clientSecret, 'agents:read', ); const token = await tokenManager.getToken(); const response = await fetch(`${baseUrl}/api/v1/billing/usage`, { headers: { Authorization: `Bearer ${token}` }, }); if (!response.ok) { throw new Error(`Failed to fetch usage data (HTTP ${response.status})`); } return response.json() as Promise; } /** Badge shown for the tenant's subscription tier. */ function SubscriptionBadge({ status }: { status: string }): React.JSX.Element { const isPro = status !== 'free'; return ( {isPro ? 'Pro' : 'Free Tier'} ); } /** A single metric card with label and value. */ function MetricCard({ label, value }: { label: string; value: string | number }): React.JSX.Element { return (

{label}

{value}

); } /** * Displays the current tenant's usage summary: * - API calls today * - Active agent count * - Subscription status (Free Tier / Pro) * * Fetches GET /api/v1/billing/usage with the current Bearer token. * Handles loading state and error state gracefully. */ export function UsagePanel(): React.JSX.Element { const { credentials } = useAuth(); const [state, setState] = React.useState(initialState); const loadUsage = React.useCallback(async (): Promise => { if (!credentials) return; setState((prev) => ({ ...prev, loadState: 'loading', errorMessage: null })); try { const data = await fetchUsage( credentials.baseUrl, credentials.clientId, credentials.clientSecret, ); setState({ loadState: 'success', data, errorMessage: null }); } catch (err) { const message = err instanceof Error ? err.message : 'Unknown error occurred.'; setState({ loadState: 'error', data: null, errorMessage: message }); } }, [credentials]); React.useEffect(() => { void loadUsage(); }, [loadUsage]); const isLoading = state.loadState === 'loading' || state.loadState === 'idle'; return (

Usage & Billing

{/* Error state */} {state.loadState === 'error' && (
{state.errorMessage ?? 'Failed to load usage data.'}
)} {/* Loading skeleton */} {isLoading && (
{[1, 2, 3].map((i) => (
))}
)} {/* Data */} {state.loadState === 'success' && state.data !== null && ( <>

Showing usage for {state.data.date}

{state.data.subscriptionStatus === 'free' && (

You are on the Free Tier — limited to 10 agents and 1,000 API calls/day.

Upgrade to Pro for unlimited agents and API calls.

)} {state.data.currentPeriodEnd !== null && (

Current period ends:{' '} {new Date(state.data.currentPeriodEnd).toLocaleDateString()}

)} )}
); }