Deadman922 commited on
Commit
e2efaa6
·
verified ·
1 Parent(s): 1578906

Upload static/index.html

Browse files
Files changed (1) hide show
  1. static/index.html +422 -0
static/index.html ADDED
@@ -0,0 +1,422 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
10
+ <style>
11
+ * { margin: 0; padding: 0; box-sizing: border-box; }
12
+ :root {
13
+ --bg-primary: #f8fafc; --bg-secondary: #ffffff; --bg-sidebar: #ffffff;
14
+ --accent: #0d9488; --accent-hover: #14b8a6; --accent-light: #ccfbf1;
15
+ --text-primary: #1e293b; --text-secondary: #64748b; --text-muted: #94a3b8;
16
+ --border: #e2e8f0; --border-light: #f1f5f9;
17
+ --success: #22c55e; --warning: #f59e0b; --error: #ef4444;
18
+ --card-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
19
+ }
20
+ body { font-family: 'Inter', sans-serif; background: var(--bg-primary); color: var(--text-primary); min-height: 100vh; }
21
+ .app-container { display: flex; min-height: 100vh; }
22
+ .sidebar { width: 260px; background: var(--bg-sidebar); border-right: 1px solid var(--border); display: flex; flex-direction: column; position: fixed; height: 100vh; overflow-y: auto; }
23
+ .sidebar-header { padding: 24px 20px; border-bottom: 1px solid var(--border); }
24
+ .logo { display: flex; align-items: center; gap: 12px; }
25
+ .logo-icon { width: 40px; height: 40px; background: linear-gradient(135deg, var(--accent), var(--accent-hover)); border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 20px; color: white; font-weight: 600; }
26
+ .logo-text { font-size: 20px; font-weight: 700; color: var(--text-primary); }
27
+ .nav-section { padding: 16px 12px; flex: 1; }
28
+ .nav-title { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); margin-bottom: 8px; padding-left: 12px; }
29
+ .nav-item { display: flex; align-items: center; gap: 12px; padding: 12px 16px; border-radius: 8px; cursor: pointer; font-size: 14px; color: var(--text-secondary); margin-bottom: 4px; transition: all 0.2s; font-weight: 500; }
30
+ .nav-item:hover { background: var(--bg-primary); color: var(--text-primary); }
31
+ .nav-item.active { background: var(--accent-light); color: var(--accent); }
32
+ .nav-icon { width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; }
33
+ .nav-badge { margin-left: auto; background: var(--accent); color: white; font-size: 11px; font-weight: 600; padding: 2px 8px; border-radius: 10px; }
34
+ .main-content { flex: 1; margin-left: 260px; min-height: 100vh; }
35
+ .top-bar { background: var(--bg-secondary); border-bottom: 1px solid var(--border); padding: 16px 32px; display: flex; justify-content: space-between; align-items: center; position: sticky; top: 0; z-index: 10; }
36
+ .page-title { font-size: 24px; font-weight: 700; color: var(--text-primary); }
37
+ .page-subtitle { font-size: 14px; color: var(--text-secondary); margin-top: 2px; }
38
+ .status-pill { display: flex; align-items: center; gap: 8px; padding: 8px 16px; background: var(--bg-primary); border-radius: 20px; font-size: 13px; font-weight: 500; }
39
+ .status-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--success); animation: pulse 2s infinite; }
40
+ @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
41
+ .content-area { padding: 24px 32px; }
42
+ .card { background: var(--bg-secondary); border-radius: 12px; box-shadow: var(--card-shadow); border: 1px solid var(--border); overflow: hidden; }
43
+ .card-header { padding: 20px 24px; border-bottom: 1px solid var(--border-light); display: flex; justify-content: space-between; align-items: center; }
44
+ .card-title { font-size: 16px; font-weight: 600; color: var(--text-primary); }
45
+ .card-body { padding: 24px; }
46
+ .dashboard-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 24px; margin-bottom: 24px; }
47
+ .metric-card { background: var(--bg-secondary); border-radius: 12px; padding: 24px; box-shadow: var(--card-shadow); border: 1px solid var(--border); }
48
+ .metric-card.primary { background: linear-gradient(135deg, var(--accent), var(--accent-hover)); color: white; border: none; }
49
+ .metric-label { font-size: 13px; font-weight: 500; color: var(--text-secondary); margin-bottom: 8px; }
50
+ .metric-card.primary .metric-label { color: rgba(255,255,255,0.8); }
51
+ .metric-value { font-size: 32px; font-weight: 700; color: var(--text-primary); }
52
+ .metric-card.primary .metric-value { color: white; }
53
+ .metric-change { font-size: 12px; color: var(--text-muted); margin-top: 4px; display: flex; align-items: center; gap: 4px; }
54
+ .metric-card.primary .metric-change { color: rgba(255,255,255,0.7); }
55
+ .section-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 24px; }
56
+ .task-list { display: flex; flex-direction: column; gap: 12px; }
57
+ .task-item { display: flex; align-items: center; gap: 16px; padding: 16px; background: var(--bg-primary); border: 2px solid transparent; border-radius: 10px; cursor: pointer; transition: all 0.2s; }
58
+ .task-item:hover { border-color: var(--border); }
59
+ .task-item.selected { border-color: var(--accent); background: var(--accent-light); }
60
+ .task-icon { width: 44px; height: 44px; background: var(--bg-secondary); border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 20px; flex-shrink: 0; }
61
+ .task-item.selected .task-icon { background: var(--accent); color: white; }
62
+ .task-info { flex: 1; }
63
+ .task-name { font-weight: 600; font-size: 15px; margin-bottom: 4px; }
64
+ .task-desc { font-size: 13px; color: var(--text-secondary); }
65
+ .task-badge { font-size: 11px; font-weight: 600; text-transform: uppercase; padding: 6px 12px; border-radius: 6px; }
66
+ .task-badge.easy { background: #dcfce7; color: #16a34a; }
67
+ .task-badge.medium { background: #fef3c7; color: #d97706; }
68
+ .task-badge.hard { background: #fee2e2; color: #dc2626; }
69
+ .form-group { margin-bottom: 20px; }
70
+ .form-label { display: block; font-size: 13px; font-weight: 600; color: var(--text-secondary); margin-bottom: 8px; }
71
+ input, select, textarea { width: 100%; background: var(--bg-primary); border: 1px solid var(--border); border-radius: 8px; padding: 12px 16px; color: var(--text-primary); font-family: 'Inter', sans-serif; font-size: 14px; outline: none; transition: all 0.2s; }
72
+ input:focus, select:focus, textarea:focus { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(13, 148, 136, 0.1); }
73
+ textarea { font-family: 'JetBrains Mono', monospace; font-size: 13px; min-height: 160px; resize: vertical; line-height: 1.6; }
74
+ .btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; padding: 12px 24px; font-size: 14px; font-weight: 600; border-radius: 8px; cursor: pointer; transition: all 0.2s; border: none; outline: none; }
75
+ .btn-primary { background: var(--accent); color: white; }
76
+ .btn-primary:hover { background: var(--accent-hover); }
77
+ .btn-secondary { background: var(--bg-primary); color: var(--text-primary); border: 1px solid var(--border); }
78
+ .btn-secondary:hover { background: var(--border-light); }
79
+ .btn-group { display: flex; gap: 12px; flex-wrap: wrap; }
80
+ .output-panel { background: var(--bg-primary); border: 1px solid var(--border); border-radius: 8px; padding: 16px; font-family: 'JetBrains Mono', monospace; font-size: 13px; min-height: 200px; max-height: 400px; overflow-y: auto; white-space: pre-wrap; word-break: break-word; color: var(--text-secondary); line-height: 1.6; }
81
+ .output-panel.success { border-color: var(--success); background: #f0fdf4; }
82
+ .output-panel.error { border-color: var(--error); background: #fef2f2; color: var(--error); }
83
+ .tab-group { display: flex; gap: 8px; margin-bottom: 20px; }
84
+ .tab-btn { padding: 10px 20px; font-size: 13px; font-weight: 500; color: var(--text-secondary); background: var(--bg-primary); border: 1px solid var(--border); border-radius: 8px; cursor: pointer; transition: all 0.2s; }
85
+ .tab-btn:hover { color: var(--text-primary); }
86
+ .tab-btn.active { background: var(--accent); color: white; border-color: var(--accent); }
87
+ .page-section { display: none; }
88
+ .page-section.active { display: block; animation: fadeIn 0.3s ease; }
89
+ @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
90
+ @media (max-width: 1200px) { .dashboard-grid { grid-template-columns: repeat(2, 1fr); } .section-grid { grid-template-columns: 1fr; } }
91
+ @media (max-width: 768px) { .sidebar { width: 100%; position: relative; height: auto; } .main-content { margin-left: 0; } .app-container { flex-direction: column; } .dashboard-grid { grid-template-columns: 1fr; } }
92
+ @keyframes spin { to { transform: rotate(360deg); } }
93
+ .loading { display: inline-block; width: 16px; height: 16px; border: 2px solid var(--border); border-top-color: var(--accent); border-radius: 50%; animation: spin 1s linear infinite; }
94
+ </style>
95
+ </head>
96
+ <body>
97
+ <div class="app-container">
98
+ <aside class="sidebar">
99
+ <div class="sidebar-header">
100
+ <div class="logo">
101
+ <div class="logo-icon">◆</div>
102
+ <div class="logo-text">SQLEnv</div>
103
+ </div>
104
+ </div>
105
+ <nav class="nav-section">
106
+ <div class="nav-title">Menu</div>
107
+ <div class="nav-item active" data-page="dashboard">
108
+ <span class="nav-icon">◈</span>Dashboard
109
+ </div>
110
+ <div class="nav-item" data-page="tasks">
111
+ <span class="nav-icon">◉</span>Tasks<span class="nav-badge">5</span>
112
+ </div>
113
+ <div class="nav-item" data-page="query">
114
+ <span class="nav-icon">◆</span>Query
115
+ </div>
116
+ <div class="nav-item" data-page="fix">
117
+ <span class="nav-icon">◆</span>Fix
118
+ </div>
119
+ </nav>
120
+ </aside>
121
+ <main class="main-content">
122
+ <div class="top-bar">
123
+ <div>
124
+ <div class="page-title" id="page-title">Dashboard</div>
125
+ <div class="page-subtitle" id="page-subtitle">Overview and system status</div>
126
+ </div>
127
+ <div class="status-pill">
128
+ <div class="status-dot"></div>
129
+ <span id="connection-status">Connected</span>
130
+ </div>
131
+ </div>
132
+ <div class="content-area">
133
+ <!-- DASHBOARD PAGE -->
134
+ <div class="page-section active" id="page-dashboard">
135
+ <div class="dashboard-grid">
136
+ <div class="metric-card primary">
137
+ <div class="metric-label">Available Tasks</div>
138
+ <div class="metric-value">5</div>
139
+ <div class="metric-change">All systems operational</div>
140
+ </div>
141
+ <div class="metric-card">
142
+ <div class="metric-label">Current Task</div>
143
+ <div class="metric-value" id="dash-task">-</div>
144
+ <div class="metric-change">Active selection</div>
145
+ </div>
146
+ <div class="metric-card">
147
+ <div class="metric-label">Steps Taken</div>
148
+ <div class="metric-value" id="dash-steps">0</div>
149
+ <div class="metric-change">Episode progress</div>
150
+ </div>
151
+ <div class="metric-card">
152
+ <div class="metric-label">Current Score</div>
153
+ <div class="metric-value" id="dash-score">0.00</div>
154
+ <div class="metric-change">Out of 1.0</div>
155
+ </div>
156
+ </div>
157
+ <div class="section-grid">
158
+ <div class="card">
159
+ <div class="card-header"><div class="card-title">Quick Actions</div></div>
160
+ <div class="card-body">
161
+ <div class="form-group">
162
+ <label class="form-label">Random Seed</label>
163
+ <input type="number" id="dash-seed" value="42" min="1" max="999999">
164
+ </div>
165
+ <div class="btn-group">
166
+ <button class="btn btn-primary" onclick="resetFromDashboard()">Reset Task</button>
167
+ <button class="btn btn-secondary" onclick="showPage('tasks')">Go to Tasks</button>
168
+ </div>
169
+ </div>
170
+ </div>
171
+ <div class="card">
172
+ <div class="card-header"><div class="card-title">System Info</div></div>
173
+ <div class="card-body">
174
+ <div class="task-list">
175
+ <div class="task-item">
176
+ <div class="task-icon">🔗</div>
177
+ <div class="task-info">
178
+ <div class="task-name">API Endpoint</div>
179
+ <div class="task-desc">/health, /reset, /step, /state</div>
180
+ </div>
181
+ </div>
182
+ <div class="task-item">
183
+ <div class="task-icon">⚡</div>
184
+ <div class="task-info">
185
+ <div class="task-name">Action Types</div>
186
+ <div class="task-desc">query, profile, fix, submit</div>
187
+ </div>
188
+ </div>
189
+ </div>
190
+ </div>
191
+ </div>
192
+ </div>
193
+ </div>
194
+ <!-- TASKS PAGE -->
195
+ <div class="page-section" id="page-tasks">
196
+ <div class="card">
197
+ <div class="card-header"><div class="card-title">Select a Data Quality Task</div></div>
198
+ <div class="card-body">
199
+ <div class="task-list">
200
+ <div class="task-item selected" data-task="null_audit" onclick="selectTask(this)">
201
+ <div class="task-icon">🔍</div>
202
+ <div class="task-info">
203
+ <div class="task-name">Null Audit</div>
204
+ <div class="task-desc">Identify columns with >50% null values and determine appropriate remediation strategy</div>
205
+ </div>
206
+ <span class="task-badge easy">Easy</span>
207
+ </div>
208
+ <div class="task-item" data-task="type_repair" onclick="selectTask(this)">
209
+ <div class="task-icon">🔧</div>
210
+ <div class="task-info">
211
+ <div class="task-name">Type Repair</div>
212
+ <div class="task-desc">Fix numeric type mismatches in transaction data that cause aggregation errors</div>
213
+ </div>
214
+ <span class="task-badge medium">Medium</span>
215
+ </div>
216
+ <div class="task-item" data-task="dedup_bug" onclick="selectTask(this)">
217
+ <div class="task-icon">📊</div>
218
+ <div class="task-info">
219
+ <div class="task-name">Dedup Bug</div>
220
+ <div class="task-desc">Diagnose and resolve data duplication issues in JOIN queries</div>
221
+ </div>
222
+ <span class="task-badge hard">Hard</span>
223
+ </div>
224
+ <div class="task-item" data-task="fk_integrity" onclick="selectTask(this)">
225
+ <div class="task-icon">🔐</div>
226
+ <div class="task-info">
227
+ <div class="task-name">FK Integrity</div>
228
+ <div class="task-desc">Remove orphaned foreign key records and restore referential integrity</div>
229
+ </div>
230
+ <span class="task-badge hard">Hard</span>
231
+ </div>
232
+ <div class="task-item" data-task="schema_drift" onclick="selectTask(this)">
233
+ <div class="task-icon">📋</div>
234
+ <div class="task-info">
235
+ <div class="task-name">Schema Drift</div>
236
+ <div class="task-desc">Canonicalize schema drift between table versions for consistent querying</div>
237
+ </div>
238
+ <span class="task-badge medium">Medium</span>
239
+ </div>
240
+ </div>
241
+ <div style="margin-top: 24px; padding-top: 24px; border-top: 1px solid var(--border-light);">
242
+ <div class="form-group">
243
+ <label class="form-label">Random Seed</label>
244
+ <input type="number" id="task-seed" value="42" min="1" max="999999">
245
+ </div>
246
+ <div class="btn-group">
247
+ <button class="btn btn-primary" onclick="resetTask()">Reset Selected Task</button>
248
+ <button class="btn btn-secondary" onclick="showPage('query')">Go to Query →</button>
249
+ </div>
250
+ </div>
251
+ </div>
252
+ </div>
253
+ </div>
254
+ <!-- QUERY PAGE -->
255
+ <div class="page-section" id="page-query">
256
+ <div class="section-grid">
257
+ <div class="card">
258
+ <div class="card-header"><div class="card-title">Execute SQL Query</div></div>
259
+ <div class="card-body">
260
+ <div class="form-group">
261
+ <label class="form-label">SQL Query</label>
262
+ <textarea id="query-input" placeholder="SELECT * FROM users WHERE email IS NULL LIMIT 10;"></textarea>
263
+ </div>
264
+ <div class="btn-group">
265
+ <button class="btn btn-primary" onclick="runQuery()">Run Query</button>
266
+ <button class="btn btn-secondary" onclick="profileTable()">Profile Table</button>
267
+ </div>
268
+ </div>
269
+ </div>
270
+ <div class="card">
271
+ <div class="card-header"><div class="card-title">Query Results</div></div>
272
+ <div class="card-body">
273
+ <div id="query-output" class="output-panel">Run a query to see results...</div>
274
+ </div>
275
+ </div>
276
+ </div>
277
+ </div>
278
+ <!-- FIX PAGE -->
279
+ <div class="page-section" id="page-fix">
280
+ <div class="section-grid">
281
+ <div class="card">
282
+ <div class="card-header"><div class="card-title">Apply Fix or Submit Answer</div></div>
283
+ <div class="card-body">
284
+ <div class="tab-group">
285
+ <button class="tab-btn active" onclick="switchTab(this, 'sql')">SQL Fix</button>
286
+ <button class="tab-btn" onclick="switchTab(this, 'answer')">Text Answer</button>
287
+ </div>
288
+ <div class="form-group">
289
+ <label class="form-label" id="fix-label">Fix SQL Statement</label>
290
+ <textarea id="fix-input" placeholder="UPDATE users SET email = 'unknown@example.com' WHERE email IS NULL;"></textarea>
291
+ </div>
292
+ <div class="btn-group">
293
+ <button class="btn btn-primary" onclick="applyFix()">Apply Fix</button>
294
+ <button class="btn btn-secondary" onclick="submitFinal()">Submit Final</button>
295
+ </div>
296
+ </div>
297
+ </div>
298
+ <div class="card">
299
+ <div class="card-header"><div class="card-title">Fix Results</div></div>
300
+ <div class="card-body">
301
+ <div id="fix-output" class="output-panel">Apply a fix to see results...</div>
302
+ </div>
303
+ </div>
304
+ </div>
305
+ </div>
306
+ </div>
307
+ </main>
308
+ </div>
309
+ <script>
310
+ const API_BASE = window.location.origin;
311
+ let currentTask = 'null_audit', currentEpisode = null, stepCount = 0, currentScore = 0, fixType = 'sql';
312
+ function showPage(pageName) {
313
+ document.querySelectorAll('.page-section').forEach(s => s.classList.remove('active'));
314
+ document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
315
+ document.getElementById('page-' + pageName).classList.add('active');
316
+ document.querySelector('.nav-item[data-page="' + pageName + '"]').classList.add('active');
317
+ const titles = { dashboard: ['Dashboard','Overview and system status'], tasks: ['Tasks','Select a data quality scenario'], query: ['Query','Execute SQL to investigate data issues'], fix: ['Fix','Apply SQL fixes or submit answers'] };
318
+ document.getElementById('page-title').textContent = titles[pageName][0];
319
+ document.getElementById('page-subtitle').textContent = titles[pageName][1];
320
+ }
321
+ document.querySelectorAll('.nav-item').forEach(item => { item.addEventListener('click', () => { showPage(item.dataset.page); }); });
322
+ async function checkConnection() {
323
+ try {
324
+ const res = await fetch(`${API_BASE}/health`);
325
+ const el = document.getElementById('connection-status');
326
+ if (res.ok) { el.textContent = 'Connected'; el.style.color = 'var(--success)'; }
327
+ else { el.textContent = 'Error'; el.style.color = 'var(--error)'; }
328
+ } catch (e) { document.getElementById('connection-status').textContent = 'Offline'; document.getElementById('connection-status').style.color = 'var(--error)'; }
329
+ }
330
+ function selectTask(el) {
331
+ document.querySelectorAll('.task-item').forEach(t => t.classList.remove('selected'));
332
+ el.classList.add('selected');
333
+ currentTask = el.dataset.task;
334
+ updateDashboard();
335
+ }
336
+ function updateDashboard() {
337
+ document.getElementById('dash-task').textContent = currentTask.replace(/_/g, ' ');
338
+ document.getElementById('dash-steps').textContent = stepCount;
339
+ document.getElementById('dash-score').textContent = currentScore.toFixed(2);
340
+ }
341
+ function formatOutput(data) { return JSON.stringify(data, null, 2); }
342
+ function showOutput(elId, data, isErr) {
343
+ const el = document.getElementById(elId);
344
+ el.textContent = formatOutput(data);
345
+ el.className = 'output-panel ' + (isErr ? 'error' : 'success');
346
+ }
347
+ async function resetTask() {
348
+ const seed = parseInt(document.getElementById('task-seed').value) || 42;
349
+ try {
350
+ const res = await fetch(`${API_BASE}/reset`, { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({task_id: currentTask, seed: seed}) });
351
+ const data = await res.json();
352
+ if (res.ok) {
353
+ currentEpisode = data.episode_id; stepCount = 0; currentScore = 0;
354
+ updateDashboard();
355
+ showOutput('query-output', {success: true, message: 'Task reset successfully', observation: data});
356
+ alert('Task reset successfully! Go to Query page to start investigating.');
357
+ } else { showOutput('query-output', data, true); }
358
+ } catch (e) { showOutput('query-output', {error: e.message}, true); }
359
+ }
360
+ function resetFromDashboard() {
361
+ const seed = parseInt(document.getElementById('dash-seed').value) || 42;
362
+ document.getElementById('task-seed').value = seed;
363
+ showPage('tasks');
364
+ setTimeout(resetTask, 100);
365
+ }
366
+ async function runQuery() {
367
+ const query = document.getElementById('query-input').value.trim();
368
+ if (!query) { alert('Please enter a SQL query'); return; }
369
+ stepCount++;
370
+ try {
371
+ const res = await fetch(`${API_BASE}/step`, { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({action_type: 'query', query: query, task_id: currentTask}) });
372
+ const data = await res.json();
373
+ showOutput('query-output', data);
374
+ if (data.reward) { currentScore = data.reward.score || 0; updateDashboard(); }
375
+ } catch (e) { showOutput('query-output', {error: e.message}, true); }
376
+ }
377
+ async function profileTable() {
378
+ stepCount++;
379
+ try {
380
+ const res = await fetch(`${API_BASE}/step`, { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({action_type: 'profile', task_id: currentTask}) });
381
+ const data = await res.json();
382
+ showOutput('query-output', data);
383
+ if (data.reward) { currentScore = data.reward.score || 0; updateDashboard(); }
384
+ } catch (e) { showOutput('query-output', {error: e.message}, true); }
385
+ }
386
+ function switchTab(btn, type) {
387
+ document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
388
+ btn.classList.add('active');
389
+ fixType = type;
390
+ document.getElementById('fix-label').textContent = type === 'sql' ? 'Fix SQL Statement' : 'Text Answer';
391
+ document.getElementById('fix-input').placeholder = type === 'sql' ? "UPDATE users SET email = 'unknown@example.com' WHERE email IS NULL;" : "The null values should be replaced with default values...";
392
+ }
393
+ async function applyFix() {
394
+ const fixContent = document.getElementById('fix-input').value.trim();
395
+ if (!fixContent) { alert('Please enter a fix'); return; }
396
+ stepCount++;
397
+ try {
398
+ const res = await fetch(`${API_BASE}/step`, { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({action_type: 'fix', [fixType === 'sql' ? 'fix_sql' : 'answer']: fixContent, task_id: currentTask}) });
399
+ const data = await res.json();
400
+ showOutput('fix-output', data);
401
+ if (data.reward) { currentScore = data.reward.score || 0; updateDashboard(); }
402
+ } catch (e) { showOutput('fix-output', {error: e.message}, true); }
403
+ }
404
+ async function submitFinal() {
405
+ stepCount++;
406
+ try {
407
+ const res = await fetch(`${API_BASE}/step`, { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({action_type: 'submit', task_id: currentTask}) });
408
+ const data = await res.json();
409
+ showOutput('fix-output', data);
410
+ if (data.reward) {
411
+ currentScore = data.reward.score || 0;
412
+ updateDashboard();
413
+ if (data.reward.done) { alert('Episode complete! Final score: ' + currentScore.toFixed(2)); }
414
+ }
415
+ } catch (e) { showOutput('fix-output', {error: e.message}, true); }
416
+ }
417
+ checkConnection();
418
+ setInterval(checkConnection, 30000);
419
+ updateDashboard();
420
+ </script>
421
+ </body>
422
+ </html>