@@ -2,18 +2,31 @@ import { useState, useCallback, useRef, DragEvent } from "react";
22import { Upload } from "@phosphor-icons/react" ;
33import { toast } from "sonner" ;
44import {
5- LineChart , Line , XAxis , YAxis , CartesianGrid , Tooltip , Legend , ResponsiveContainer
5+ LineChart , Line , XAxis , YAxis , CartesianGrid , Tooltip , Legend , ResponsiveContainer ,
6+ BarChart , Bar , Cell
67} from "recharts" ;
78import { Card } from "@/components/ui/card" ;
89import { Button } from "@/components/ui/button" ;
910import { Separator } from "@/components/ui/separator" ;
1011import { ChartContainer , ChartTooltip , ChartTooltipContent } from "@/components/ui/chart" ;
11- import { AggregatedData , CopilotUsageData , aggregateDataByDay , parseCSV } from "@/lib/utils" ;
12+ import { Table , TableBody , TableCell , TableHead , TableHeader , TableRow } from "@/components/ui/table" ;
13+ import {
14+ AggregatedData ,
15+ CopilotUsageData ,
16+ ModelUsageSummary ,
17+ DailyModelData ,
18+ aggregateDataByDay ,
19+ parseCSV ,
20+ getModelUsageSummary ,
21+ getDailyModelData
22+ } from "@/lib/utils" ;
1223
1324function App ( ) {
1425 const [ data , setData ] = useState < CopilotUsageData [ ] | null > ( null ) ;
1526 const [ aggregatedData , setAggregatedData ] = useState < AggregatedData [ ] > ( [ ] ) ;
1627 const [ uniqueModels , setUniqueModels ] = useState < string [ ] > ( [ ] ) ;
28+ const [ modelSummary , setModelSummary ] = useState < ModelUsageSummary [ ] > ( [ ] ) ;
29+ const [ dailyModelData , setDailyModelData ] = useState < DailyModelData [ ] > ( [ ] ) ;
1730 const [ isDragging , setIsDragging ] = useState ( false ) ;
1831 const fileInputRef = useRef < HTMLInputElement > ( null ) ;
1932
@@ -37,12 +50,22 @@ function App() {
3750 const aggregated = aggregateDataByDay ( parsedData ) ;
3851 setAggregatedData ( aggregated ) ;
3952
53+ // Get model usage summary
54+ const summary = getModelUsageSummary ( parsedData ) ;
55+ setModelSummary ( summary ) ;
56+
57+ // Get daily model data for bar chart
58+ const dailyData = getDailyModelData ( parsedData ) ;
59+ setDailyModelData ( dailyData ) ;
60+
4061 toast . success ( `Loaded ${ parsedData . length } records successfully` ) ;
4162 } catch ( error ) {
4263 console . error ( "Error parsing CSV:" , error ) ;
4364 toast . error ( "Failed to parse CSV file. Please check the format." ) ;
4465 setData ( null ) ;
4566 setAggregatedData ( [ ] ) ;
67+ setModelSummary ( [ ] ) ;
68+ setDailyModelData ( [ ] ) ;
4669 }
4770 } ;
4871
@@ -118,6 +141,72 @@ function App() {
118141 a . date . localeCompare ( b . date )
119142 ) ;
120143 } , [ aggregatedData ] ) ;
144+
145+ // Generate bar chart data grouped by date and model
146+ const barChartData = useCallback ( ( ) => {
147+ if ( ! dailyModelData . length ) return [ ] ;
148+
149+ // Group by date first
150+ const groupedByDate : Record < string , any > = { } ;
151+
152+ // Get all unique dates and models
153+ const dates = new Set < string > ( ) ;
154+ const models = new Set < string > ( ) ;
155+
156+ dailyModelData . forEach ( item => {
157+ dates . add ( item . date ) ;
158+ models . add ( item . model ) ;
159+ } ) ;
160+
161+ // Create entries for each date
162+ dates . forEach ( date => {
163+ groupedByDate [ date ] = {
164+ date,
165+ } ;
166+
167+ // Initialize models with zero
168+ models . forEach ( model => {
169+ groupedByDate [ date ] [ model ] = 0 ;
170+ } ) ;
171+ } ) ;
172+
173+ // Fill in the actual data
174+ dailyModelData . forEach ( item => {
175+ groupedByDate [ item . date ] [ item . model ] = item . requests ;
176+ } ) ;
177+
178+ // Convert to array sorted by date
179+ return Object . values ( groupedByDate ) . sort ( ( a : any , b : any ) =>
180+ a . date . localeCompare ( b . date )
181+ ) ;
182+ } , [ dailyModelData ] ) ;
183+
184+ // Get unique model names for bar chart
185+ const getUniqueModelsForBarChart = useCallback ( ( ) => {
186+ return uniqueModels ;
187+ } , [ uniqueModels ] ) ;
188+
189+ // Generate colors for models in bar chart
190+ const getModelColors = useCallback ( ( ) => {
191+ // Use a set of predefined colors that are visually distinct
192+ const colors = [
193+ "#4285F4" , // Blue
194+ "#EA4335" , // Red
195+ "#FBBC05" , // Yellow
196+ "#34A853" , // Green
197+ "#8E44AD" , // Purple
198+ "#F39C12" , // Orange
199+ "#16A085" , // Teal
200+ "#E74C3C" , // Red-Orange
201+ "#3498DB" , // Light Blue
202+ "#1ABC9C" // Turquoise
203+ ] ;
204+
205+ return uniqueModels . reduce ( ( acc , model , index ) => {
206+ acc [ model ] = colors [ index % colors . length ] ;
207+ return acc ;
208+ } , { } as Record < string , string > ) ;
209+ } , [ uniqueModels ] ) ;
121210
122211 return (
123212 < div className = "container max-w-7xl mx-auto py-8 px-4 min-h-screen" >
@@ -167,7 +256,7 @@ function App() {
167256 < div >
168257 < h2 className = "text-2xl font-semibold mb-2" > Usage Statistics</ h2 >
169258 < Separator className = "mb-4" />
170- < div className = "grid grid-cols-1 md:grid-cols-3 gap-4" >
259+ < div className = "grid grid-cols-1 md:grid-cols-3 gap-4 mb-4 " >
171260 < Card >
172261 < div className = "p-5" >
173262 < h3 className = "text-sm font-medium text-muted-foreground mb-1" > Total Requests</ h3 >
@@ -190,9 +279,55 @@ function App() {
190279 < p className = "text-2xl font-bold" >
191280 { uniqueModels . length }
192281 </ p >
193- < div className = "mt-2 text-xs text-muted-foreground" >
194- { uniqueModels . join ( ", " ) }
195- </ div >
282+ </ div >
283+ </ Card >
284+ </ div >
285+
286+ { /* Model Usage Table */ }
287+ < div className = "grid grid-cols-1 lg:grid-cols-2 gap-4 mb-6" >
288+ < Card className = "p-5" >
289+ < h3 className = "text-md font-medium mb-3" > Requests per Model</ h3 >
290+ < div className = "overflow-auto max-h-60" >
291+ < Table >
292+ < TableHeader >
293+ < TableRow >
294+ < TableHead > Model</ TableHead >
295+ < TableHead className = "text-right" > Total Requests</ TableHead >
296+ < TableHead className = "text-right" > Compliant</ TableHead >
297+ < TableHead className = "text-right" > Exceeding</ TableHead >
298+ </ TableRow >
299+ </ TableHeader >
300+ < TableBody >
301+ { modelSummary . map ( ( item ) => (
302+ < TableRow key = { item . model } >
303+ < TableCell className = "font-medium" > { item . model } </ TableCell >
304+ < TableCell className = "text-right" > { item . totalRequests . toLocaleString ( ) } </ TableCell >
305+ < TableCell className = "text-right" > { item . compliantRequests . toLocaleString ( ) } </ TableCell >
306+ < TableCell className = "text-right" > { item . exceedingRequests . toLocaleString ( ) } </ TableCell >
307+ </ TableRow >
308+ ) ) }
309+ </ TableBody >
310+ </ Table >
311+ </ div >
312+ </ Card >
313+
314+ < Card className = "p-5" >
315+ < h3 className = "text-md font-medium mb-3" > Models List</ h3 >
316+ < div className = "overflow-auto max-h-60" >
317+ < Table >
318+ < TableHeader >
319+ < TableRow >
320+ < TableHead > Model Name</ TableHead >
321+ </ TableRow >
322+ </ TableHeader >
323+ < TableBody >
324+ { uniqueModels . map ( ( model ) => (
325+ < TableRow key = { model } >
326+ < TableCell > { model } </ TableCell >
327+ </ TableRow >
328+ ) ) }
329+ </ TableBody >
330+ </ Table >
196331 </ div >
197332 </ Card >
198333 </ div >
@@ -201,7 +336,7 @@ function App() {
201336 < div >
202337 < h2 className = "text-2xl font-semibold mb-2" > Daily Usage Overview</ h2 >
203338 < Separator className = "mb-6" />
204- < div className = "bg-card p-4 rounded-lg border" >
339+ < div className = "bg-card p-4 rounded-lg border mb-8 " >
205340 < ChartContainer
206341 config = { {
207342 compliant : { color : "#10b981" } , // green
@@ -278,11 +413,71 @@ function App() {
278413 </ LineChart >
279414 </ ChartContainer >
280415 </ div >
416+
417+ { /* Bar Chart - Requests per Model per Day */ }
418+ < h2 className = "text-2xl font-semibold mb-2" > Requests per Model per Day</ h2 >
419+ < Separator className = "mb-6" />
420+ < div className = "bg-card p-4 rounded-lg border" >
421+ < ChartContainer
422+ config = { getModelColors ( ) }
423+ className = "h-[500px] w-full"
424+ >
425+ < BarChart data = { barChartData ( ) } >
426+ < CartesianGrid strokeDasharray = "3 3" opacity = { 0.2 } />
427+ < XAxis
428+ dataKey = "date"
429+ tick = { { fill : 'var(--foreground)' } }
430+ tickLine = { { stroke : 'var(--border)' } }
431+ />
432+ < YAxis
433+ tick = { { fill : 'var(--foreground)' } }
434+ tickLine = { { stroke : 'var(--border)' } }
435+ />
436+ < Tooltip
437+ content = { ( { active, payload, label } ) => {
438+ if ( active && payload && payload . length ) {
439+ return (
440+ < div className = "border rounded-lg bg-background shadow-lg p-3" >
441+ < div className = "font-medium mb-2" > { label } </ div >
442+ < div className = "space-y-2" >
443+ { payload . map ( ( entry , index ) => (
444+ < div key = { `item-${ index } ` } className = "flex justify-between items-center gap-4" >
445+ < div className = "flex items-center gap-1.5" >
446+ < div
447+ className = "w-2 h-2 rounded-full"
448+ style = { { backgroundColor : entry . color } }
449+ />
450+ < span > { entry . name } :</ span >
451+ </ div >
452+ < div className = "font-medium" > { entry . value } </ div >
453+ </ div >
454+ ) ) }
455+ </ div >
456+ </ div >
457+ ) ;
458+ }
459+ return null ;
460+ } }
461+ />
462+ < Legend />
463+
464+ { /* Generate a bar for each model */ }
465+ { getUniqueModelsForBarChart ( ) . map ( ( model , index ) => (
466+ < Bar
467+ key = { model }
468+ dataKey = { model }
469+ name = { model }
470+ fill = { getModelColors ( ) [ model ] }
471+ />
472+ ) ) }
473+ </ BarChart >
474+ </ ChartContainer >
475+ </ div >
281476 </ div >
282477 </ div >
283478 ) }
284479 </ div >
285480 ) ;
286481}
287482
288- export default App ;
483+ export default App ;
0 commit comments