Deadman922 commited on
Commit
d5b7516
·
verified ·
1 Parent(s): 059e0e6

Upload static/index.html

Browse files
Files changed (1) hide show
  1. static/index.html +718 -0
static/index.html ADDED
@@ -0,0 +1,718 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>SQL Quality Environment</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
10
+ <style>
11
+ * {
12
+ margin: 0;
13
+ padding: 0;
14
+ box-sizing: border-box;
15
+ }
16
+
17
+ :root {
18
+ --bg-primary: #0a0a0a;
19
+ --bg-secondary: #141414;
20
+ --bg-tertiary: #1a1a1a;
21
+ --bg-card: #1f1f1f;
22
+ --border: #2a2a2a;
23
+ --text-primary: #ffffff;
24
+ --text-secondary: #a3a3a3;
25
+ --text-tertiary: #737373;
26
+ --accent: #ffffff;
27
+ --success: #22c55e;
28
+ --error: #ef4444;
29
+ --warning: #f59e0b;
30
+ }
31
+
32
+ body {
33
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
34
+ background: var(--bg-primary);
35
+ color: var(--text-primary);
36
+ min-height: 100vh;
37
+ line-height: 1.6;
38
+ }
39
+
40
+ .container {
41
+ max-width: 1400px;
42
+ margin: 0 auto;
43
+ padding: 24px;
44
+ }
45
+
46
+ header {
47
+ margin-bottom: 32px;
48
+ padding-bottom: 24px;
49
+ border-bottom: 1px solid var(--border);
50
+ }
51
+
52
+ h1 {
53
+ font-size: 28px;
54
+ font-weight: 600;
55
+ letter-spacing: -0.02em;
56
+ margin-bottom: 8px;
57
+ }
58
+
59
+ .subtitle {
60
+ color: var(--text-secondary);
61
+ font-size: 14px;
62
+ font-weight: 400;
63
+ }
64
+
65
+ .bento-grid {
66
+ display: grid;
67
+ grid-template-columns: repeat(4, 1fr);
68
+ gap: 16px;
69
+ margin-bottom: 24px;
70
+ }
71
+
72
+ .bento-card {
73
+ background: var(--bg-card);
74
+ border: 1px solid var(--border);
75
+ border-radius: 12px;
76
+ padding: 20px;
77
+ transition: all 0.2s ease;
78
+ }
79
+
80
+ .bento-card:hover {
81
+ border-color: var(--text-tertiary);
82
+ }
83
+
84
+ .bento-card.large {
85
+ grid-column: span 2;
86
+ grid-row: span 2;
87
+ }
88
+
89
+ .bento-card.wide {
90
+ grid-column: span 2;
91
+ }
92
+
93
+ .card-header {
94
+ display: flex;
95
+ align-items: center;
96
+ gap: 12px;
97
+ margin-bottom: 16px;
98
+ }
99
+
100
+ .card-icon {
101
+ width: 40px;
102
+ height: 40px;
103
+ background: var(--bg-tertiary);
104
+ border-radius: 8px;
105
+ display: flex;
106
+ align-items: center;
107
+ justify-content: center;
108
+ font-size: 20px;
109
+ }
110
+
111
+ .card-title {
112
+ font-size: 16px;
113
+ font-weight: 500;
114
+ color: var(--text-primary);
115
+ }
116
+
117
+ .card-subtitle {
118
+ font-size: 13px;
119
+ color: var(--text-tertiary);
120
+ }
121
+
122
+ select, input, textarea, button {
123
+ font-family: 'Inter', sans-serif;
124
+ font-size: 14px;
125
+ }
126
+
127
+ select, input, textarea {
128
+ width: 100%;
129
+ background: var(--bg-secondary);
130
+ border: 1px solid var(--border);
131
+ border-radius: 8px;
132
+ padding: 12px;
133
+ color: var(--text-primary);
134
+ outline: none;
135
+ transition: border-color 0.2s;
136
+ }
137
+
138
+ select:focus, input:focus, textarea:focus {
139
+ border-color: var(--accent);
140
+ }
141
+
142
+ textarea {
143
+ min-height: 120px;
144
+ resize: vertical;
145
+ font-family: 'JetBrains Mono', 'Fira Code', monospace;
146
+ font-size: 13px;
147
+ line-height: 1.5;
148
+ }
149
+
150
+ button {
151
+ background: var(--text-primary);
152
+ color: var(--bg-primary);
153
+ border: none;
154
+ border-radius: 8px;
155
+ padding: 12px 24px;
156
+ font-weight: 500;
157
+ cursor: pointer;
158
+ transition: all 0.2s;
159
+ display: inline-flex;
160
+ align-items: center;
161
+ gap: 8px;
162
+ }
163
+
164
+ button:hover {
165
+ background: var(--text-secondary);
166
+ transform: translateY(-1px);
167
+ }
168
+
169
+ button.secondary {
170
+ background: var(--bg-tertiary);
171
+ color: var(--text-primary);
172
+ border: 1px solid var(--border);
173
+ }
174
+
175
+ button.secondary:hover {
176
+ background: var(--border);
177
+ }
178
+
179
+ button:disabled {
180
+ opacity: 0.5;
181
+ cursor: not-allowed;
182
+ transform: none;
183
+ }
184
+
185
+ .action-buttons {
186
+ display: flex;
187
+ gap: 12px;
188
+ flex-wrap: wrap;
189
+ margin-top: 16px;
190
+ }
191
+
192
+ .output-area {
193
+ background: var(--bg-secondary);
194
+ border: 1px solid var(--border);
195
+ border-radius: 8px;
196
+ padding: 16px;
197
+ margin-top: 16px;
198
+ min-height: 200px;
199
+ max-height: 400px;
200
+ overflow-y: auto;
201
+ font-family: 'JetBrains Mono', 'Fira Code', monospace;
202
+ font-size: 12px;
203
+ line-height: 1.6;
204
+ white-space: pre-wrap;
205
+ word-break: break-word;
206
+ }
207
+
208
+ .output-area.error {
209
+ border-color: var(--error);
210
+ color: var(--error);
211
+ }
212
+
213
+ .output-area.success {
214
+ border-color: var(--success);
215
+ }
216
+
217
+ .metric-row {
218
+ display: flex;
219
+ justify-content: space-between;
220
+ padding: 12px 0;
221
+ border-bottom: 1px solid var(--border);
222
+ }
223
+
224
+ .metric-row:last-child {
225
+ border-bottom: none;
226
+ }
227
+
228
+ .metric-label {
229
+ color: var(--text-secondary);
230
+ font-size: 13px;
231
+ }
232
+
233
+ .metric-value {
234
+ font-weight: 500;
235
+ font-size: 14px;
236
+ }
237
+
238
+ .status-badge {
239
+ display: inline-flex;
240
+ align-items: center;
241
+ gap: 6px;
242
+ padding: 6px 12px;
243
+ border-radius: 6px;
244
+ font-size: 12px;
245
+ font-weight: 500;
246
+ }
247
+
248
+ .status-badge.online {
249
+ background: rgba(34, 197, 94, 0.1);
250
+ color: var(--success);
251
+ }
252
+
253
+ .status-badge.offline {
254
+ background: rgba(239, 68, 68, 0.1);
255
+ color: var(--error);
256
+ }
257
+
258
+ .status-badge.pending {
259
+ background: rgba(245, 158, 11, 0.1);
260
+ color: var(--warning);
261
+ }
262
+
263
+ .task-list {
264
+ list-style: none;
265
+ }
266
+
267
+ .task-item {
268
+ padding: 12px;
269
+ background: var(--bg-secondary);
270
+ border-radius: 8px;
271
+ margin-bottom: 8px;
272
+ cursor: pointer;
273
+ transition: all 0.2s;
274
+ border: 1px solid transparent;
275
+ }
276
+
277
+ .task-item:hover {
278
+ background: var(--bg-tertiary);
279
+ border-color: var(--border);
280
+ }
281
+
282
+ .task-item.active {
283
+ border-color: var(--accent);
284
+ background: var(--bg-tertiary);
285
+ }
286
+
287
+ .task-name {
288
+ font-weight: 500;
289
+ font-size: 14px;
290
+ margin-bottom: 4px;
291
+ }
292
+
293
+ .task-desc {
294
+ font-size: 12px;
295
+ color: var(--text-tertiary);
296
+ }
297
+
298
+ .task-difficulty {
299
+ display: inline-block;
300
+ padding: 2px 8px;
301
+ border-radius: 4px;
302
+ font-size: 11px;
303
+ margin-top: 6px;
304
+ text-transform: uppercase;
305
+ font-weight: 600;
306
+ }
307
+
308
+ .task-difficulty.easy {
309
+ background: rgba(34, 197, 94, 0.1);
310
+ color: var(--success);
311
+ }
312
+
313
+ .task-difficulty.medium {
314
+ background: rgba(245, 158, 11, 0.1);
315
+ color: var(--warning);
316
+ }
317
+
318
+ .task-difficulty.hard {
319
+ background: rgba(239, 68, 68, 0.1);
320
+ color: var(--error);
321
+ }
322
+
323
+ .loading {
324
+ display: inline-block;
325
+ width: 20px;
326
+ height: 20px;
327
+ border: 2px solid var(--border);
328
+ border-top-color: var(--accent);
329
+ border-radius: 50%;
330
+ animation: spin 1s linear infinite;
331
+ }
332
+
333
+ @keyframes spin {
334
+ to { transform: rotate(360deg); }
335
+ }
336
+
337
+ .hidden {
338
+ display: none !important;
339
+ }
340
+
341
+ @media (max-width: 1200px) {
342
+ .bento-grid {
343
+ grid-template-columns: repeat(2, 1fr);
344
+ }
345
+ .bento-card.large {
346
+ grid-column: span 2;
347
+ }
348
+ }
349
+
350
+ @media (max-width: 768px) {
351
+ .bento-grid {
352
+ grid-template-columns: 1fr;
353
+ }
354
+ .bento-card.large,
355
+ .bento-card.wide {
356
+ grid-column: span 1;
357
+ }
358
+ }
359
+ </style>
360
+ </head>
361
+ <body>
362
+ <div class="container">
363
+ <header>
364
+ <h1>📊 SQL Quality Environment</h1>
365
+ <p class="subtitle">Interactive testing interface for SQL data quality auditing tasks</p>
366
+ </header>
367
+
368
+ <div class="bento-grid">
369
+ <!-- Status Card -->
370
+ <div class="bento-card">
371
+ <div class="card-header">
372
+ <div class="card-icon">🔌</div>
373
+ <div>
374
+ <div class="card-title">System Status</div>
375
+ <div class="card-subtitle">API Connection</div>
376
+ </div>
377
+ </div>
378
+ <div style="text-align: center; padding: 20px 0;">
379
+ <span id="status-badge" class="status-badge pending">
380
+ <span class="loading"></span> Checking...
381
+ </span>
382
+ </div>
383
+ <button id="btn-check" class="secondary" style="width: 100%;">Check Connection</button>
384
+ </div>
385
+
386
+ <!-- Task Selection Card -->
387
+ <div class="bento-card wide">
388
+ <div class="card-header">
389
+ <div class="card-icon">🎯</div>
390
+ <div>
391
+ <div class="card-title">Select Task</div>
392
+ <div class="card-subtitle">Choose a data quality scenario</div>
393
+ </div>
394
+ </div>
395
+ <ul class="task-list">
396
+ <li class="task-item active" data-task="null_audit">
397
+ <div class="task-name">Null Audit</div>
398
+ <div class="task-desc">Identify columns with excessive null values</div>
399
+ <span class="task-difficulty easy">Easy</span>
400
+ </li>
401
+ <li class="task-item" data-task="type_repair">
402
+ <div class="task-name">Type Repair</div>
403
+ <div class="task-desc">Fix numeric type mismatches in transactions</div>
404
+ <span class="task-difficulty medium">Medium</span>
405
+ </li>
406
+ <li class="task-item" data-task="dedup_bug">
407
+ <div class="task-name">Dedup Bug</div>
408
+ <div class="task-desc">Diagnose duplication in order join queries</div>
409
+ <span class="task-difficulty hard">Hard</span>
410
+ </li>
411
+ <li class="task-item" data-task="fk_integrity">
412
+ <div class="task-name">FK Integrity</div>
413
+ <div class="task-desc">Remove orphaned foreign key records</div>
414
+ <span class="task-difficulty hard">Hard</span>
415
+ </li>
416
+ <li class="task-item" data-task="schema_drift">
417
+ <div class="task-name">Schema Drift</div>
418
+ <div class="task-desc">Canonicalize schema drift between versions</div>
419
+ <span class="task-difficulty medium">Medium</span>
420
+ </li>
421
+ </ul>
422
+ </div>
423
+
424
+ <!-- Task Control Card -->
425
+ <div class="bento-card">
426
+ <div class="card-header">
427
+ <div class="card-icon">🎮</div>
428
+ <div>
429
+ <div class="card-title">Task Control</div>
430
+ <div class="card-subtitle">Initialize or reset</div>
431
+ </div>
432
+ </div>
433
+ <div style="margin-bottom: 16px;">
434
+ <label style="display: block; margin-bottom: 8px; font-size: 13px; color: var(--text-secondary);">Random Seed</label>
435
+ <input type="number" id="seed-input" value="42" min="1" max="999999">
436
+ </div>
437
+ <button id="btn-reset" style="width: 100%; margin-bottom: 8px;">🔄 Reset Task</button>
438
+ <button id="btn-state" class="secondary" style="width: 100%;">📋 Get State</button>
439
+ </div>
440
+
441
+ <!-- Metrics Card -->
442
+ <div class="bento-card">
443
+ <div class="card-header">
444
+ <div class="card-icon">📈</div>
445
+ <div>
446
+ <div class="card-title">Episode Metrics</div>
447
+ <div class="card-subtitle">Current performance</div>
448
+ </div>
449
+ </div>
450
+ <div class="metric-row">
451
+ <span class="metric-label">Task ID</span>
452
+ <span id="metric-task" class="metric-value">-</span>
453
+ </div>
454
+ <div class="metric-row">
455
+ <span class="metric-label">Episode ID</span>
456
+ <span id="metric-episode" class="metric-value">-</span>
457
+ </div>
458
+ <div class="metric-row">
459
+ <span class="metric-label">Step Count</span>
460
+ <span id="metric-steps" class="metric-value">0</span>
461
+ </div>
462
+ <div class="metric-row">
463
+ <span class="metric-label">Current Score</span>
464
+ <span id="metric-score" class="metric-value">0.0000</span>
465
+ </div>
466
+ <div class="metric-row">
467
+ <span class="metric-label">Status</span>
468
+ <span id="metric-status" class="metric-value">-</span>
469
+ </div>
470
+ </div>
471
+
472
+ <!-- Query Action Card -->
473
+ <div class="bento-card large">
474
+ <div class="card-header">
475
+ <div class="card-icon">🔍</div>
476
+ <div>
477
+ <div class="card-title">Execute Query</div>
478
+ <div class="card-subtitle">Run SQL to investigate data issues</div>
479
+ </div>
480
+ </div>
481
+ <textarea id="query-input" placeholder="SELECT * FROM users LIMIT 10;"></textarea>
482
+ <div class="action-buttons">
483
+ <button id="btn-query">▶️ Run Query</button>
484
+ <button id="btn-profile" class="secondary">📊 Profile Table</button>
485
+ </div>
486
+ <div id="query-output" class="output-area hidden"></div>
487
+ </div>
488
+
489
+ <!-- Fix Action Card -->
490
+ <div class="bento-card large">
491
+ <div class="card-header">
492
+ <div class="card-icon">🔧</div>
493
+ <div>
494
+ <div class="card-title">Apply Fix</div>
495
+ <div class="card-subtitle">Submit SQL fix or answer</div>
496
+ </div>
497
+ </div>
498
+ <div style="margin-bottom: 16px;">
499
+ <label style="display: block; margin-bottom: 8px; font-size: 13px; color: var(--text-secondary);">Fix Type</label>
500
+ <select id="fix-type">
501
+ <option value="sql">SQL Fix Statement</option>
502
+ <option value="answer">Text Answer</option>
503
+ </select>
504
+ </div>
505
+ <textarea id="fix-input" placeholder="UPDATE users SET email = 'unknown' WHERE email IS NULL;"></textarea>
506
+ <div class="action-buttons">
507
+ <button id="btn-fix">🔧 Apply Fix</button>
508
+ <button id="btn-submit" class="secondary">✅ Submit Final</button>
509
+ </div>
510
+ <div id="fix-output" class="output-area hidden"></div>
511
+ </div>
512
+ </div>
513
+ </div>
514
+
515
+ <script>
516
+ const API_BASE = window.location.origin;
517
+ let currentTask = 'null_audit';
518
+ let currentEpisode = null;
519
+ let stepCount = 0;
520
+
521
+ // Status check
522
+ async function checkStatus() {
523
+ const badge = document.getElementById('status-badge');
524
+ badge.className = 'status-badge pending';
525
+ badge.innerHTML = '<span class="loading"></span> Checking...';
526
+
527
+ try {
528
+ const res = await fetch(`${API_BASE}/health`);
529
+ if (res.ok) {
530
+ badge.className = 'status-badge online';
531
+ badge.innerHTML = '🟢 Online';
532
+ return true;
533
+ }
534
+ } catch (e) {}
535
+
536
+ badge.className = 'status-badge offline';
537
+ badge.innerHTML = '🔴 Offline';
538
+ return false;
539
+ }
540
+
541
+ // Format JSON output
542
+ function formatOutput(data, isError = false) {
543
+ const output = JSON.stringify(data, null, 2);
544
+ return output;
545
+ }
546
+
547
+ // Show output
548
+ function showOutput(elementId, data, isError = false) {
549
+ const el = document.getElementById(elementId);
550
+ el.textContent = formatOutput(data);
551
+ el.classList.remove('hidden', 'success', 'error');
552
+ el.classList.add(isError ? 'error' : 'success');
553
+ }
554
+
555
+ // Update metrics
556
+ function updateMetrics(observation, reward) {
557
+ if (observation) {
558
+ document.getElementById('metric-task').textContent = observation.task_id || currentTask;
559
+ document.getElementById('metric-episode').textContent = observation.episode_id?.slice(0, 8) + '...' || '-';
560
+ }
561
+ if (reward) {
562
+ document.getElementById('metric-score').textContent = reward.score?.toFixed(4) || '0.0000';
563
+ document.getElementById('metric-status').textContent = reward.done ? '✅ Complete' : '🔄 Active';
564
+ }
565
+ document.getElementById('metric-steps').textContent = stepCount;
566
+ }
567
+
568
+ // Task selection
569
+ document.querySelectorAll('.task-item').forEach(item => {
570
+ item.addEventListener('click', () => {
571
+ document.querySelectorAll('.task-item').forEach(i => i.classList.remove('active'));
572
+ item.classList.add('active');
573
+ currentTask = item.dataset.task;
574
+ });
575
+ });
576
+
577
+ // Reset task
578
+ document.getElementById('btn-reset').addEventListener('click', async () => {
579
+ const seed = parseInt(document.getElementById('seed-input').value) || 42;
580
+
581
+ try {
582
+ const res = await fetch(`${API_BASE}/reset`, {
583
+ method: 'POST',
584
+ headers: {'Content-Type': 'application/json'},
585
+ body: JSON.stringify({task_id: currentTask, seed: seed})
586
+ });
587
+
588
+ const data = await res.json();
589
+
590
+ if (res.ok) {
591
+ currentEpisode = data.episode_id;
592
+ stepCount = 0;
593
+ updateMetrics(data, null);
594
+ showOutput('query-output', {success: true, message: 'Task reset successfully', observation: data});
595
+ } else {
596
+ showOutput('query-output', data, true);
597
+ }
598
+ } catch (e) {
599
+ showOutput('query-output', {error: e.message}, true);
600
+ }
601
+ });
602
+
603
+ // Get state
604
+ document.getElementById('btn-state').addEventListener('click', async () => {
605
+ try {
606
+ const res = await fetch(`${API_BASE}/state`);
607
+ const data = await res.json();
608
+ showOutput('query-output', data);
609
+ } catch (e) {
610
+ showOutput('query-output', {error: e.message}, true);
611
+ }
612
+ });
613
+
614
+ // Run query
615
+ document.getElementById('btn-query').addEventListener('click', async () => {
616
+ const query = document.getElementById('query-input').value.trim();
617
+ if (!query) return;
618
+
619
+ stepCount++;
620
+
621
+ try {
622
+ const res = await fetch(`${API_BASE}/step`, {
623
+ method: 'POST',
624
+ headers: {'Content-Type': 'application/json'},
625
+ body: JSON.stringify({
626
+ action_type: 'query',
627
+ query: query,
628
+ task_id: currentTask
629
+ })
630
+ });
631
+
632
+ const data = await res.json();
633
+ showOutput('query-output', data);
634
+ updateMetrics(null, data.reward);
635
+ } catch (e) {
636
+ showOutput('query-output', {error: e.message}, true);
637
+ }
638
+ });
639
+
640
+ // Profile table
641
+ document.getElementById('btn-profile').addEventListener('click', async () => {
642
+ stepCount++;
643
+
644
+ try {
645
+ const res = await fetch(`${API_BASE}/step`, {
646
+ method: 'POST',
647
+ headers: {'Content-Type': 'application/json'},
648
+ body: JSON.stringify({
649
+ action_type: 'profile',
650
+ task_id: currentTask
651
+ })
652
+ });
653
+
654
+ const data = await res.json();
655
+ showOutput('query-output', data);
656
+ updateMetrics(null, data.reward);
657
+ } catch (e) {
658
+ showOutput('query-output', {error: e.message}, true);
659
+ }
660
+ });
661
+
662
+ // Apply fix
663
+ document.getElementById('btn-fix').addEventListener('click', async () => {
664
+ const fixContent = document.getElementById('fix-input').value.trim();
665
+ if (!fixContent) return;
666
+
667
+ const fixType = document.getElementById('fix-type').value;
668
+ stepCount++;
669
+
670
+ try {
671
+ const res = await fetch(`${API_BASE}/step`, {
672
+ method: 'POST',
673
+ headers: {'Content-Type': 'application/json'},
674
+ body: JSON.stringify({
675
+ action_type: 'fix',
676
+ [fixType === 'sql' ? 'fix_sql' : 'answer']: fixContent,
677
+ task_id: currentTask
678
+ })
679
+ });
680
+
681
+ const data = await res.json();
682
+ showOutput('fix-output', data);
683
+ updateMetrics(null, data.reward);
684
+ } catch (e) {
685
+ showOutput('fix-output', {error: e.message}, true);
686
+ }
687
+ });
688
+
689
+ // Submit final
690
+ document.getElementById('btn-submit').addEventListener('click', async () => {
691
+ stepCount++;
692
+
693
+ try {
694
+ const res = await fetch(`${API_BASE}/step`, {
695
+ method: 'POST',
696
+ headers: {'Content-Type': 'application/json'},
697
+ body: JSON.stringify({
698
+ action_type: 'submit',
699
+ task_id: currentTask
700
+ })
701
+ });
702
+
703
+ const data = await res.json();
704
+ showOutput('fix-output', data);
705
+ updateMetrics(null, data.reward);
706
+ } catch (e) {
707
+ showOutput('fix-output', {error: e.message}, true);
708
+ }
709
+ });
710
+
711
+ // Check connection button
712
+ document.getElementById('btn-check').addEventListener('click', checkStatus);
713
+
714
+ // Initial status check
715
+ checkStatus();
716
+ </script>
717
+ </body>
718
+ </html>