11"use client" ;
22
3- import * as React from "react" ;
4- import { Cpu , Zap , Clock , Gauge , Database , Brain } from "lucide-react " ;
5- import { Progress } from "@/components/ui/progress " ;
3+ import { Brain , MessageSquare , Tags , Sparkles , CheckCircle2 , Loader2 , Clock , Zap , Database } from "lucide- react" ;
4+ import { cn } from "@/lib/utils " ;
5+ import type { PipelineModels , ModelStage } from "@/types " ;
66
77interface MLInfoPanelProps {
88 isProcessing : boolean ;
9- modelName ?: string ;
9+ pipelineModels : PipelineModels ;
1010 processingSpeed ?: number ;
1111 tokensProcessed ?: number ;
12- avgConfidence ?: number ;
13- currentBatch ?: number ;
14- totalBatches ?: number ;
1512 processingTimeSeconds ?: number ;
1613}
1714
15+ const stageLabels : Record < ModelStage , string > = {
16+ pending : "Waiting" ,
17+ loading : "Loading" ,
18+ active : "Processing" ,
19+ embedding : "Embedding" ,
20+ clustering : "Clustering" ,
21+ connecting : "Connecting" ,
22+ generating : "Generating" ,
23+ complete : "Complete" ,
24+ unavailable : "Unavailable" ,
25+ } ;
26+
27+ function ModelCard ( {
28+ icon,
29+ label,
30+ modelName,
31+ stage,
32+ detail,
33+ color,
34+ } : Readonly < {
35+ icon : React . ReactNode ;
36+ label : string ;
37+ modelName : string ;
38+ stage : ModelStage ;
39+ detail ?: string ;
40+ color : string ;
41+ } > ) : JSX . Element {
42+ const isActive = stage !== "pending" && stage !== "complete" && stage !== "unavailable" ;
43+ const isComplete = stage === "complete" ;
44+ const isUnavailable = stage === "unavailable" ;
45+
46+ const renderIcon = ( ) : React . ReactNode => {
47+ if ( isComplete ) {
48+ return < CheckCircle2 className = "h-4 w-4 text-emerald-600" /> ;
49+ }
50+ if ( isActive ) {
51+ return < Loader2 className = "h-4 w-4 animate-spin" style = { { color } } /> ;
52+ }
53+ return < span className = { isUnavailable ? "text-slate-400" : "text-slate-500" } > { icon } </ span > ;
54+ } ;
55+
56+ return (
57+ < div
58+ className = { cn (
59+ "flex items-center gap-3 p-2.5 rounded-lg border transition-all" ,
60+ isActive && "bg-[#D4714E]/5 border-[#D4714E]/20" ,
61+ isComplete && "bg-emerald-50/50 border-emerald-200/50" ,
62+ isUnavailable && "bg-slate-50 border-slate-200 opacity-60" ,
63+ ! isActive && ! isComplete && ! isUnavailable && "bg-slate-50/50 border-slate-200/50"
64+ ) }
65+ >
66+ { /* Icon */ }
67+ < div
68+ className = { cn (
69+ "h-8 w-8 rounded-lg flex items-center justify-center flex-shrink-0" ,
70+ isActive && `bg-[${ color } ]/10` ,
71+ isComplete && "bg-emerald-100" ,
72+ ! isActive && ! isComplete && "bg-slate-100"
73+ ) }
74+ style = { isActive ? { backgroundColor : `${ color } 15` } : undefined }
75+ >
76+ { renderIcon ( ) }
77+ </ div >
78+
79+ { /* Info */ }
80+ < div className = "flex-1 min-w-0" >
81+ < div className = "flex items-center gap-2" >
82+ < span className = "text-xs font-semibold text-slate-700" > { label } </ span >
83+ { isActive && (
84+ < span className = "text-[10px] font-medium px-1.5 py-0.5 rounded-full bg-[#D4714E]/10 text-[#D4714E]" >
85+ { stageLabels [ stage ] }
86+ </ span >
87+ ) }
88+ </ div >
89+ < p className = "text-[10px] font-mono text-slate-500 truncate" title = { modelName } >
90+ { formatModelName ( modelName ) }
91+ </ p >
92+ </ div >
93+
94+ { /* Detail/Status */ }
95+ < div className = "text-right flex-shrink-0" >
96+ { detail && isActive && (
97+ < span className = "text-[10px] font-medium text-[#D4714E] tabular-nums" > { detail } </ span >
98+ ) }
99+ { isComplete && < span className = "text-[10px] font-medium text-emerald-600" > Done</ span > }
100+ { isUnavailable && < span className = "text-[10px] text-slate-400" > Skipped</ span > }
101+ </ div >
102+ </ div >
103+ ) ;
104+ }
105+
106+ function formatModelName ( name : string ) : string {
107+ const parts = name . split ( "/" ) ;
108+ return parts . at ( - 1 ) ?? name ;
109+ }
110+
18111export function MLInfoPanel ( {
19112 isProcessing,
20- modelName = "nlptown/bert-base-multilingual-uncased-sentiment" ,
113+ pipelineModels ,
21114 processingSpeed = 0 ,
22115 tokensProcessed = 0 ,
23- avgConfidence = 0 ,
24- currentBatch = 0 ,
25- totalBatches = 0 ,
26116 processingTimeSeconds = 0 ,
27- } : MLInfoPanelProps ) : JSX . Element {
28- const formatModelName = ( name : string ) => {
29- // Show only the model name part for readability
30- const parts = name . split ( "/" ) ;
31- return parts [ parts . length - 1 ] ;
32- } ;
117+ } : Readonly < MLInfoPanelProps > ) : JSX . Element {
118+ const hasStarted = processingTimeSeconds > 0 || isProcessing ;
33119
34120 return (
35121 < div className = "rounded-lg border bg-white overflow-hidden" >
122+ { /* Header */ }
36123 < div className = "px-4 py-2.5 border-b bg-[#FAFAFA] flex items-center gap-2" >
37124 < Brain className = "h-4 w-4 text-[#D4714E]" />
38125 < h3 className = "text-sm font-semibold tracking-tight" > ML Pipeline</ h3 >
@@ -48,110 +135,64 @@ export function MLInfoPanel({
48135 </ div >
49136
50137 < div className = "p-3 space-y-3" >
51- { /* Model Info */ }
52- < div className = "flex items-start gap-2" >
53- < div className = "h-7 w-7 rounded bg-slate-100 flex items-center justify-center flex-shrink-0" >
54- < Cpu className = "h-4 w-4 text-slate-600" />
55- </ div >
56- < div className = "min-w-0" >
57- < p className = "text-[10px] text-muted-foreground uppercase tracking-wider" >
58- Model
59- </ p >
60- < p className = "text-xs font-mono font-medium truncate" title = { modelName } >
61- { formatModelName ( modelName ) }
62- </ p >
63- </ div >
138+ { /* Model Cards */ }
139+ < div className = "space-y-2" >
140+ < ModelCard
141+ icon = { < MessageSquare className = "h-4 w-4" /> }
142+ label = "Sentiment Analysis"
143+ modelName = { pipelineModels . sentiment . name }
144+ stage = { pipelineModels . sentiment . stage }
145+ detail = { pipelineModels . sentiment . detail }
146+ color = "#2D7A5E"
147+ />
148+ < ModelCard
149+ icon = { < Tags className = "h-4 w-4" /> }
150+ label = "Topic Detection"
151+ modelName = { pipelineModels . topics . name }
152+ stage = { pipelineModels . topics . stage }
153+ detail = { pipelineModels . topics . detail }
154+ color = "#9B7B5B"
155+ />
156+ < ModelCard
157+ icon = { < Sparkles className = "h-4 w-4" /> }
158+ label = "AI Summaries"
159+ modelName = { pipelineModels . summaries . name }
160+ stage = { pipelineModels . summaries . stage }
161+ detail = { pipelineModels . summaries . detail }
162+ color = "#D4714E"
163+ />
64164 </ div >
65165
66- { /* Processing Stats - Only show when processing or after */ }
67- { ( isProcessing || processingTimeSeconds > 0 ) && (
68- < >
69- < div className = "grid grid-cols-2 gap-2" >
70- { /* Speed */ }
71- < div className = "rounded bg-slate-50 p-2" >
72- < div className = "flex items-center gap-1.5" >
73- < Zap className = "h-3 w-3 text-amber-500" />
74- < span className = "text-[10px] text-muted-foreground" > Speed</ span >
75- </ div >
76- < p className = "text-sm font-bold tabular-nums mt-0.5" >
77- { processingSpeed . toFixed ( 1 ) }
78- < span className = "text-[10px] font-normal text-muted-foreground ml-1" >
79- /sec
80- </ span >
81- </ p >
82- </ div >
83-
84- { /* Tokens */ }
85- < div className = "rounded bg-slate-50 p-2" >
86- < div className = "flex items-center gap-1.5" >
87- < Database className = "h-3 w-3 text-blue-500" />
88- < span className = "text-[10px] text-muted-foreground" > Tokens</ span >
89- </ div >
90- < p className = "text-sm font-bold tabular-nums mt-0.5" >
91- { tokensProcessed . toLocaleString ( ) }
92- </ p >
166+ { /* Stats - Only show when processing or after */ }
167+ { hasStarted && (
168+ < div className = "grid grid-cols-3 gap-2 pt-2 border-t border-slate-100" >
169+ < div className = "text-center" >
170+ < div className = "flex items-center justify-center gap-1 text-slate-400" >
171+ < Zap className = "h-3 w-3" />
93172 </ div >
94-
95- { /* Confidence */ }
96- < div className = "rounded bg-slate-50 p-2" >
97- < div className = "flex items-center gap-1.5" >
98- < Gauge className = "h-3 w-3 text-emerald-500" />
99- < span className = "text-[10px] text-muted-foreground" > Confidence</ span >
100- </ div >
101- < p className = "text-sm font-bold tabular-nums mt-0.5" >
102- { ( avgConfidence * 100 ) . toFixed ( 1 ) } %
103- </ p >
104- </ div >
105-
106- { /* Time */ }
107- < div className = "rounded bg-slate-50 p-2" >
108- < div className = "flex items-center gap-1.5" >
109- < Clock className = "h-3 w-3 text-[#D4714E]" />
110- < span className = "text-[10px] text-muted-foreground" > Time</ span >
111- </ div >
112- < p className = "text-sm font-bold tabular-nums mt-0.5" >
113- { processingTimeSeconds . toFixed ( 1 ) } s
114- </ p >
173+ < p className = "text-sm font-bold tabular-nums text-slate-700" >
174+ { processingSpeed . toFixed ( 1 ) }
175+ < span className = "text-[9px] font-normal text-slate-400" > /s</ span >
176+ </ p >
177+ < p className = "text-[9px] text-slate-400" > Speed</ p >
178+ </ div >
179+ < div className = "text-center" >
180+ < div className = "flex items-center justify-center gap-1 text-slate-400" >
181+ < Database className = "h-3 w-3" />
115182 </ div >
183+ < p className = "text-sm font-bold tabular-nums text-slate-700" >
184+ { tokensProcessed . toLocaleString ( ) }
185+ </ p >
186+ < p className = "text-[9px] text-slate-400" > Tokens</ p >
116187 </ div >
117-
118- { /* Batch Progress */ }
119- { isProcessing && totalBatches > 0 && (
120- < div className = "space-y-1.5" >
121- < div className = "flex items-center justify-between" >
122- < span className = "text-[10px] text-muted-foreground" >
123- Batch Progress
124- </ span >
125- < span className = "text-[10px] font-medium tabular-nums" >
126- { currentBatch } /{ totalBatches }
127- </ span >
128- </ div >
129- < Progress
130- value = { ( currentBatch / totalBatches ) * 100 }
131- className = "h-1.5"
132- />
188+ < div className = "text-center" >
189+ < div className = "flex items-center justify-center gap-1 text-slate-400" >
190+ < Clock className = "h-3 w-3" />
133191 </ div >
134- ) }
135- </ >
136- ) }
137-
138- { /* Embedding visualization when processing */ }
139- { isProcessing && (
140- < div className = "rounded bg-[#D4714E]/5 p-2" >
141- < p className = "text-[10px] text-[#D4714E] font-medium mb-1.5" >
142- Generating Embeddings
143- </ p >
144- < div className = "flex gap-0.5" >
145- { Array . from ( { length : 20 } ) . map ( ( _ , i ) => (
146- < div
147- key = { i }
148- className = "flex-1 h-3 rounded-sm bg-[#D4714E]/20"
149- style = { {
150- animation : `pulse 1.5s ease-in-out ${ i * 0.1 } s infinite` ,
151- opacity : 0.3 + Math . random ( ) * 0.7 ,
152- } }
153- />
154- ) ) }
192+ < p className = "text-sm font-bold tabular-nums text-slate-700" >
193+ { processingTimeSeconds . toFixed ( 1 ) } s
194+ </ p >
195+ < p className = "text-[9px] text-slate-400" > Time</ p >
155196 </ div >
156197 </ div >
157198 ) }
0 commit comments