UNPKG

@hhoangphuoc/escape-room-cli

Version:

A CLI for playing AI-generated escape room games. Install globally with: npm install -g @hhoangphuoc/escape-room-cli

266 lines (265 loc) 10.3 kB
// // escape-room-cli/source/components/UsageDashboard.tsx export {}; // import React, { useState, useEffect } from 'react'; // import { Box, Text, Spacer } from 'ink'; // import { useAuth } from '../context/AuthContext.js'; // interface UserUsageMetrics { // userId: string; // totalRequests: number; // totalInputTokens: number; // totalOutputTokens: number; // totalTokens: number; // totalCost: number; // costByModel: Record<string, { // requests: number; // inputTokens: number; // outputTokens: number; // totalCost: number; // }>; // firstRequest: string; // lastRequest: string; // } // interface GameSessionMetrics { // gameId: string; // userId: string; // gameMode: string; // totalRequests: number; // totalInputTokens: number; // totalOutputTokens: number; // totalTokens: number; // totalCost: number; // startTime: string; // endTime?: string; // } // interface PricingInfo { // models: Record<string, { input: number; output: number; }>; // lastUpdated: string; // currency: string; // unit: string; // } // export const UsageDashboard: React.FC = () => { // const { apiCall } = useAuth(); // const [userMetrics, setUserMetrics] = useState<UserUsageMetrics | null>(null); // const [pricingInfo, setPricingInfo] = useState<PricingInfo | null>(null); // const [loading, setLoading] = useState(true); // const [error, setError] = useState<string | null>(null); // const [activeTab, setActiveTab] = useState<'overview' | 'models' | 'pricing'>('overview'); // useEffect(() => { // const fetchData = async () => { // try { // setLoading(true); // // Fetch user metrics // const userResponse = await apiCall('/api/usage/user'); // if (userResponse.success) { // setUserMetrics(userResponse.data); // } // // Fetch pricing info // const pricingResponse = await apiCall('/api/usage/pricing'); // if (pricingResponse.success) { // setPricingInfo(pricingResponse.data); // } // setError(null); // } catch (err) { // setError(err instanceof Error ? err.message : 'Failed to load usage data'); // } finally { // setLoading(false); // } // }; // fetchData(); // }, []); // const formatCost = (cost: number): string => { // return cost < 0.01 ? `$${cost.toFixed(5)}` : `$${cost.toFixed(3)}`; // }; // const formatTokens = (tokens: number): string => { // return tokens.toLocaleString(); // }; // const formatDate = (dateString: string): string => { // return new Date(dateString).toLocaleDateString(); // }; // if (loading) { // return ( // <Box justifyContent="center"> // <Text color="blue">Loading usage dashboard...</Text> // </Box> // ); // } // if (error) { // return ( // <Box justifyContent="center"> // <Text color="red">Error: {error}</Text> // </Box> // ); // } // return ( // <Box flexDirection="column" padding={1}> // {/* Header */} // <Box borderStyle="double" borderColor="cyan" padding={1}> // <Text color="cyan" bold>AI Usage Dashboard</Text> // <Spacer /> // <Text color="gray">User ID: {userMetrics?.userId || 'Unknown'}</Text> // </Box> // {/* Tab Navigation */} // <Box marginTop={1}> // <Box marginRight={2}> // <Text color={activeTab === 'overview' ? 'cyan' : 'gray'} bold={activeTab === 'overview'}> // [1] Overview // </Text> // </Box> // <Box marginRight={2}> // <Text color={activeTab === 'models' ? 'cyan' : 'gray'} bold={activeTab === 'models'}> // [2] Models // </Text> // </Box> // <Box> // <Text color={activeTab === 'pricing' ? 'cyan' : 'gray'} bold={activeTab === 'pricing'}> // [3] Pricing // </Text> // </Box> // </Box> // {/* Content */} // <Box marginTop={1} flexDirection="column"> // {activeTab === 'overview' && userMetrics && ( // <OverviewTab userMetrics={userMetrics} formatCost={formatCost} formatTokens={formatTokens} formatDate={formatDate} /> // )} // {activeTab === 'models' && userMetrics && ( // <ModelsTab userMetrics={userMetrics} formatCost={formatCost} formatTokens={formatTokens} /> // )} // {activeTab === 'pricing' && pricingInfo && ( // <PricingTab pricingInfo={pricingInfo} formatCost={formatCost} /> // )} // </Box> // {/* Footer */} // <Box marginTop={2} borderStyle="single" borderColor="gray" padding={1}> // <Text color="gray"> // Use number keys (1-3) to switch tabs. Press 'q' to return to game. // </Text> // </Box> // </Box> // ); // }; // const OverviewTab: React.FC<{ // userMetrics: UserUsageMetrics; // formatCost: (cost: number) => string; // formatTokens: (tokens: number) => string; // formatDate: (date: string) => string; // }> = ({ userMetrics, formatCost, formatTokens, formatDate }) => ( // <Box flexDirection="column"> // {/* Summary Stats */} // <Box borderStyle="single" borderColor="green" padding={1}> // <Box flexDirection="column" width="50%"> // <Text color="green" bold>Total Usage</Text> // <Text>Requests: {userMetrics.totalRequests}</Text> // <Text>Cost: {formatCost(userMetrics.totalCost)}</Text> // <Text>Tokens: {formatTokens(userMetrics.totalTokens)}</Text> // </Box> // <Box flexDirection="column" width="50%"> // <Text color="yellow" bold>Token Breakdown</Text> // <Text>Input: {formatTokens(userMetrics.totalInputTokens)}</Text> // <Text>Output: {formatTokens(userMetrics.totalOutputTokens)}</Text> // <Text>Avg/Request: {Math.round(userMetrics.totalTokens / Math.max(userMetrics.totalRequests, 1))}</Text> // </Box> // </Box> // {/* Date Range */} // <Box marginTop={1} borderStyle="single" borderColor="blue" padding={1}> // <Text color="blue" bold>Activity Period</Text> // <Spacer /> // <Text>First: {formatDate(userMetrics.firstRequest)}</Text> // <Text> → </Text> // <Text>Last: {formatDate(userMetrics.lastRequest)}</Text> // </Box> // {/* Cost Analysis */} // <Box marginTop={1} borderStyle="single" borderColor="magenta" padding={1}> // <Text color="magenta" bold>Cost Analysis</Text> // <Spacer /> // <Text>Avg/Request: {formatCost(userMetrics.totalCost / Math.max(userMetrics.totalRequests, 1))}</Text> // </Box> // </Box> // ); // const ModelsTab: React.FC<{ // userMetrics: UserUsageMetrics; // formatCost: (cost: number) => string; // formatTokens: (tokens: number) => string; // }> = ({ userMetrics, formatCost, formatTokens }) => ( // <Box flexDirection="column"> // <Text color="cyan" bold>Model Usage Breakdown</Text> // {Object.keys(userMetrics.costByModel).length === 0 ? ( // <Box marginTop={1}> // <Text color="gray">No model usage data available</Text> // </Box> // ) : ( // <Box marginTop={1} flexDirection="column"> // {Object.entries(userMetrics.costByModel) // .sort(([,a], [,b]) => b.totalCost - a.totalCost) // .map(([model, data]) => ( // <Box key={model} borderStyle="single" borderColor="yellow" padding={1} marginTop={1}> // <Box flexDirection="column" width="100%"> // <Text color="yellow" bold>{model}</Text> // <Box> // <Box width="33%"> // <Text>Requests: {data.requests}</Text> // </Box> // <Box width="33%"> // <Text>Cost: {formatCost(data.totalCost)}</Text> // </Box> // <Box width="34%"> // <Text>Tokens: {formatTokens(data.inputTokens + data.outputTokens)}</Text> // </Box> // </Box> // <Box> // <Box width="50%"> // <Text color="green">Input: {formatTokens(data.inputTokens)}</Text> // </Box> // <Box width="50%"> // <Text color="blue">Output: {formatTokens(data.outputTokens)}</Text> // </Box> // </Box> // <Text color="gray"> // Avg: {formatCost(data.totalCost / Math.max(data.requests, 1))}/req // </Text> // </Box> // </Box> // ))} // </Box> // )} // </Box> // ); // const PricingTab: React.FC<{ // pricingInfo: PricingInfo; // formatCost: (cost: number) => string; // }> = ({ pricingInfo, formatCost }) => ( // <Box flexDirection="column"> // <Box> // <Text color="cyan" bold>Current OpenAI Pricing</Text> // <Spacer /> // <Text color="gray">{pricingInfo.unit} in {pricingInfo.currency}</Text> // </Box> // <Box marginTop={1} flexDirection="column"> // {Object.entries(pricingInfo.models) // .sort(([,a], [,b]) => b.input - a.input) // .map(([model, pricing]) => ( // <Box key={model} borderStyle="single" borderColor="green" padding={1} marginTop={1}> // <Box flexDirection="column" width="100%"> // <Text color="green" bold>{model}</Text> // <Box> // <Box width="50%"> // <Text color="yellow">Input: {formatCost(pricing.input)}/1K</Text> // </Box> // <Box width="50%"> // <Text color="blue">Output: {formatCost(pricing.output)}/1K</Text> // </Box> // </Box> // <Text color="gray"> // Ratio: 1:{(pricing.output / pricing.input).toFixed(1)} (out:in) // </Text> // </Box> // </Box> // ))} // </Box> // <Box marginTop={2} borderStyle="single" borderColor="gray" padding={1}> // <Text color="gray"> // Last updated: {pricingInfo.lastUpdated} // </Text> // </Box> // </Box> // ); // export default UsageDashboard;