1- import { X , TrendingUp , DollarSign , Activity , BarChart3 } from 'lucide-react'
1+ import {
2+ X ,
3+ TrendingUp ,
4+ DollarSign ,
5+ Activity ,
6+ BarChart3 ,
7+ Calendar ,
8+ } from 'lucide-react'
29import { Dialog , DialogContent } from '@/components/ui/dialog'
310import { Card } from '@/components/ui/card'
411import { Button } from '@/components/ui/button'
@@ -14,9 +21,20 @@ interface CostSummaryModalProps {
1421 byModel : Record < string , { tokens : number ; cost : number ; count : number } >
1522 byOperation : Record < string , { tokens : number ; cost : number ; count : number } >
1623 }
24+ monthlyStats ?: Record <
25+ string ,
26+ { tokens : number ; cost : number ; operations : number }
27+ >
28+ currentMonthStats ?: { tokens : number ; cost : number ; operations : number }
1729}
1830
19- export function CostSummaryModal ( { isOpen, onClose, stats } : CostSummaryModalProps ) {
31+ export function CostSummaryModal ( {
32+ isOpen,
33+ onClose,
34+ stats,
35+ monthlyStats,
36+ currentMonthStats,
37+ } : CostSummaryModalProps ) {
2038 const avgCost = stats . operations > 0 ? stats . totalCost / stats . operations : 0
2139
2240 const formatTokens = ( tokens : number ) => {
@@ -30,6 +48,17 @@ export function CostSummaryModal({ isOpen, onClose, stats }: CostSummaryModalPro
3048 return `$${ cost . toFixed ( 3 ) } m`
3149 }
3250
51+ const formatMonthLabel = ( monthKey : string ) => {
52+ const [ year , month ] = monthKey . split ( '-' )
53+ const date = new Date ( parseInt ( year ) , parseInt ( month ) - 1 )
54+ return date . toLocaleDateString ( 'en-US' , { month : 'long' , year : 'numeric' } )
55+ }
56+
57+ // Sort monthly stats by date (newest first)
58+ const sortedMonthlyStats = monthlyStats
59+ ? Object . entries ( monthlyStats ) . sort ( ( [ a ] , [ b ] ) => b . localeCompare ( a ) )
60+ : [ ]
61+
3362 return (
3463 < Dialog open = { isOpen } onOpenChange = { onClose } >
3564 < DialogContent className = "max-w-2xl p-0 gap-0 bg-white/95 backdrop-blur-xl border-white/20" >
@@ -44,8 +73,12 @@ export function CostSummaryModal({ isOpen, onClose, stats }: CostSummaryModalPro
4473 </ div >
4574 </ div >
4675 < div >
47- < h2 className = "text-2xl font-bold text-slate-800" > Token Usage & Cost Summary </ h2 >
48- < p className = "text-sm text-slate-600" > Real-time API usage tracking</ p >
76+ < h2 className = "text-2xl font-bold text-slate-800" >
77+ Token Usage & Cost Summary
78+ </ h2 >
79+ < p className = "text-sm text-slate-600" >
80+ Real-time API usage tracking
81+ </ p >
4982 </ div >
5083 </ div >
5184 < Button
@@ -73,9 +106,13 @@ export function CostSummaryModal({ isOpen, onClose, stats }: CostSummaryModalPro
73106 < div className = "w-10 h-10 rounded-xl bg-blue-500 flex items-center justify-center shadow-md" >
74107 < TrendingUp className = "w-5 h-5 text-white" />
75108 </ div >
76- < span className = "text-sm font-medium text-slate-600" > Total Tokens</ span >
109+ < span className = "text-sm font-medium text-slate-600" >
110+ Total Tokens
111+ </ span >
77112 </ div >
78- < p className = "text-3xl font-bold text-slate-800" > { formatTokens ( stats . totalTokens ) } </ p >
113+ < p className = "text-3xl font-bold text-slate-800" >
114+ { formatTokens ( stats . totalTokens ) }
115+ </ p >
79116 </ Card >
80117 </ motion . div >
81118
@@ -89,9 +126,13 @@ export function CostSummaryModal({ isOpen, onClose, stats }: CostSummaryModalPro
89126 < div className = "w-10 h-10 rounded-xl bg-purple-500 flex items-center justify-center shadow-md" >
90127 < DollarSign className = "w-5 h-5 text-white" />
91128 </ div >
92- < span className = "text-sm font-medium text-slate-600" > Total Cost</ span >
129+ < span className = "text-sm font-medium text-slate-600" >
130+ Total Cost
131+ </ span >
93132 </ div >
94- < p className = "text-3xl font-bold text-slate-800" > { formatCost ( stats . totalCost ) } </ p >
133+ < p className = "text-3xl font-bold text-slate-800" >
134+ { formatCost ( stats . totalCost ) }
135+ </ p >
95136 </ Card >
96137 </ motion . div >
97138
@@ -105,9 +146,13 @@ export function CostSummaryModal({ isOpen, onClose, stats }: CostSummaryModalPro
105146 < div className = "w-10 h-10 rounded-xl bg-emerald-500 flex items-center justify-center shadow-md" >
106147 < Activity className = "w-5 h-5 text-white" />
107148 </ div >
108- < span className = "text-sm font-medium text-slate-600" > Operations</ span >
149+ < span className = "text-sm font-medium text-slate-600" >
150+ Operations
151+ </ span >
109152 </ div >
110- < p className = "text-3xl font-bold text-slate-800" > { stats . operations } </ p >
153+ < p className = "text-3xl font-bold text-slate-800" >
154+ { stats . operations }
155+ </ p >
111156 </ Card >
112157 </ motion . div >
113158
@@ -121,9 +166,13 @@ export function CostSummaryModal({ isOpen, onClose, stats }: CostSummaryModalPro
121166 < div className = "w-10 h-10 rounded-xl bg-orange-500 flex items-center justify-center shadow-md" >
122167 < DollarSign className = "w-5 h-5 text-white" />
123168 </ div >
124- < span className = "text-sm font-medium text-slate-600" > Avg Cost</ span >
169+ < span className = "text-sm font-medium text-slate-600" >
170+ Avg Cost
171+ </ span >
125172 </ div >
126- < p className = "text-3xl font-bold text-slate-800" > { formatCost ( avgCost ) } </ p >
173+ < p className = "text-3xl font-bold text-slate-800" >
174+ { formatCost ( avgCost ) }
175+ </ p >
127176 </ Card >
128177 </ motion . div >
129178 </ div >
@@ -135,7 +184,9 @@ export function CostSummaryModal({ isOpen, onClose, stats }: CostSummaryModalPro
135184 transition = { { delay : 0.3 } }
136185 >
137186 < Card className = "p-5" >
138- < h3 className = "text-lg font-semibold text-slate-800 mb-4" > By Model</ h3 >
187+ < h3 className = "text-lg font-semibold text-slate-800 mb-4" >
188+ By Model
189+ </ h3 >
139190 < div className = "space-y-3" >
140191 { Object . entries ( stats . byModel ) . map ( ( [ model , data ] ) => (
141192 < div
@@ -149,8 +200,12 @@ export function CostSummaryModal({ isOpen, onClose, stats }: CostSummaryModalPro
149200 </ p >
150201 </ div >
151202 < div className = "text-right" >
152- < p className = "font-semibold text-slate-800" > { formatTokens ( data . tokens ) } </ p >
153- < p className = "text-sm text-slate-600" > { formatCost ( data . cost ) } </ p >
203+ < p className = "font-semibold text-slate-800" >
204+ { formatTokens ( data . tokens ) }
205+ </ p >
206+ < p className = "text-sm text-slate-600" >
207+ { formatCost ( data . cost ) }
208+ </ p >
154209 </ div >
155210 </ div >
156211 ) ) }
@@ -165,7 +220,9 @@ export function CostSummaryModal({ isOpen, onClose, stats }: CostSummaryModalPro
165220 transition = { { delay : 0.35 } }
166221 >
167222 < Card className = "p-5" >
168- < h3 className = "text-lg font-semibold text-slate-800 mb-4" > By Operation</ h3 >
223+ < h3 className = "text-lg font-semibold text-slate-800 mb-4" >
224+ By Operation
225+ </ h3 >
169226 < div className = "space-y-3" >
170227 { Object . entries ( stats . byOperation ) . map ( ( [ operation , data ] ) => (
171228 < div
@@ -175,18 +232,108 @@ export function CostSummaryModal({ isOpen, onClose, stats }: CostSummaryModalPro
175232 < div >
176233 < p className = "font-medium text-slate-800" > { operation } </ p >
177234 < p className = "text-sm text-slate-600" >
178- { data . count } { data . count === 1 ? 'operation' : 'operations' }
235+ { data . count } { ' ' }
236+ { data . count === 1 ? 'operation' : 'operations' }
179237 </ p >
180238 </ div >
181239 < div className = "text-right" >
182- < p className = "font-semibold text-slate-800" > { formatTokens ( data . tokens ) } </ p >
183- < p className = "text-sm text-slate-600" > { formatCost ( data . cost ) } </ p >
240+ < p className = "font-semibold text-slate-800" >
241+ { formatTokens ( data . tokens ) }
242+ </ p >
243+ < p className = "text-sm text-slate-600" >
244+ { formatCost ( data . cost ) }
245+ </ p >
184246 </ div >
185247 </ div >
186248 ) ) }
187249 </ div >
188250 </ Card >
189251 </ motion . div >
252+
253+ { /* Current Month Billing */ }
254+ { currentMonthStats && currentMonthStats . operations > 0 && (
255+ < motion . div
256+ initial = { { opacity : 0 , y : 20 } }
257+ animate = { { opacity : 1 , y : 0 } }
258+ transition = { { delay : 0.4 } }
259+ >
260+ < Card className = "p-5 bg-gradient-to-br from-amber-50 to-amber-100/50 border-amber-200/50" >
261+ < div className = "flex items-center gap-3 mb-4" >
262+ < div className = "w-10 h-10 rounded-xl bg-amber-500 flex items-center justify-center shadow-md" >
263+ < Calendar className = "w-5 h-5 text-white" />
264+ </ div >
265+ < h3 className = "text-lg font-semibold text-slate-800" >
266+ Current Month (
267+ { formatMonthLabel (
268+ `${ new Date ( ) . getFullYear ( ) } -${ String ( new Date ( ) . getMonth ( ) + 1 ) . padStart ( 2 , '0' ) } `
269+ ) }
270+ )
271+ </ h3 >
272+ </ div >
273+ < div className = "grid grid-cols-3 gap-4" >
274+ < div className = "text-center p-3 rounded-xl bg-white/60 border border-amber-200" >
275+ < p className = "text-sm text-slate-600 mb-1" > Tokens</ p >
276+ < p className = "text-xl font-bold text-slate-800" >
277+ { formatTokens ( currentMonthStats . tokens ) }
278+ </ p >
279+ </ div >
280+ < div className = "text-center p-3 rounded-xl bg-white/60 border border-amber-200" >
281+ < p className = "text-sm text-slate-600 mb-1" > Cost</ p >
282+ < p className = "text-xl font-bold text-slate-800" >
283+ { formatCost ( currentMonthStats . cost ) }
284+ </ p >
285+ </ div >
286+ < div className = "text-center p-3 rounded-xl bg-white/60 border border-amber-200" >
287+ < p className = "text-sm text-slate-600 mb-1" > Operations</ p >
288+ < p className = "text-xl font-bold text-slate-800" >
289+ { currentMonthStats . operations }
290+ </ p >
291+ </ div >
292+ </ div >
293+ </ Card >
294+ </ motion . div >
295+ ) }
296+
297+ { /* Monthly Billing Breakdown */ }
298+ { sortedMonthlyStats . length > 0 && (
299+ < motion . div
300+ initial = { { opacity : 0 , y : 20 } }
301+ animate = { { opacity : 1 , y : 0 } }
302+ transition = { { delay : 0.45 } }
303+ >
304+ < Card className = "p-5" >
305+ < h3 className = "text-lg font-semibold text-slate-800 mb-4" >
306+ Monthly Billing History
307+ </ h3 >
308+ < div className = "space-y-3 max-h-[300px] overflow-y-auto" >
309+ { sortedMonthlyStats . map ( ( [ monthKey , data ] ) => (
310+ < div
311+ key = { monthKey }
312+ className = "flex items-center justify-between p-4 rounded-xl bg-slate-50 border border-slate-200 hover:bg-slate-100 transition-colors"
313+ >
314+ < div >
315+ < p className = "font-medium text-slate-800" >
316+ { formatMonthLabel ( monthKey ) }
317+ </ p >
318+ < p className = "text-sm text-slate-600" >
319+ { data . operations } { ' ' }
320+ { data . operations === 1 ? 'operation' : 'operations' }
321+ </ p >
322+ </ div >
323+ < div className = "text-right" >
324+ < p className = "font-semibold text-slate-800" >
325+ { formatCost ( data . cost ) }
326+ </ p >
327+ < p className = "text-sm text-slate-600" >
328+ { formatTokens ( data . tokens ) } tokens
329+ </ p >
330+ </ div >
331+ </ div >
332+ ) ) }
333+ </ div >
334+ </ Card >
335+ </ motion . div >
336+ ) }
190337 </ div >
191338
192339 { /* Footer */ }
0 commit comments