feat(ui): apply global currency formatting to stats components

- Update UsageSummaryCards to use global currency store and formatting
- Update RevenueByModelTable to use global currency store and formatting
- Ensure consistent sat/msat/usd display across all dashboard metrics
This commit is contained in:
Shroominic
2025-11-23 21:51:36 +00:00
committed by Cursor Agent
parent 3371b03068
commit 6f4e57eeff
2 changed files with 47 additions and 21 deletions
+24 -8
View File
@@ -11,6 +11,10 @@ import {
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Progress } from '@/components/ui/progress';
import { ModelRevenueData } from '@/lib/api/services/admin';
import { useCurrencyStore } from '@/lib/stores/currency';
import { useQuery } from '@tanstack/react-query';
import { fetchBtcUsdPrice, btcToSatsRate } from '@/lib/exchange-rate';
import { formatFromMsat, convertToMsat } from '@/lib/currency';
interface RevenueByModelTableProps {
models: ModelRevenueData[];
@@ -21,12 +25,24 @@ export function RevenueByModelTable({
models,
totalRevenue,
}: RevenueByModelTableProps) {
const { displayUnit } = useCurrencyStore();
const { data: btcUsdPrice } = useQuery({
queryKey: ['btc-usd-price'],
queryFn: fetchBtcUsdPrice,
refetchInterval: 120_000,
staleTime: 60_000,
});
const usdPerSat = btcUsdPrice ? btcToSatsRate(btcUsdPrice) : null;
const formatAmount = (sats: number) =>
formatFromMsat(convertToMsat(sats, 'sat'), displayUnit, usdPerSat);
return (
<Card>
<CardHeader>
<CardTitle>Revenue by Model</CardTitle>
<p className="text-sm text-muted-foreground">
Total Revenue: <span className="font-mono font-medium text-foreground">{totalRevenue.toLocaleString(undefined, { maximumFractionDigits: 2 })}</span> sats
Total Revenue: <span className="font-mono font-medium text-foreground">{formatAmount(totalRevenue)}</span>
</p>
</CardHeader>
<CardContent>
@@ -37,10 +53,10 @@ export function RevenueByModelTable({
<TableHead className="text-right">Requests</TableHead>
<TableHead className="text-right">Successful</TableHead>
<TableHead className="text-right">Failed</TableHead>
<TableHead className="text-right">Revenue (sats)</TableHead>
<TableHead className="text-right">Revenue</TableHead>
<TableHead className="w-[100px]">Share</TableHead>
<TableHead className="text-right">Refunds (sats)</TableHead>
<TableHead className="text-right">Net Revenue (sats)</TableHead>
<TableHead className="text-right">Refunds</TableHead>
<TableHead className="text-right">Net Revenue</TableHead>
<TableHead className="text-right">Avg/Request</TableHead>
</TableRow>
</TableHeader>
@@ -61,7 +77,7 @@ export function RevenueByModelTable({
<TableCell className="text-right text-green-600 font-mono">{model.successful}</TableCell>
<TableCell className="text-right text-red-600 font-mono">{model.failed}</TableCell>
<TableCell className="text-right font-mono">
{model.revenue_sats.toLocaleString(undefined, { maximumFractionDigits: 2 })}
{formatAmount(model.revenue_sats)}
</TableCell>
<TableCell>
<div className="flex items-center gap-2">
@@ -70,13 +86,13 @@ export function RevenueByModelTable({
</div>
</TableCell>
<TableCell className="text-right text-red-500 font-mono">
{model.refunds_sats.toLocaleString(undefined, { maximumFractionDigits: 2 })}
{formatAmount(model.refunds_sats)}
</TableCell>
<TableCell className="text-right font-semibold font-mono">
{model.net_revenue_sats.toLocaleString(undefined, { maximumFractionDigits: 2 })}
{formatAmount(model.net_revenue_sats)}
</TableCell>
<TableCell className="text-right text-muted-foreground font-mono">
{model.avg_revenue_per_request.toLocaleString(undefined, { maximumFractionDigits: 3 })}
{formatAmount(model.avg_revenue_per_request)}
</TableCell>
</TableRow>
);
+23 -13
View File
@@ -14,12 +14,28 @@ import {
TrendingDown,
Coins,
} from 'lucide-react';
import { useCurrencyStore } from '@/lib/stores/currency';
import { useQuery } from '@tanstack/react-query';
import { fetchBtcUsdPrice, btcToSatsRate } from '@/lib/exchange-rate';
import { formatFromMsat } from '@/lib/currency';
interface UsageSummaryCardsProps {
summary: UsageSummary;
}
export function UsageSummaryCards({ summary }: UsageSummaryCardsProps) {
const { displayUnit } = useCurrencyStore();
const { data: btcUsdPrice } = useQuery({
queryKey: ['btc-usd-price'],
queryFn: fetchBtcUsdPrice,
refetchInterval: 120_000,
staleTime: 60_000,
});
const usdPerSat = btcUsdPrice ? btcToSatsRate(btcUsdPrice) : null;
const formatAmount = (msat: number) =>
formatFromMsat(msat, displayUnit, usdPerSat);
const cards = [
{
title: 'Total Requests',
@@ -34,32 +50,26 @@ export function UsageSummaryCards({ summary }: UsageSummaryCardsProps) {
color: 'text-green-500',
},
{
title: 'Revenue (sats)',
value: summary.revenue_sats.toLocaleString(undefined, {
maximumFractionDigits: 2,
}),
title: 'Revenue',
value: formatAmount(summary.revenue_msats),
icon: Coins,
color: 'text-green-600',
},
{
title: 'Net Revenue (sats)',
value: summary.net_revenue_sats.toLocaleString(undefined, {
maximumFractionDigits: 2,
}),
title: 'Net Revenue',
value: formatAmount(summary.net_revenue_msats),
icon: DollarSign,
color: 'text-emerald-600',
},
{
title: 'Refunds (sats)',
value: summary.refunds_sats.toLocaleString(undefined, {
maximumFractionDigits: 2,
}),
title: 'Refunds',
value: formatAmount(summary.refunds_msats),
icon: TrendingDown,
color: 'text-red-500',
},
{
title: 'Avg Revenue/Request',
value: `${(summary.avg_revenue_per_request_msats / 1000).toLocaleString(undefined, { maximumFractionDigits: 3 })} sats`,
value: formatAmount(summary.avg_revenue_per_request_msats),
icon: CreditCard,
color: 'text-cyan-500',
},