-
- Admin Dashboard
-
-
- Monitor and manage wallet balances
+
+ Usage Analytics
+
+
+ Monitor requests, errors, and revenue over the last {timeRange} hours
-
-
{
- if (value) {
- setDisplayUnit(value as DisplayUnit);
- }
- }}
- variant='outline'
- size='sm'
- >
- mSAT
- sat
-
- USD
-
-
+
+
+
+
-
-
-
+
+ {summaryLoading ? (
+
Loading summary...
+ ) : summaryData ? (
+
+ ) : null}
+
+
+ {metricsLoading ? (
+
+ Loading metrics...
+
+ ) : metricsData && metricsData.metrics.length > 0 ? (
+ <>
+
+ ({
+ ...m,
+ revenue_sats: m.revenue_msats / 1000,
+ refunds_sats: m.refunds_msats / 1000,
+ net_revenue_sats:
+ (m.revenue_msats - m.refunds_msats) / 1000,
+ })) as Array & { timestamp: string }>}
+ title='Revenue Over Time (sats)'
+ dataKeys={[
+ {
+ key: 'revenue_sats',
+ name: 'Revenue',
+ color: '#10b981',
+ },
+ {
+ key: 'net_revenue_sats',
+ name: 'Net Revenue',
+ color: '#059669',
+ },
+ {
+ key: 'refunds_sats',
+ name: 'Refunds',
+ color: '#ef4444',
+ },
+ ]}
+ />
+
+
& { timestamp: string }>}
+ title='Request Volume'
+ dataKeys={[
+ {
+ key: 'total_requests',
+ name: 'Total Requests',
+ color: '#3b82f6',
+ },
+ {
+ key: 'successful_chat_completions',
+ name: 'Successful',
+ color: '#22c55e',
+ },
+ {
+ key: 'failed_requests',
+ name: 'Failed',
+ color: '#f43f5e',
+ },
+ ]}
+ />
+ & { timestamp: string }>}
+ title='Error Tracking'
+ dataKeys={[
+ {
+ key: 'errors',
+ name: 'Errors',
+ color: '#f97316',
+ },
+ {
+ key: 'warnings',
+ name: 'Warnings',
+ color: '#eab308',
+ },
+ {
+ key: 'upstream_errors',
+ name: 'Upstream Errors',
+ color: '#dc2626',
+ },
+ ]}
+ />
+ & { timestamp: string }>}
+ title='Payment Activity'
+ dataKeys={[
+ {
+ key: 'payment_processed',
+ name: 'Payments Processed',
+ color: '#8b5cf6',
+ },
+ ]}
+ />
+
+ {summaryData && summaryData.unique_models.length > 0 && (
+
+
+ Active Models
+
+
+
+ {summaryData.unique_models.map((model) => (
+
+ {model}
+
+ ))}
+
+
+
+ )}
+ {summaryData &&
+ summaryData.error_types &&
+ Object.keys(summaryData.error_types).length > 0 && (
+
+
+ Error Types Distribution
+
+
+
+ {Object.entries(summaryData.error_types)
+ .sort(([, a], [, b]) => b - a)
+ .map(([type, count]) => (
+
+ {type}
+
+ {count}
+
+
+ ))}
+
+
+
+ )}
+
+ >
+ ) : (
+
+
+ No Data Available
+
+
+
+ No metrics data found for the selected time range. This
+ could be because no requests have been logged yet or the
+ log files are not available.
+
+
+
+ )}
-
- Loading revenue by model...
+ ) : revenueByModelData && revenueByModelData.models.length > 0 ? (
+
-
+ ) : null}
+
+ {errorLoading ? (
+
Loading errors...
+ ) : errorData ? (
+
+ ) : null}
diff --git a/ui/app/usage/page.tsx b/ui/app/usage/page.tsx
deleted file mode 100644
index 69673470..00000000
--- a/ui/app/usage/page.tsx
+++ /dev/null
@@ -1,302 +0,0 @@
-'use client';
-
-import { useState } from 'react';
-import { useQuery } from '@tanstack/react-query';
-import { AppSidebar } from '@/components/app-sidebar';
-import { SiteHeader } from '@/components/site-header';
-import { SidebarInset, SidebarProvider } from '@/components/ui/sidebar';
-import { UsageMetricsChart } from '@/components/usage-metrics-chart';
-import { UsageSummaryCards } from '@/components/usage-summary-cards';
-import { ErrorDetailsTable } from '@/components/error-details-table';
-import { RevenueByModelTable } from '@/components/revenue-by-model-table';
-import { AdminService } from '@/lib/api/services/admin';
-import { Button } from '@/components/ui/button';
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from '@/components/ui/select';
-import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
-import { RefreshCw } from 'lucide-react';
-
-export default function UsagePage() {
- const [timeRange, setTimeRange] = useState('24');
- const [interval, setInterval] = useState('15');
-
- const {
- data: metricsData,
- isLoading: metricsLoading,
- refetch: refetchMetrics,
- } = useQuery({
- queryKey: ['usage-metrics', interval, timeRange],
- queryFn: () =>
- AdminService.getUsageMetrics(parseInt(interval), parseInt(timeRange)),
- refetchInterval: 60_000,
- staleTime: 30_000,
- });
-
- const {
- data: summaryData,
- isLoading: summaryLoading,
- refetch: refetchSummary,
- } = useQuery({
- queryKey: ['usage-summary', timeRange],
- queryFn: () => AdminService.getUsageSummary(parseInt(timeRange)),
- refetchInterval: 60_000,
- staleTime: 30_000,
- });
-
- const {
- data: errorData,
- isLoading: errorLoading,
- refetch: refetchErrors,
- } = useQuery({
- queryKey: ['usage-errors', timeRange],
- queryFn: () => AdminService.getErrorDetails(parseInt(timeRange), 100),
- refetchInterval: 60_000,
- staleTime: 30_000,
- });
-
- const {
- data: revenueByModelData,
- isLoading: revenueByModelLoading,
- refetch: refetchRevenueByModel,
- } = useQuery({
- queryKey: ['revenue-by-model', timeRange],
- queryFn: () => AdminService.getRevenueByModel(parseInt(timeRange), 20),
- refetchInterval: 60_000,
- staleTime: 30_000,
- });
-
- const handleRefresh = () => {
- refetchMetrics();
- refetchSummary();
- refetchErrors();
- refetchRevenueByModel();
- };
-
- return (
-
-
-
-
-
-
-
-
- Usage Tracking
-
-
- Monitor system usage, requests, and errors over time
-
-
-
-
-
-
-
-
-
-
- {summaryLoading ? (
-
Loading summary...
- ) : summaryData ? (
-
- ) : null}
-
-
- {metricsLoading ? (
-
- Loading metrics...
-
- ) : metricsData && metricsData.metrics.length > 0 ? (
- <>
-
& { timestamp: string }>}
- title='Request Volume'
- dataKeys={[
- {
- key: 'total_requests',
- name: 'Total Requests',
- color: '#3b82f6',
- },
- {
- key: 'successful_chat_completions',
- name: 'Successful',
- color: '#10b981',
- },
- {
- key: 'failed_requests',
- name: 'Failed',
- color: '#ef4444',
- },
- ]}
- />
- ({
- ...m,
- revenue_sats: m.revenue_msats / 1000,
- refunds_sats: m.refunds_msats / 1000,
- net_revenue_sats:
- (m.revenue_msats - m.refunds_msats) / 1000,
- })) as Array & { timestamp: string }>}
- title='Revenue Over Time (sats)'
- dataKeys={[
- {
- key: 'revenue_sats',
- name: 'Revenue',
- color: '#10b981',
- },
- {
- key: 'refunds_sats',
- name: 'Refunds',
- color: '#ef4444',
- },
- {
- key: 'net_revenue_sats',
- name: 'Net Revenue',
- color: '#059669',
- },
- ]}
- />
- & { timestamp: string }>}
- title='Error Tracking'
- dataKeys={[
- {
- key: 'errors',
- name: 'Errors',
- color: '#f97316',
- },
- {
- key: 'warnings',
- name: 'Warnings',
- color: '#eab308',
- },
- {
- key: 'upstream_errors',
- name: 'Upstream Errors',
- color: '#ef4444',
- },
- ]}
- />
- & { timestamp: string }>}
- title='Payment Activity'
- dataKeys={[
- {
- key: 'payment_processed',
- name: 'Payments Processed',
- color: '#6366f1',
- },
- ]}
- />
- >
- ) : (
-
-
- No Data Available
-
-
-
- No metrics data found for the selected time range. This
- could be because no requests have been logged yet or the
- log files are not available.
-
-
-
- )}
-
-
- {revenueByModelLoading ? (
-
Loading revenue by model...
- ) : revenueByModelData && revenueByModelData.models.length > 0 ? (
-
- ) : null}
-
- {errorLoading ? (
-
Loading errors...
- ) : errorData ? (
-
- ) : null}
-
- {summaryData && summaryData.unique_models.length > 0 && (
-
-
- Active Models
-
-
-
- {summaryData.unique_models.map((model) => (
-
- {model}
-
- ))}
-
-
-
- )}
-
- {summaryData &&
- summaryData.error_types &&
- Object.keys(summaryData.error_types).length > 0 && (
-
-
- Error Types Distribution
-
-
-
- {Object.entries(summaryData.error_types)
- .sort(([, a], [, b]) => b - a)
- .map(([type, count]) => (
-
- {type}
-
- {count}
-
-
- ))}
-
-
-
- )}
-
-
-
-
- );
-}