Skip to content

Commit 7027d85

Browse files
committed
commit Update account registration and serializers
1 parent b2ac2ee commit 7027d85

File tree

14 files changed

+365
-267
lines changed

14 files changed

+365
-267
lines changed

frontend/src/components/Lab/MetricsBarChart.js

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
import React from 'react';
22

3-
const MetricsBarChart = ({ title, items }) => {
3+
const MetricsBarChart = ({ title, items, theme = 'light' }) => {
44
const max = Math.max(...items.map((i) => i.value), 0);
55

6+
const panelClass = theme === 'dark' ? 'bg-white/5 border border-white/10 rounded-lg p-6' : 'bg-white rounded-lg shadow-md p-6';
7+
const titleClass = theme === 'dark' ? 'text-white' : 'text-gray-900';
8+
const metaTextClass = theme === 'dark' ? 'text-gray-400' : 'text-gray-500';
9+
const labelClass = theme === 'dark' ? 'text-gray-200' : 'text-gray-700';
10+
const valueClass = theme === 'dark' ? 'text-gray-100' : 'text-gray-600';
11+
const trackClass = theme === 'dark' ? 'h-2 bg-white/10 rounded' : 'h-2 bg-gray-100 rounded';
12+
613
return (
7-
<div className="bg-white rounded-lg shadow-md p-6">
14+
<div className={panelClass}>
815
<div className="flex items-center justify-between mb-4">
9-
<h3 className="text-lg font-bold text-gray-900">{title}</h3>
10-
<div className="text-xs text-gray-500">Max: {max.toFixed ? max.toFixed(2) : max}</div>
16+
<h3 className={`text-lg font-bold ${titleClass}`}>{title}</h3>
17+
<div className={`text-xs ${metaTextClass}`}>Max: {max.toFixed ? max.toFixed(2) : max}</div>
1118
</div>
1219

1320
<div className="space-y-3">
@@ -16,10 +23,10 @@ const MetricsBarChart = ({ title, items }) => {
1623
return (
1724
<div key={item.label}>
1825
<div className="flex items-center justify-between text-sm mb-1">
19-
<span className="font-semibold text-gray-700">{item.label}</span>
20-
<span className="text-gray-600">{item.value}</span>
26+
<span className={`font-semibold ${labelClass}`}>{item.label}</span>
27+
<span className={valueClass}>{item.value}</span>
2128
</div>
22-
<div className="h-2 bg-gray-100 rounded">
29+
<div className={trackClass}>
2330
<div className="h-2 bg-primary-600 rounded" style={{ width: `${pct}%` }} />
2431
</div>
2532
</div>

frontend/src/pages/Lab/AILabDatasets.js

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, { useCallback, useEffect, useState } from 'react';
2+
import { useOutletContext } from 'react-router-dom';
23
import { aiLabService } from '../../services';
34

45
const formatApiError = (e, fallback) => {
@@ -15,6 +16,8 @@ const formatApiError = (e, fallback) => {
1516
};
1617

1718
const AILabDatasets = () => {
19+
const { theme } = useOutletContext();
20+
const isDark = theme === 'dark';
1821
const [items, setItems] = useState([]);
1922
const [loading, setLoading] = useState(true);
2023
const [error, setError] = useState('');
@@ -73,7 +76,7 @@ const AILabDatasets = () => {
7376
<div className="space-y-6">
7477
<div>
7578
<h1 className="text-3xl font-bold">Dataset Manager</h1>
76-
<p className="text-gray-600 mt-1">Upload and manage training datasets.</p>
79+
<p className={isDark ? 'text-gray-300 mt-1' : 'text-gray-600 mt-1'}>Upload and manage training datasets.</p>
7780
</div>
7881

7982
{error && (
@@ -82,31 +85,31 @@ const AILabDatasets = () => {
8285
</div>
8386
)}
8487

85-
<div className="bg-white rounded-lg shadow-md p-6">
86-
<h2 className="text-xl font-bold text-gray-900 mb-4">Upload Dataset</h2>
88+
<div className={isDark ? 'bg-white/5 border border-white/10 rounded-lg p-6' : 'bg-white rounded-lg shadow-md p-6'}>
89+
<h2 className={isDark ? 'text-xl font-bold text-white mb-4' : 'text-xl font-bold text-gray-900 mb-4'}>Upload Dataset</h2>
8790
<form onSubmit={handleUpload} className="space-y-4">
8891
<div>
89-
<label className="block text-sm font-semibold text-gray-700 mb-2">Name</label>
92+
<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>
9093
<input
9194
value={name}
9295
onChange={(e) => setName(e.target.value)}
93-
className="w-full px-4 py-3 rounded-lg border border-gray-300 text-gray-900 outline-none"
96+
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'}
9497
placeholder="e.g. customer-churn-v1"
9598
disabled={uploading}
9699
/>
97100
</div>
98101
<div>
99-
<label className="block text-sm font-semibold text-gray-700 mb-2">Description</label>
102+
<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>
100103
<input
101104
value={description}
102105
onChange={(e) => setDescription(e.target.value)}
103-
className="w-full px-4 py-3 rounded-lg border border-gray-300 text-gray-900 outline-none"
106+
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'}
104107
placeholder="Optional"
105108
disabled={uploading}
106109
/>
107110
</div>
108111
<div>
109-
<label className="block text-sm font-semibold text-gray-700 mb-2">File</label>
112+
<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>
110113
<input
111114
type="file"
112115
onChange={(e) => setFile(e.target.files?.[0] || null)}
@@ -124,43 +127,43 @@ const AILabDatasets = () => {
124127
</form>
125128
</div>
126129

127-
<div className="bg-white rounded-lg shadow-md p-6">
130+
<div className={isDark ? 'bg-white/5 border border-white/10 rounded-lg p-6' : 'bg-white rounded-lg shadow-md p-6'}>
128131
<div className="flex items-center justify-between gap-4 mb-4">
129-
<h2 className="text-xl font-bold text-gray-900">Datasets</h2>
132+
<h2 className={isDark ? 'text-xl font-bold text-white' : 'text-xl font-bold text-gray-900'}>Datasets</h2>
130133
<button
131134
type="button"
132135
onClick={load}
133-
className="px-4 py-2 border border-gray-200 rounded-lg font-semibold hover:bg-gray-50"
136+
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'}
134137
disabled={loading}
135138
>
136139
Refresh
137140
</button>
138141
</div>
139142

140143
{loading ? (
141-
<div className="text-gray-600">Loading…</div>
144+
<div className={isDark ? 'text-gray-300' : 'text-gray-600'}>Loading…</div>
142145
) : items.length === 0 ? (
143-
<div className="text-gray-600">No datasets uploaded yet.</div>
146+
<div className={isDark ? 'text-gray-300' : 'text-gray-600'}>No datasets uploaded yet.</div>
144147
) : (
145148
<div className="overflow-x-auto">
146149
<table className="w-full text-sm">
147150
<thead>
148-
<tr className="text-left text-gray-600 border-b">
151+
<tr className={isDark ? 'text-left text-gray-300 border-b border-white/10' : 'text-left text-gray-600 border-b'}>
149152
<th className="py-2">Name</th>
150153
<th className="py-2">Created</th>
151154
<th className="py-2">File</th>
152155
</tr>
153156
</thead>
154157
<tbody>
155158
{items.map((d) => (
156-
<tr key={d.id} className="border-b last:border-b-0">
157-
<td className="py-2 text-gray-900 font-semibold">{d.name}</td>
158-
<td className="py-2 text-gray-600">{d.created_at ? new Date(d.created_at).toLocaleString() : '-'}</td>
159+
<tr key={d.id} className={isDark ? 'border-b border-white/10 last:border-b-0' : 'border-b last:border-b-0'}>
160+
<td className={isDark ? 'py-2 text-white font-semibold' : 'py-2 text-gray-900 font-semibold'}>{d.name}</td>
161+
<td className={isDark ? 'py-2 text-gray-200' : 'py-2 text-gray-600'}>{d.created_at ? new Date(d.created_at).toLocaleString() : '-'}</td>
159162
<td className="py-2">
160163
{d.file_url ? (
161164
<a className="text-primary-700 hover:underline" href={d.file_url} target="_blank" rel="noreferrer">Download</a>
162165
) : (
163-
<span className="text-gray-500"></span>
166+
<span className={isDark ? 'text-gray-300' : 'text-gray-500'}></span>
164167
)}
165168
</td>
166169
</tr>

frontend/src/pages/Lab/AILabRegistry.js

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import React, { useCallback, useEffect, useState } from 'react';
2+
import { useOutletContext } from 'react-router-dom';
23
import { aiLabService } from '../../services';
34

45
const 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

Comments
 (0)