nice-bill commited on
Commit
6d5c008
·
1 Parent(s): 5e00ba0

Add dark mode toggle

Browse files
Files changed (2) hide show
  1. frontend/src/App.jsx +92 -66
  2. frontend/tailwind.config.js +1 -0
frontend/src/App.jsx CHANGED
@@ -11,6 +11,14 @@ function App() {
11
  const [runs, setRuns] = useState([])
12
  const [selectedRun, setSelectedRun] = useState(null)
13
  const [trends, setTrends] = useState(null)
 
 
 
 
 
 
 
 
14
 
15
  useEffect(() => {
16
  fetchRuns()
@@ -54,28 +62,44 @@ function App() {
54
  }
55
 
56
  return (
57
- <div className="min-h-screen bg-slate-50">
58
  {/* Header */}
59
- <header className="bg-white border-b border-slate-200 px-6 py-4">
60
  <div className="max-w-6xl mx-auto flex justify-between items-center">
61
- <h1 className="text-xl font-semibold text-slate-800">DeFi Agent Arena</h1>
62
- <div className="flex gap-4 text-sm text-slate-600">
63
- <span>Backend: <span className={trends ? 'text-green-600' : 'text-red-600'}>{trends ? 'Connected' : 'Disconnected'}</span></span>
 
 
 
 
 
 
 
 
 
 
 
 
64
  </div>
65
  </div>
66
  </header>
67
 
68
  <main className="max-w-6xl mx-auto px-6 py-8">
69
  {/* Refresh Section */}
70
- <section className="bg-white rounded-lg border border-slate-200 p-6 mb-8">
71
  <div className="flex justify-between items-center">
72
- <h2 className="text-lg font-medium text-slate-800">Simulation Data</h2>
73
  <button
74
  onClick={() => {
75
  fetchRuns()
76
  fetchTrends()
77
  }}
78
- className="bg-slate-800 text-white px-6 py-2 rounded hover:bg-slate-700"
 
 
 
 
79
  >
80
  Refresh
81
  </button>
@@ -84,63 +108,65 @@ function App() {
84
 
85
  {/* Trends Section */}
86
  {trends && (
87
- <section className="bg-white rounded-lg border border-slate-200 p-6 mb-8">
88
- <h2 className="text-lg font-medium text-slate-800 mb-4">Trends</h2>
89
  <div className="grid grid-cols-4 gap-4">
90
- <div className="bg-slate-50 rounded p-4">
91
- <div className="text-sm text-slate-600">Total Runs</div>
92
- <div className="text-2xl font-semibold text-slate-800">{trends.run_count}</div>
93
  </div>
94
- <div className="bg-slate-50 rounded p-4">
95
- <div className="text-sm text-slate-600">Avg Profit</div>
96
- <div className="text-2xl font-semibold text-slate-800">{trends.avg_profit?.toFixed(2) || 0}</div>
97
  </div>
98
- <div className="bg-slate-50 rounded p-4">
99
- <div className="text-sm text-slate-600">Avg Inequality</div>
100
- <div className="text-2xl font-semibold text-slate-800">{(trends.avg_gini * 100).toFixed(1)}%</div>
101
  </div>
102
- <div className="bg-slate-50 rounded p-4">
103
- <div className="text-sm text-slate-600">Profit Trend</div>
104
- <div className="text-2xl font-semibold text-slate-800 capitalize">{trends.profit_trend}</div>
105
  </div>
106
  </div>
107
  </section>
108
  )}
109
 
110
  {/* Runs List */}
111
- <section className="bg-white rounded-lg border border-slate-200 p-6 mb-8">
112
- <h2 className="text-lg font-medium text-slate-800 mb-4">Runs</h2>
113
  {runs.length === 0 ? (
114
- <p className="text-slate-500">No runs yet. Start one above!</p>
115
  ) : (
116
  <div className="overflow-x-auto">
117
  <table className="w-full">
118
  <thead>
119
- <tr className="border-b border-slate-200 text-left">
120
- <th className="py-2 px-3 text-sm font-medium text-slate-600">Run</th>
121
- <th className="py-2 px-3 text-sm font-medium text-slate-600">Status</th>
122
- <th className="py-2 px-3 text-sm font-medium text-slate-600">Started</th>
123
- <th className="py-2 px-3 text-sm font-medium text-slate-600">Action</th>
124
  </tr>
125
  </thead>
126
  <tbody>
127
  {runs.map((run) => (
128
- <tr key={run.id} className="border-b border-slate-100">
129
- <td className="py-2 px-3 text-slate-800">#{run.run_number}</td>
130
  <td className="py-2 px-3">
131
  <span className={`px-2 py-1 rounded text-xs ${
132
- run.status === 'completed' ? 'bg-green-100 text-green-800' : 'bg-yellow-100 text-yellow-800'
 
 
133
  }`}>
134
  {run.status}
135
  </span>
136
  </td>
137
- <td className="py-2 px-3 text-slate-600 text-sm">
138
  {new Date(run.start_time).toLocaleString()}
139
  </td>
140
  <td className="py-2 px-3">
141
  <button
142
  onClick={() => selectRun(run.id)}
143
- className="text-blue-600 hover:text-blue-800 text-sm"
144
  >
145
  View
146
  </button>
@@ -155,10 +181,10 @@ function App() {
155
 
156
  {/* Run Detail */}
157
  {selectedRun && (
158
- <section className="bg-white rounded-lg border border-slate-200 p-6">
159
  <div className="flex justify-between items-center mb-4">
160
- <h2 className="text-lg font-medium text-slate-800">Run Details</h2>
161
- <button onClick={() => setSelectedRun(null)} className="text-slate-500 hover:text-slate-700">
162
  Close
163
  </button>
164
  </div>
@@ -166,44 +192,44 @@ function App() {
166
  {/* Metrics */}
167
  {selectedRun.metrics && (
168
  <div className="grid grid-cols-3 gap-4 mb-6">
169
- <div className="bg-slate-50 rounded p-4">
170
- <div className="text-sm text-slate-600">Gini Coefficient</div>
171
- <div className="text-xl font-semibold text-slate-800">{selectedRun.metrics.gini_coefficient?.toFixed(4)}</div>
172
  </div>
173
- <div className="bg-slate-50 rounded p-4">
174
- <div className="text-sm text-slate-600">Avg Profit</div>
175
- <div className="text-xl font-semibold text-slate-800">{selectedRun.metrics.avg_agent_profit?.toFixed(2)}</div>
176
  </div>
177
- <div className="bg-slate-50 rounded p-4">
178
- <div className="text-sm text-slate-600">Cooperation Rate</div>
179
- <div className="text-xl font-semibold text-slate-800">{selectedRun.metrics.cooperation_rate?.toFixed(2)}</div>
180
  </div>
181
  </div>
182
  )}
183
 
184
  {/* Agents */}
185
- <h3 className="font-medium text-slate-800 mb-3">Agents</h3>
186
  <div className="overflow-x-auto">
187
  <table className="w-full">
188
  <thead>
189
- <tr className="border-b border-slate-200 text-left">
190
- <th className="py-2 px-3 text-sm font-medium text-slate-600">Agent</th>
191
- <th className="py-2 px-3 text-sm font-medium text-slate-600">Token A</th>
192
- <th className="py-2 px-3 text-sm font-medium text-slate-600">Token B</th>
193
- <th className="py-2 px-3 text-sm font-medium text-slate-600">Profit</th>
194
- <th className="py-2 px-3 text-sm font-medium text-slate-600">Strategy</th>
195
  </tr>
196
  </thead>
197
  <tbody>
198
  {(selectedRun.agents || []).map((agent, i) => (
199
- <tr key={i} className="border-b border-slate-100">
200
- <td className="py-2 px-3 text-slate-800">{agent.name}</td>
201
- <td className="py-2 px-3 text-slate-600">{agent.token_a?.toFixed(2)}</td>
202
- <td className="py-2 px-3 text-slate-600">{agent.token_b?.toFixed(2)}</td>
203
- <td className={`py-2 px-3 ${agent.profit >= 0 ? 'text-green-600' : 'text-red-600'}`}>
204
  {agent.profit?.toFixed(2)}
205
  </td>
206
- <td className="py-2 px-3 text-slate-600">{agent.strategy}</td>
207
  </tr>
208
  ))}
209
  </tbody>
@@ -213,18 +239,18 @@ function App() {
213
  {/* Actions */}
214
  {(selectedRun.actions || []).length > 0 && (
215
  <>
216
- <h3 className="font-medium text-slate-800 mt-6 mb-3">Actions Log</h3>
217
  <div className="space-y-2 max-h-64 overflow-y-auto">
218
  {selectedRun.actions.slice(0, 20).map((action, i) => (
219
- <div key={i} className="bg-slate-50 rounded p-3 text-sm">
220
  <div className="flex gap-2 mb-1">
221
- <span className="font-medium text-slate-800">{action.agent_name}</span>
222
- <span className="text-slate-500">Turn {action.turn}</span>
223
  </div>
224
- <div className="text-slate-600">
225
  <span className="font-medium">{action.action_type}</span>
226
  {action.reasoning && (
227
- <p className="text-slate-500 mt-1 text-xs">{action.reasoning.substring(0, 150)}...</p>
228
  )}
229
  </div>
230
  </div>
 
11
  const [runs, setRuns] = useState([])
12
  const [selectedRun, setSelectedRun] = useState(null)
13
  const [trends, setTrends] = useState(null)
14
+ const [darkMode, setDarkMode] = useState(() => {
15
+ const saved = localStorage.getItem('darkMode')
16
+ return saved ? JSON.parse(saved) : false
17
+ })
18
+
19
+ useEffect(() => {
20
+ localStorage.setItem('darkMode', JSON.stringify(darkMode))
21
+ }, [darkMode])
22
 
23
  useEffect(() => {
24
  fetchRuns()
 
62
  }
63
 
64
  return (
65
+ <div className={`min-h-screen ${darkMode ? 'bg-slate-900' : 'bg-slate-50'}`}>
66
  {/* Header */}
67
+ <header className={`${darkMode ? 'bg-slate-800 border-slate-700' : 'bg-white border-slate-200'} border-b px-6 py-4`}>
68
  <div className="max-w-6xl mx-auto flex justify-between items-center">
69
+ <h1 className={`text-xl font-semibold ${darkMode ? 'text-white' : 'text-slate-800'}`}>DeFi Agent Arena</h1>
70
+ <div className="flex gap-4 items-center">
71
+ <span className={`text-sm ${trends ? 'text-green-500' : 'text-red-500'}`}>
72
+ {trends ? 'Connected' : 'Disconnected'}
73
+ </span>
74
+ <button
75
+ onClick={() => setDarkMode(!darkMode)}
76
+ className={`px-3 py-1.5 rounded text-sm ${
77
+ darkMode
78
+ ? 'bg-slate-700 text-white hover:bg-slate-600'
79
+ : 'bg-slate-200 text-slate-800 hover:bg-slate-300'
80
+ }`}
81
+ >
82
+ {darkMode ? 'Light' : 'Dark'}
83
+ </button>
84
  </div>
85
  </div>
86
  </header>
87
 
88
  <main className="max-w-6xl mx-auto px-6 py-8">
89
  {/* Refresh Section */}
90
+ <section className={`${darkMode ? 'bg-slate-800 border-slate-700' : 'bg-white border-slate-200'} rounded-lg border p-6 mb-8`}>
91
  <div className="flex justify-between items-center">
92
+ <h2 className={`text-lg font-medium ${darkMode ? 'text-white' : 'text-slate-800'}`}>Simulation Data</h2>
93
  <button
94
  onClick={() => {
95
  fetchRuns()
96
  fetchTrends()
97
  }}
98
+ className={`px-6 py-2 rounded ${
99
+ darkMode
100
+ ? 'bg-slate-700 text-white hover:bg-slate-600'
101
+ : 'bg-slate-800 text-white hover:bg-slate-700'
102
+ }`}
103
  >
104
  Refresh
105
  </button>
 
108
 
109
  {/* Trends Section */}
110
  {trends && (
111
+ <section className={`${darkMode ? 'bg-slate-800 border-slate-700' : 'bg-white border-slate-200'} rounded-lg border p-6 mb-8`}>
112
+ <h2 className={`text-lg font-medium ${darkMode ? 'text-white' : 'text-slate-800'} mb-4`}>Trends</h2>
113
  <div className="grid grid-cols-4 gap-4">
114
+ <div className={`${darkMode ? 'bg-slate-700' : 'bg-slate-50'} rounded p-4`}>
115
+ <div className={`text-sm ${darkMode ? 'text-slate-400' : 'text-slate-600'}`}>Total Runs</div>
116
+ <div className={`text-2xl font-semibold ${darkMode ? 'text-white' : 'text-slate-800'}`}>{trends.run_count}</div>
117
  </div>
118
+ <div className={`${darkMode ? 'bg-slate-700' : 'bg-slate-50'} rounded p-4`}>
119
+ <div className={`text-sm ${darkMode ? 'text-slate-400' : 'text-slate-600'}`}>Avg Profit</div>
120
+ <div className={`text-2xl font-semibold ${darkMode ? 'text-white' : 'text-slate-800'}`}>{trends.avg_profit?.toFixed(2) || 0}</div>
121
  </div>
122
+ <div className={`${darkMode ? 'bg-slate-700' : 'bg-slate-50'} rounded p-4`}>
123
+ <div className={`text-sm ${darkMode ? 'text-slate-400' : 'text-slate-600'}`}>Avg Inequality</div>
124
+ <div className={`text-2xl font-semibold ${darkMode ? 'text-white' : 'text-slate-800'}`}>{(trends.avg_gini * 100).toFixed(1)}%</div>
125
  </div>
126
+ <div className={`${darkMode ? 'bg-slate-700' : 'bg-slate-50'} rounded p-4`}>
127
+ <div className={`text-sm ${darkMode ? 'text-slate-400' : 'text-slate-600'}`}>Profit Trend</div>
128
+ <div className={`text-2xl font-semibold ${darkMode ? 'text-white' : 'text-slate-800'} capitalize`}>{trends.profit_trend}</div>
129
  </div>
130
  </div>
131
  </section>
132
  )}
133
 
134
  {/* Runs List */}
135
+ <section className={`${darkMode ? 'bg-slate-800 border-slate-700' : 'bg-white border-slate-200'} rounded-lg border p-6 mb-8`}>
136
+ <h2 className={`text-lg font-medium ${darkMode ? 'text-white' : 'text-slate-800'} mb-4`}>Runs</h2>
137
  {runs.length === 0 ? (
138
+ <p className={darkMode ? 'text-slate-400' : 'text-slate-500'}>No runs yet.</p>
139
  ) : (
140
  <div className="overflow-x-auto">
141
  <table className="w-full">
142
  <thead>
143
+ <tr className={`border-b ${darkMode ? 'border-slate-700' : 'border-slate-200'} text-left`}>
144
+ <th className={`py-2 px-3 text-sm font-medium ${darkMode ? 'text-slate-400' : 'text-slate-600'}`}>Run</th>
145
+ <th className={`py-2 px-3 text-sm font-medium ${darkMode ? 'text-slate-400' : 'text-slate-600'}`}>Status</th>
146
+ <th className={`py-2 px-3 text-sm font-medium ${darkMode ? 'text-slate-400' : 'text-slate-600'}`}>Started</th>
147
+ <th className={`py-2 px-3 text-sm font-medium ${darkMode ? 'text-slate-400' : 'text-slate-600'}`}>Action</th>
148
  </tr>
149
  </thead>
150
  <tbody>
151
  {runs.map((run) => (
152
+ <tr key={run.id} className={`border-b ${darkMode ? 'border-slate-700' : 'border-slate-100'}`}>
153
+ <td className={`py-2 px-3 ${darkMode ? 'text-white' : 'text-slate-800'}`}>#{run.run_number}</td>
154
  <td className="py-2 px-3">
155
  <span className={`px-2 py-1 rounded text-xs ${
156
+ run.status === 'completed'
157
+ ? darkMode ? 'bg-green-900 text-green-300' : 'bg-green-100 text-green-800'
158
+ : darkMode ? 'bg-yellow-900 text-yellow-300' : 'bg-yellow-100 text-yellow-800'
159
  }`}>
160
  {run.status}
161
  </span>
162
  </td>
163
+ <td className={`py-2 px-3 text-sm ${darkMode ? 'text-slate-400' : 'text-slate-600'}`}>
164
  {new Date(run.start_time).toLocaleString()}
165
  </td>
166
  <td className="py-2 px-3">
167
  <button
168
  onClick={() => selectRun(run.id)}
169
+ className={`text-sm ${darkMode ? 'text-blue-400 hover:text-blue-300' : 'text-blue-600 hover:text-blue-800'}`}
170
  >
171
  View
172
  </button>
 
181
 
182
  {/* Run Detail */}
183
  {selectedRun && (
184
+ <section className={`${darkMode ? 'bg-slate-800 border-slate-700' : 'bg-white border-slate-200'} rounded-lg border p-6`}>
185
  <div className="flex justify-between items-center mb-4">
186
+ <h2 className={`text-lg font-medium ${darkMode ? 'text-white' : 'text-slate-800'}`}>Run Details</h2>
187
+ <button onClick={() => setSelectedRun(null)} className={`${darkMode ? 'text-slate-400 hover:text-white' : 'text-slate-500 hover:text-slate-700'}`}>
188
  Close
189
  </button>
190
  </div>
 
192
  {/* Metrics */}
193
  {selectedRun.metrics && (
194
  <div className="grid grid-cols-3 gap-4 mb-6">
195
+ <div className={`${darkMode ? 'bg-slate-700' : 'bg-slate-50'} rounded p-4`}>
196
+ <div className={`text-sm ${darkMode ? 'text-slate-400' : 'text-slate-600'}`}>Gini Coefficient</div>
197
+ <div className={`text-xl font-semibold ${darkMode ? 'text-white' : 'text-slate-800'}`}>{selectedRun.metrics.gini_coefficient?.toFixed(4)}</div>
198
  </div>
199
+ <div className={`${darkMode ? 'bg-slate-700' : 'bg-slate-50'} rounded p-4`}>
200
+ <div className={`text-sm ${darkMode ? 'text-slate-400' : 'text-slate-600'}`}>Avg Profit</div>
201
+ <div className={`text-xl font-semibold ${darkMode ? 'text-white' : 'text-slate-800'}`}>{selectedRun.metrics.avg_agent_profit?.toFixed(2)}</div>
202
  </div>
203
+ <div className={`${darkMode ? 'bg-slate-700' : 'bg-slate-50'} rounded p-4`}>
204
+ <div className={`text-sm ${darkMode ? 'text-slate-400' : 'text-slate-600'}`}>Cooperation Rate</div>
205
+ <div className={`text-xl font-semibold ${darkMode ? 'text-white' : 'text-slate-800'}`}>{selectedRun.metrics.cooperation_rate?.toFixed(2)}</div>
206
  </div>
207
  </div>
208
  )}
209
 
210
  {/* Agents */}
211
+ <h3 className={`font-medium ${darkMode ? 'text-white' : 'text-slate-800'} mb-3`}>Agents</h3>
212
  <div className="overflow-x-auto">
213
  <table className="w-full">
214
  <thead>
215
+ <tr className={`border-b ${darkMode ? 'border-slate-700' : 'border-slate-200'} text-left`}>
216
+ <th className={`py-2 px-3 text-sm font-medium ${darkMode ? 'text-slate-400' : 'text-slate-600'}`}>Agent</th>
217
+ <th className={`py-2 px-3 text-sm font-medium ${darkMode ? 'text-slate-400' : 'text-slate-600'}`}>Token A</th>
218
+ <th className={`py-2 px-3 text-sm font-medium ${darkMode ? 'text-slate-400' : 'text-slate-600'}`}>Token B</th>
219
+ <th className={`py-2 px-3 text-sm font-medium ${darkMode ? 'text-slate-400' : 'text-slate-600'}`}>Profit</th>
220
+ <th className={`py-2 px-3 text-sm font-medium ${darkMode ? 'text-slate-400' : 'text-slate-600'}`}>Strategy</th>
221
  </tr>
222
  </thead>
223
  <tbody>
224
  {(selectedRun.agents || []).map((agent, i) => (
225
+ <tr key={i} className={`border-b ${darkMode ? 'border-slate-700' : 'border-slate-100'}`}>
226
+ <td className={`py-2 px-3 ${darkMode ? 'text-white' : 'text-slate-800'}`}>{agent.name}</td>
227
+ <td className={`py-2 px-3 ${darkMode ? 'text-slate-300' : 'text-slate-600'}`}>{agent.token_a?.toFixed(2)}</td>
228
+ <td className={`py-2 px-3 ${darkMode ? 'text-slate-300' : 'text-slate-600'}`}>{agent.token_b?.toFixed(2)}</td>
229
+ <td className={`py-2 px-3 ${agent.profit >= 0 ? 'text-green-500' : 'text-red-500'}`}>
230
  {agent.profit?.toFixed(2)}
231
  </td>
232
+ <td className={`py-2 px-3 ${darkMode ? 'text-slate-300' : 'text-slate-600'}`}>{agent.strategy}</td>
233
  </tr>
234
  ))}
235
  </tbody>
 
239
  {/* Actions */}
240
  {(selectedRun.actions || []).length > 0 && (
241
  <>
242
+ <h3 className={`font-medium ${darkMode ? 'text-white' : 'text-slate-800'} mt-6 mb-3`}>Actions Log</h3>
243
  <div className="space-y-2 max-h-64 overflow-y-auto">
244
  {selectedRun.actions.slice(0, 20).map((action, i) => (
245
+ <div key={i} className={`${darkMode ? 'bg-slate-700' : 'bg-slate-50'} rounded p-3 text-sm`}>
246
  <div className="flex gap-2 mb-1">
247
+ <span className={`font-medium ${darkMode ? 'text-white' : 'text-slate-800'}`}>{action.agent_name}</span>
248
+ <span className={darkMode ? 'text-slate-400' : 'text-slate-500'}>Turn {action.turn}</span>
249
  </div>
250
+ <div className={darkMode ? 'text-slate-300' : 'text-slate-600'}>
251
  <span className="font-medium">{action.action_type}</span>
252
  {action.reasoning && (
253
+ <p className={`${darkMode ? 'text-slate-400' : 'text-slate-500'} mt-1 text-xs`}>{action.reasoning.substring(0, 150)}...</p>
254
  )}
255
  </div>
256
  </div>
frontend/tailwind.config.js CHANGED
@@ -1,5 +1,6 @@
1
  /** @type {import('tailwindcss').Config} */
2
  export default {
 
3
  content: [
4
  "./index.html",
5
  "./src/**/*.{js,ts,jsx,tsx}",
 
1
  /** @type {import('tailwindcss').Config} */
2
  export default {
3
+ darkMode: 'class',
4
  content: [
5
  "./index.html",
6
  "./src/**/*.{js,ts,jsx,tsx}",