Deadman922 commited on
Commit
2ff57aa
Β·
verified Β·
1 Parent(s): b34c341

Upload index.html

Browse files
Files changed (1) hide show
  1. static/index.html +215 -2
static/index.html CHANGED
@@ -686,6 +686,28 @@
686
  <!-- MAIN CONTENT -->
687
  <main class="main">
688
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
689
  <!-- HOW TO USE SECTION -->
690
  <div class="help-section" id="help-section">
691
  <div class="help-title">
@@ -964,6 +986,13 @@ let state = {
964
  schemaInfo: null,
965
  };
966
 
 
 
 
 
 
 
 
967
  // ─── TASK DEFINITIONS ─────────────────────────────────────────────────
968
  const TASKS = {
969
  null_audit: {
@@ -1143,6 +1172,46 @@ async function resetEpisode() {
1143
 
1144
  // ─── QUERY ────────────────────────────────────────────────────────────
1145
  async function runQuery() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1146
  if (!state.currentTask) { toast('No active task β€” initialize first', 'error'); return; }
1147
  const sql = document.getElementById('query-sql').value.trim();
1148
  if (!sql) { toast('Enter a SQL statement', 'error'); return; }
@@ -1199,7 +1268,8 @@ async function runQuery() {
1199
  }
1200
  }
1201
  } else {
1202
- output.innerHTML = '<div class="output-error">Error: ' + escapeHtml(r.data.detail || 'Unknown error') + '</div>';
 
1203
  }
1204
  setLoading('query', false);
1205
  }
@@ -1282,6 +1352,7 @@ function verifyFix() {
1282
  // ─── SUBMIT ───────────────────────────────────────────────────────────
1283
  async function submitEpisode() {
1284
  if (!state.currentTask) { toast('No active task', 'error'); return; }
 
1285
 
1286
  const task = TASKS[state.currentTask];
1287
  const body = {
@@ -1340,6 +1411,144 @@ async function submitEpisode() {
1340
  setLoading('submit', false);
1341
  }
1342
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1343
  // ─── SCHEMA RENDERING ─────────────────────────────────────────────────
1344
  function parseColumn(col) {
1345
  // col is either a string "email (TEXT)" or an object {name, type, nullable}
@@ -1392,7 +1601,11 @@ function renderSchema(schemaInfo) {
1392
  html += '<tr><td class="col-name">' + escapeHtml(parsed.name) + '</td>';
1393
  html += '<td class="col-type"><span class="' + typeClass + '">' + escapeHtml(parsed.type) + '</span></td></tr>';
1394
  }
1395
- html += '</tbody></table></div>';
 
 
 
 
1396
  }
1397
  panel.innerHTML = html;
1398
  }
 
686
  <!-- MAIN CONTENT -->
687
  <main class="main">
688
 
689
+ <!-- ─── UPLOAD SECTION ──────────────────────────────────────────────── -->
690
+ <div class="help-section" style="background: linear-gradient(135deg, #E3F2FD 0%, #F3E5F5 100%); border-color: #90CAF9; margin-bottom: 16px;" id="upload-section">
691
+ <div class="help-title" style="color: #1565C0;">
692
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
693
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
694
+ <polyline points="17 8 12 3 7 8"/>
695
+ <line x1="12" y1="3" x2="12" y2="15"/>
696
+ </svg>
697
+ Custom Database Upload
698
+ </div>
699
+ <div style="display: flex; gap: 12px; align-items: center; flex-wrap: wrap;">
700
+ <span style="font-size: 13px; color: var(--text-secondary);">Or use your own database:</span>
701
+ <input type="file" id="db-file-input" accept=".sqlite,.db,.csv" style="font-size: 13px; padding: 6px; border: 1px solid var(--card-border); border-radius: var(--radius);">
702
+ <button class="btn btn-primary" onclick="uploadDatabase()" id="upload-btn">
703
+ <span class="spinner" id="upload-spinner"></span>
704
+ <span id="upload-btn-text">Upload</span>
705
+ </button>
706
+ <span id="upload-status" style="font-size: 12px; color: var(--text-secondary);">No file loaded</span>
707
+ <button class="btn btn-secondary" onclick="backToTasks()" id="back-to-tasks-btn" style="display: none;">Back to Tasks</button>
708
+ </div>
709
+ <div id="upload-error" class="output-error" style="display: none; margin-top: 12px;"></div>
710
+ </div>
711
  <!-- HOW TO USE SECTION -->
712
  <div class="help-section" id="help-section">
713
  <div class="help-title">
 
986
  schemaInfo: null,
987
  };
988
 
989
+ // ─── MODE ───────────────────────────────────────────────────────────────
990
+ let mode = 'task'; // 'task' or 'custom'
991
+ let customDbLoaded = false;
992
+ let customDbSchema = [];
993
+ let customDbFilename = '';
994
+ let firstCustomTable = '';
995
+
996
  // ─── TASK DEFINITIONS ─────────────────────────────────────────────────
997
  const TASKS = {
998
  null_audit: {
 
1172
 
1173
  // ─── QUERY ────────────────────────────────────────────────────────────
1174
  async function runQuery() {
1175
+ if (mode === 'custom') {
1176
+ // Custom DB mode - use /query-custom endpoint
1177
+ const sql = document.getElementById('query-sql').value.trim();
1178
+ if (!sql) { toast('Enter a SQL statement', 'error'); return; }
1179
+
1180
+ setLoading('query', true);
1181
+ const container = document.getElementById('query-output-container');
1182
+ const output = document.getElementById('query-output');
1183
+ container.style.display = 'block';
1184
+
1185
+ try {
1186
+ const r = await api('POST', '/query-custom', { sql: sql });
1187
+
1188
+ if (r.ok) {
1189
+ if (r.data.error) {
1190
+ // SQL error with message
1191
+ output.innerHTML = '<div class="output-error">' + escapeHtml(r.data.error) +
1192
+ '<div class="error-tip">Tip: ' + escapeHtml(r.data.message || 'Check your query') + '</div></div>';
1193
+ } else if (r.data.rows) {
1194
+ // Success - format as table
1195
+ output.innerHTML = formatArrayAsTable(r.data.rows);
1196
+ if (r.data.row_count > 0) {
1197
+ output.innerHTML = '<div class="result-summary"><strong>' + r.data.row_count + '</strong> rows returned</div>' + output.innerHTML;
1198
+ }
1199
+ } else {
1200
+ output.innerHTML = '<div class="output-body empty">' + escapeHtml(r.data.message || 'No data') + '</div>';
1201
+ }
1202
+ } else {
1203
+ output.innerHTML = '<div class="output-error">Error: ' + escapeHtml(r.data.detail || 'Failed to execute query') +
1204
+ '<div class="error-tip">Tip: Only SELECT queries are allowed. Check table names in schema panel.</div></div>';
1205
+ }
1206
+ } catch (e) {
1207
+ output.innerHTML = '<div class="output-error">Error: ' + escapeHtml(e.message) + '</div>';
1208
+ }
1209
+
1210
+ setLoading('query', false);
1211
+ return;
1212
+ }
1213
+
1214
+ // RL Task mode - use /step endpoint
1215
  if (!state.currentTask) { toast('No active task β€” initialize first', 'error'); return; }
1216
  const sql = document.getElementById('query-sql').value.trim();
1217
  if (!sql) { toast('Enter a SQL statement', 'error'); return; }
 
1268
  }
1269
  }
1270
  } else {
1271
+ output.innerHTML = '<div class="output-error">Error: ' + escapeHtml(r.data.detail || 'Unknown error') +
1272
+ '<div class="error-tip">Tip: Check your SQL syntax and table names.</div></div>';
1273
  }
1274
  setLoading('query', false);
1275
  }
 
1352
  // ─── SUBMIT ───────────────────────────────────────────────────────────
1353
  async function submitEpisode() {
1354
  if (!state.currentTask) { toast('No active task', 'error'); return; }
1355
+ if (mode === 'custom') { toast('Submit not available in custom DB mode', 'error'); return; }
1356
 
1357
  const task = TASKS[state.currentTask];
1358
  const body = {
 
1411
  setLoading('submit', false);
1412
  }
1413
 
1414
+ // ─── CUSTOM DB UPLOAD ─────────────────────────────────────────────────
1415
+ async function uploadDatabase() {
1416
+ const fileInput = document.getElementById('db-file-input');
1417
+ const file = fileInput.files[0];
1418
+
1419
+ if (!file) {
1420
+ toast('Please select a file first', 'error');
1421
+ return;
1422
+ }
1423
+
1424
+ const validExts = ['.sqlite', '.db', '.csv'];
1425
+ const ext = file.name.slice(file.name.lastIndexOf('.')).toLowerCase();
1426
+ if (!validExts.includes(ext)) {
1427
+ document.getElementById('upload-error').style.display = 'block';
1428
+ document.getElementById('upload-error').innerHTML = 'Invalid file type. Must be .sqlite, .db, or .csv';
1429
+ return;
1430
+ }
1431
+
1432
+ setLoading('upload', true);
1433
+ document.getElementById('upload-error').style.display = 'none';
1434
+
1435
+ const formData = new FormData();
1436
+ formData.append('file', file);
1437
+
1438
+ try {
1439
+ const res = await fetch(BASE + '/upload-db', {
1440
+ method: 'POST',
1441
+ body: formData
1442
+ });
1443
+
1444
+ const data = await res.json();
1445
+
1446
+ if (res.ok && data.status === 'ok') {
1447
+ mode = 'custom';
1448
+ customDbLoaded = true;
1449
+ customDbSchema = data.tables;
1450
+ customDbFilename = data.filename;
1451
+ firstCustomTable = data.tables[0]?.table_name || 'table';
1452
+
1453
+ // Update UI
1454
+ document.getElementById('upload-status').innerHTML = 'βœ“ ' + data.filename + ' loaded (' + data.tables.length + ' tables)';
1455
+ document.getElementById('upload-status').style.color = 'var(--success)';
1456
+ document.getElementById('back-to-tasks-btn').style.display = 'inline-flex';
1457
+
1458
+ // Update schema panel
1459
+ renderSchema(data.tables);
1460
+
1461
+ // Clear and set placeholder
1462
+ document.getElementById('query-sql').value = '';
1463
+ document.getElementById('query-sql').placeholder = 'SELECT * FROM ' + firstCustomTable + ' LIMIT 10';
1464
+
1465
+ // Show custom mode UI
1466
+ updateUIForMode();
1467
+
1468
+ toast('Database uploaded successfully', 'success');
1469
+ } else {
1470
+ document.getElementById('upload-error').style.display = 'block';
1471
+ document.getElementById('upload-error').innerHTML = escapeHtml(data.detail || 'Upload failed');
1472
+ toast('Upload failed', 'error');
1473
+ }
1474
+ } catch (e) {
1475
+ document.getElementById('upload-error').style.display = 'block';
1476
+ document.getElementById('upload-error').innerHTML = 'Error: ' + escapeHtml(e.message);
1477
+ toast('Upload error', 'error');
1478
+ }
1479
+
1480
+ setLoading('upload', false);
1481
+ }
1482
+
1483
+ function backToTasks() {
1484
+ mode = 'task';
1485
+ customDbLoaded = false;
1486
+ customDbSchema = [];
1487
+ document.getElementById('upload-status').textContent = 'No file loaded';
1488
+ document.getElementById('upload-status').style.color = 'var(--text-secondary)';
1489
+ document.getElementById('back-to-tasks-btn').style.display = 'none';
1490
+ document.getElementById('db-file-input').value = '';
1491
+
1492
+ // Reset UI
1493
+ updateUIForMode();
1494
+
1495
+ // Reset schema panel if no task selected
1496
+ if (!state.currentTask) {
1497
+ document.getElementById('schema-panel').innerHTML = '<div class="output-body empty">Select a task to view schema</div>';
1498
+ } else {
1499
+ renderSchema(state.schemaInfo);
1500
+ }
1501
+
1502
+ toast('Back to RL Task mode', 'success');
1503
+ }
1504
+
1505
+ function updateUIForMode() {
1506
+ const fixTabs = document.getElementById('fix-tabs');
1507
+ const queryPanel = document.getElementById('investigate-panel-query');
1508
+ const profilePanel = document.getElementById('investigate-panel-profile');
1509
+ const taskSelect = document.getElementById('task-select');
1510
+ const resetBtn = document.getElementById('reset-btn');
1511
+
1512
+ if (mode === 'custom') {
1513
+ // Hide fix/submit tabs
1514
+ if (fixTabs) fixTabs.style.display = 'none';
1515
+ // Hide profile tab
1516
+ if (profilePanel) {
1517
+ const profileTab = document.querySelector('.tab[onclick*="profile"]');
1518
+ if (profileTab) profileTab.style.display = 'none';
1519
+ }
1520
+ // Disable task selector
1521
+ taskSelect.disabled = true;
1522
+ resetBtn.disabled = true;
1523
+
1524
+ // Add custom mode indicator
1525
+ let indicator = document.getElementById('custom-mode-indicator');
1526
+ if (!indicator) {
1527
+ indicator = document.createElement('div');
1528
+ indicator.id = 'custom-mode-indicator';
1529
+ indicator.className = 'help-section';
1530
+ indicator.style.cssText = 'background: #E3F2FD; border-color: #90CAF9; margin-top: 16px;';
1531
+ indicator.innerHTML = '<div style="font-size: 13px; color: #1565C0;"><strong>Custom DB Mode</strong> β€” No scoring available. Use SELECT queries to explore your data.</div>';
1532
+ queryPanel.insertBefore(indicator, queryPanel.firstChild);
1533
+ }
1534
+ indicator.style.display = 'block';
1535
+
1536
+ } else {
1537
+ // Show all tabs
1538
+ if (fixTabs) fixTabs.style.display = 'flex';
1539
+ const profileTab = document.querySelector('.tab[onclick*="profile"]');
1540
+ if (profileTab) profileTab.style.display = 'block';
1541
+
1542
+ // Enable task selector
1543
+ taskSelect.disabled = false;
1544
+ resetBtn.disabled = false;
1545
+
1546
+ // Hide custom mode indicator
1547
+ const indicator = document.getElementById('custom-mode-indicator');
1548
+ if (indicator) indicator.style.display = 'none';
1549
+ }
1550
+ }
1551
+
1552
  // ─── SCHEMA RENDERING ─────────────────────────────────────────────────
1553
  function parseColumn(col) {
1554
  // col is either a string "email (TEXT)" or an object {name, type, nullable}
 
1601
  html += '<tr><td class="col-name">' + escapeHtml(parsed.name) + '</td>';
1602
  html += '<td class="col-type"><span class="' + typeClass + '">' + escapeHtml(parsed.type) + '</span></td></tr>';
1603
  }
1604
+ html += '</tbody></table>';
1605
+ html += '<div style="margin-top: 8px; padding: 6px 8px; background: var(--surface); border-radius: 4px; font-size: 11px; color: var(--text-tertiary);">';
1606
+ html += 'Tip: Use <code style="background: var(--bg); padding: 1px 4px; border-radius: 3px;">SELECT * FROM ' + escapeHtml(tableName) + ' LIMIT 10</code> to explore this table';
1607
+ html += '</div>';
1608
+ html += '</div>';
1609
  }
1610
  panel.innerHTML = html;
1611
  }