11import React , { useCallback , useEffect , useState } from 'react' ;
2+ import { useOutletContext } from 'react-router-dom' ;
23import { aiLabService } from '../../services' ;
34
45const AILabRegistry = ( ) => {
6+ const { theme } = useOutletContext ( ) ;
7+ const isDark = theme === 'dark' ;
58 const [ items , setItems ] = useState ( [ ] ) ;
69 const [ loading , setLoading ] = useState ( true ) ;
710 const [ error , setError ] = useState ( '' ) ;
@@ -89,7 +92,7 @@ const AILabRegistry = () => {
8992 < div className = "space-y-6" >
9093 < div >
9194 < h1 className = "text-3xl font-bold" > Model Registry</ h1 >
92- < p className = " text-gray-600 mt-1" > Store and browse trained model artifacts.</ p >
95+ < p className = { isDark ? ' text-gray-300 mt-1' : 'text-gray- 600 mt-1' } > Store and browse trained model artifacts.</ p >
9396 </ div >
9497
9598 { error && (
@@ -98,63 +101,63 @@ const AILabRegistry = () => {
98101 </ div >
99102 ) }
100103
101- < div className = " bg-white rounded-lg shadow-md p-6" >
102- < h2 className = " text-xl font-bold text-gray-900 mb-4" > Register Model</ h2 >
104+ < div className = { isDark ? ' bg-white/5 border border-white/10 rounded-lg p-6' : 'bg-white rounded-lg shadow-md p-6' } >
105+ < h2 className = { isDark ? ' text-xl font-bold text-white mb-4' : 'text-xl font-bold text- gray-900 mb-4' } > Register Model</ h2 >
103106 < form onSubmit = { handleUpload } className = "space-y-4" >
104107 < div className = "grid grid-cols-1 md:grid-cols-2 gap-4" >
105108 < div >
106- < label className = " block text-sm font-semibold text-gray-700 mb-2" > Name</ label >
109+ < label className = { isDark ? ' block text-sm font-semibold text-gray-200 mb-2' : 'block text-sm font-semibold text-gray- 700 mb-2' } > Name</ label >
107110 < input
108111 value = { name }
109112 onChange = { ( e ) => setName ( e . target . value ) }
110- className = " w-full px-4 py-3 rounded-lg border border-gray-300 text-gray-900 outline-none"
113+ className = { isDark ? ' w-full px-4 py-3 rounded-lg border border-white/10 bg-white/5 text-white outline-none' : 'w-full px-4 py-3 rounded-lg border border- gray-300 text-gray-900 outline-none' }
111114 placeholder = "e.g. churn-model"
112115 disabled = { uploading }
113116 />
114117 </ div >
115118 < div >
116- < label className = " block text-sm font-semibold text-gray-700 mb-2" > Version</ label >
119+ < label className = { isDark ? ' block text-sm font-semibold text-gray-200 mb-2' : 'block text-sm font-semibold text-gray- 700 mb-2' } > Version</ label >
117120 < input
118121 value = { version }
119122 onChange = { ( e ) => setVersion ( e . target . value ) }
120- className = " w-full px-4 py-3 rounded-lg border border-gray-300 text-gray-900 outline-none"
123+ className = { isDark ? ' w-full px-4 py-3 rounded-lg border border-white/10 bg-white/5 text-white outline-none' : 'w-full px-4 py-3 rounded-lg border border- gray-300 text-gray-900 outline-none' }
121124 placeholder = "e.g. v1.0.0"
122125 disabled = { uploading }
123126 />
124127 </ div >
125128 </ div >
126129
127130 < div >
128- < label className = " block text-sm font-semibold text-gray-700 mb-2" > Description</ label >
131+ < label className = { isDark ? ' block text-sm font-semibold text-gray-200 mb-2' : 'block text-sm font-semibold text-gray- 700 mb-2' } > Description</ label >
129132 < input
130133 value = { description }
131134 onChange = { ( e ) => setDescription ( e . target . value ) }
132- className = " w-full px-4 py-3 rounded-lg border border-gray-300 text-gray-900 outline-none"
135+ className = { isDark ? ' w-full px-4 py-3 rounded-lg border border-white/10 bg-white/5 text-white outline-none' : 'w-full px-4 py-3 rounded-lg border border- gray-300 text-gray-900 outline-none' }
133136 placeholder = "Optional"
134137 disabled = { uploading }
135138 />
136139 </ div >
137140
138141 < div >
139- < label className = " block text-sm font-semibold text-gray-700 mb-2" > Metrics (JSON)</ label >
142+ < label className = { isDark ? ' block text-sm font-semibold text-gray-200 mb-2' : 'block text-sm font-semibold text-gray- 700 mb-2' } > Metrics (JSON)</ label >
140143 < textarea
141144 value = { metricsText }
142145 onChange = { ( e ) => setMetricsText ( e . target . value ) }
143146 rows = { 6 }
144- className = " w-full font-mono text-xs px-4 py-3 rounded-lg border border-gray-300 text-gray-900 outline-none"
147+ className = { isDark ? ' w-full font-mono text-xs px-4 py-3 rounded-lg border border-white/10 bg-white/5 text-white outline-none' : 'w-full font-mono text-xs px-4 py-3 rounded-lg border border- gray-300 text-gray-900 outline-none' }
145148 disabled = { uploading }
146149 />
147150 </ div >
148151
149152 < div >
150- < label className = " block text-sm font-semibold text-gray-700 mb-2" > File</ label >
153+ < label className = { isDark ? ' block text-sm font-semibold text-gray-200 mb-2' : 'block text-sm font-semibold text-gray- 700 mb-2' } > File</ label >
151154 < input
152155 type = "file"
153156 onChange = { ( e ) => setFile ( e . target . files ?. [ 0 ] || null ) }
154157 className = "w-full"
155158 disabled = { uploading }
156159 />
157- < div className = " text-xs text-gray-500 mt-1" > Upload a model artifact (e.g. .pkl, .onnx, .pt).</ div >
160+ < div className = { isDark ? ' text-xs text-gray-300 mt-1' : 'text-xs text-gray- 500 mt-1' } > Upload a model artifact (e.g. .pkl, .onnx, .pt).</ div >
158161 </ div >
159162
160163 < button
@@ -167,28 +170,28 @@ const AILabRegistry = () => {
167170 </ form >
168171 </ div >
169172
170- < div className = " bg-white rounded-lg shadow-md p-6" >
173+ < div className = { isDark ? ' bg-white/5 border border-white/10 rounded-lg p-6' : 'bg-white rounded-lg shadow-md p-6' } >
171174 < div className = "flex items-center justify-between gap-4 mb-4" >
172- < h2 className = " text-xl font-bold text-gray-900" > Registered Models</ h2 >
175+ < h2 className = { isDark ? ' text-xl font-bold text-white' : 'text-xl font-bold text- gray-900' } > Registered Models</ h2 >
173176 < button
174177 type = "button"
175178 onClick = { load }
176- className = " px-4 py-2 border border-gray-200 rounded-lg font-semibold hover:bg-gray-50"
179+ className = { isDark ? ' px-4 py-2 border border-white/10 rounded-lg font-semibold hover:bg-white/10 text-white' : 'px-4 py-2 border border- gray-200 rounded-lg font-semibold hover:bg-gray-50' }
177180 disabled = { loading }
178181 >
179182 Refresh
180183 </ button >
181184 </ div >
182185
183186 { loading ? (
184- < div className = " text-gray-600" > Loading…</ div >
187+ < div className = { isDark ? ' text-gray-300' : 'text-gray- 600' } > Loading…</ div >
185188 ) : items . length === 0 ? (
186- < div className = " text-gray-600" > No models registered yet.</ div >
189+ < div className = { isDark ? ' text-gray-300' : 'text-gray- 600' } > No models registered yet.</ div >
187190 ) : (
188191 < div className = "overflow-x-auto" >
189192 < table className = "w-full text-sm" >
190193 < thead >
191- < tr className = " text-left text-gray-600 border-b" >
194+ < tr className = { isDark ? ' text-left text-gray-300 border-b border-white/10' : 'text-left text-gray- 600 border-b' } >
192195 < th className = "py-2" > Name</ th >
193196 < th className = "py-2" > Version</ th >
194197 < th className = "py-2" > Created</ th >
@@ -197,15 +200,15 @@ const AILabRegistry = () => {
197200 </ thead >
198201 < tbody >
199202 { items . map ( ( m ) => (
200- < tr key = { m . id } className = " border-b last:border-b-0" >
201- < td className = " py-2 text-gray-900 font-semibold" > { m . name } </ td >
202- < td className = " py-2 text-gray-600" > { m . version || '—' } </ td >
203- < td className = " py-2 text-gray-600" > { m . created_at ? new Date ( m . created_at ) . toLocaleString ( ) : '-' } </ td >
203+ < tr key = { m . id } className = { isDark ? ' border-b border-white/10 last:border-b-0' : 'border-b last:border-b-0' } >
204+ < td className = { isDark ? ' py-2 text-white font-semibold' : 'py-2 text- gray-900 font-semibold' } > { m . name } </ td >
205+ < td className = { isDark ? ' py-2 text-gray-200' : 'py-2 text-gray- 600' } > { m . version || '—' } </ td >
206+ < td className = { isDark ? ' py-2 text-gray-200' : 'py-2 text-gray- 600' } > { m . created_at ? new Date ( m . created_at ) . toLocaleString ( ) : '-' } </ td >
204207 < td className = "py-2" >
205208 { m . file_url ? (
206209 < a className = "text-primary-700 hover:underline" href = { m . file_url } target = "_blank" rel = "noreferrer" > Download</ a >
207210 ) : (
208- < span className = " text-gray-500" > —</ span >
211+ < span className = { isDark ? ' text-gray-300' : 'text-gray- 500' } > —</ span >
209212 ) }
210213 </ td >
211214 </ tr >
0 commit comments