MissSqui commited on
Commit
cc2dcdf
·
verified ·
1 Parent(s): 02a591b

Update abc12

Browse files
Files changed (1) hide show
  1. abc12 +849 -1
abc12 CHANGED
@@ -1092,4 +1092,852 @@ def modify_experiment_zone(zone_id: int, name: str, vector_db: str, llm_model: s
1092
 
1093
  cursor.close()
1094
 
1095
- conn.close()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1092
 
1093
  cursor.close()
1094
 
1095
+ conn.close()
1096
+
1097
+
1098
+
1099
+ -----------------------------------------------------------------------------
1100
+ <!DOCTYPE html>
1101
+ <html lang="en">
1102
+ <head>
1103
+ <meta charset="UTF-8" />
1104
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
1105
+ <title>Chat Interface</title>
1106
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
1107
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" rel="stylesheet">
1108
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
1109
+ <script src="https://cdn.tailwindcss.com"></script>
1110
+ <style>
1111
+ /* Unified CSS for Chat, EffortsHomePage, and NotebookPage */
1112
+
1113
+ body {
1114
+ background-color: #f5f8fc;
1115
+ color: #1a1a1a;
1116
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
1117
+ padding-bottom: 4rem;
1118
+ }
1119
+
1120
+ .bg-gradient-to-r {
1121
+ background: linear-gradient(90deg, #4f46e5, #3b82f6) !important;
1122
+ }
1123
+
1124
+ .text-white {
1125
+ color: #fff !important;
1126
+ }
1127
+
1128
+ .card.card-item,
1129
+ .w-1\/4.bg-white,
1130
+ .w-3\/4.bg-white {
1131
+ background-color: #ffffff !important;
1132
+ border: 1px solid #d0d7e2 !important;
1133
+ color: #1a1a1a;
1134
+ border-radius: 10px !important;
1135
+ box-shadow: 0 1px 4px rgba(0,0,0,0.05) !important;
1136
+ padding: 1rem 1.5rem !important;
1137
+ }
1138
+
1139
+ .shadow-md, .shadow {
1140
+ box-shadow: 0 1px 4px rgba(0,0,0,0.05) !important;
1141
+ }
1142
+
1143
+ .rounded-2xl, .rounded-lg {
1144
+ border-radius: 10px !important;
1145
+ }
1146
+
1147
+ #chat-box {
1148
+ background: #f8f9fc !important;
1149
+ border: 1px solid #d0d7e2 !important;
1150
+ border-radius: 10px !important;
1151
+ color: #1a1a1a;
1152
+ height: 26rem;
1153
+ }
1154
+
1155
+ input.form-control, .form-control, select.form-control,
1156
+ #persona, #message, #summary-type {
1157
+ background: #f8f9fc !important;
1158
+ color: #1a1a1a !important;
1159
+ border: 1px solid #d0d7e2 !important;
1160
+ border-radius: 6px !important;
1161
+ }
1162
+
1163
+ input.form-control::placeholder, .form-control::placeholder,
1164
+ #persona::placeholder, #message::placeholder {
1165
+ color: #9aa5bc !important;
1166
+ }
1167
+
1168
+ .btn-gradient, .upload-button, .submit-btn, .bg-blue-600, .bg-green-600 {
1169
+ background: linear-gradient(90deg, #4f46e5, #3b82f6) !important;
1170
+ color: white !important;
1171
+ border: none !important;
1172
+ border-radius: 8px !important;
1173
+ font-weight: 600;
1174
+ transition: background 0.2s;
1175
+ }
1176
+
1177
+ .btn-gradient:hover, .upload-button:hover, .submit-btn:hover, .bg-blue-700, .bg-green-700 {
1178
+ background: linear-gradient(90deg, #4338ca, #2563eb) !important;
1179
+ }
1180
+
1181
+ .btn-danger, .btn-outline-danger {
1182
+ background-color: #d9534f !important;
1183
+ color: white !important;
1184
+ border: none !important;
1185
+ border-radius: 8px !important;
1186
+ font-weight: 600;
1187
+ }
1188
+
1189
+ .btn-danger:hover, .btn-outline-danger:hover {
1190
+ background-color: #c9302c !important;
1191
+ }
1192
+
1193
+ #file-list > li {
1194
+ border-radius: 10px !important;
1195
+ border: 1px solid #d0d7e2 !important;
1196
+ background: #fff !important;
1197
+ box-shadow: 0 1px 4px rgba(0,0,0,0.05) !important;
1198
+ padding: 1rem 1.5rem !important;
1199
+ margin-bottom: 1rem !important;
1200
+ transition: box-shadow 0.2s, border-color 0.2s;
1201
+ }
1202
+
1203
+ #file-list > li:hover {
1204
+ border-color: #a0b4e3 !important;
1205
+ background-color: #f0f4ff !important;
1206
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.06) !important;
1207
+ }
1208
+
1209
+ .loading-dots {
1210
+ display: inline-block;
1211
+ text-align: left;
1212
+ }
1213
+ .loading-dots span {
1214
+ display: inline-block;
1215
+ width: 8px;
1216
+ height: 8px;
1217
+ margin: 0 2px;
1218
+ background-color: #555;
1219
+ border-radius: 50%;
1220
+ animation: blink 1.4s infinite both;
1221
+ }
1222
+ .loading-dots span:nth-child(2) { animation-delay: 0.2s; }
1223
+ .loading-dots span:nth-child(3) { animation-delay: 0.4s; }
1224
+ @keyframes blink {
1225
+ 0%, 80%, 100% { opacity: 0; }
1226
+ 40% { opacity: 1; }
1227
+ }
1228
+
1229
+ #uploadModal .modal-content,
1230
+ .modal-content {
1231
+ background: #ffffff !important;
1232
+ color: #1a1a1a;
1233
+ border-radius: 12px !important;
1234
+ border: 1px solid #d0d7e2 !important;
1235
+ }
1236
+
1237
+ #uploadModal .drop-zone {
1238
+ border-radius: 10px !important;
1239
+ border: 2px dashed #d0d7e2 !important;
1240
+ background: #f8f9fc !important;
1241
+ color: #9aa5bc !important;
1242
+ transition: border-color 0.2s, color 0.2s;
1243
+ }
1244
+
1245
+ #uploadModal .drop-zone:hover, #uploadModal .drop-zone.border-blue-500 {
1246
+ border-color: #4f46e5 !important;
1247
+ color: #4f46e5 !important;
1248
+ }
1249
+
1250
+ #effort-path, #notebook-path, #file-path {
1251
+ font-size: 1rem;
1252
+ font-weight: 600;
1253
+ }
1254
+
1255
+ @media (max-width: 900px) {
1256
+ .w-1\/4, .w-3\/4 {
1257
+ width: 100% !important;
1258
+ display: block !important;
1259
+ }
1260
+ .flex {
1261
+ flex-direction: column !important;
1262
+ }
1263
+ }
1264
+
1265
+ /* Header Styles */
1266
+ .chat-header {
1267
+ background: #fff;
1268
+ border-radius: 12px;
1269
+ box-shadow: 0 1px 4px rgba(0,0,0,0.05);
1270
+ padding: 1.25rem 2rem;
1271
+ display: flex;
1272
+ justify-content: space-between;
1273
+ align-items: center;
1274
+ margin-bottom: 2rem;
1275
+ border: 1px solid #d0d7e2;
1276
+ }
1277
+ .chat-header-title {
1278
+ font-size: 1.75rem; /* Matches h2 in your other HTML files */
1279
+ background: linear-gradient(90deg, #4f46e5, #3b82f6);
1280
+ background-clip: text;
1281
+ -webkit-background-clip: text;
1282
+ -webkit-text-fill-color: transparent;
1283
+ }
1284
+ .chat-header .no-style {
1285
+ background: none;
1286
+ background-clip: initial;
1287
+ -webkit-background-clip: initial;
1288
+ -webkit-text-fill-color: initial;
1289
+ color: inherit;
1290
+ }
1291
+ </style>
1292
+ </head>
1293
+ <body class="font-sans">
1294
+
1295
+ <!-- Header -->
1296
+ <div class="chat-header container-title mb-4">
1297
+ <div class="d-flex align-items-center gap-3">
1298
+ <button
1299
+ onclick="window.location.href='/notebook/?effort_id={{ effort_id }}&user_id={{ user_id }}'"
1300
+ id = "backBtn"
1301
+ class="btn btn-outline-primary"
1302
+ aria-label="Go back"
1303
+ style="padding: 0.375rem 0.75rem;"
1304
+ >
1305
+ <i class="bi bi-arrow-left"></i>
1306
+ </button>
1307
+ <h2 class="fw-bold mb-0 chat-header-title">
1308
+ <span class="no-style">📚</span> Document Chat & Summarization
1309
+ </h2>
1310
+ </div>
1311
+ <div class="position-relative" style="min-width: 14rem;">
1312
+ <select id="zone-dropdown"
1313
+ class="form-control bg-white text-dark shadow-sm"
1314
+ style="padding-right: 2.5rem;">
1315
+ </select>
1316
+ <span class="position-absolute top-50 end-0 translate-middle-y pe-3">
1317
+ <svg class="w-4 h-4 text-secondary" fill="none" stroke="currentColor" stroke-width="2"
1318
+ viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" style="width: 1.25rem; height: 1.25rem;">
1319
+ <polyline points="6 9 12 15 18 9"></polyline>
1320
+ </svg>
1321
+ </span>
1322
+ </div>
1323
+ </div>
1324
+
1325
+ <!-- Directory Path Display -->
1326
+ <div class="max-w-7xl mx-auto mt-6 px-4 text-gray-700 text-sm font-medium select-none">
1327
+ <span id="effort-path" class="text-gray-800 font-semibold">{{ effort_name }}</span>
1328
+ <span class="mx-2 text-gray-500">→</span>
1329
+ <span id="notebook-path" class="text-gray-800 font-semibold">{{ notebook_name }}</span>
1330
+ <span class="mx-2 text-gray-500">→</span>
1331
+ <span id="file-path" class="text-blue-700 font-semibold">No File Selected</span>
1332
+ </div>
1333
+
1334
+ <!-- Main Layout -->
1335
+ <div class="flex max-w-7xl mx-auto mt-6 gap-6 px-4">
1336
+ <!-- LEFT: Sidebar -->
1337
+ <div class="w-1/4 bg-white rounded-2xl shadow-md p-4 h-[34rem] overflow-y-auto">
1338
+ <button class="upload-button w-full bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded mb-4"
1339
+ onclick="openModal()">Upload File(s)</button>
1340
+ <h3 class="text-lg font-semibold mb-2 text-gray-700">📂 Available Files</h3>
1341
+ <ul id="file-list" class="space-y-3 text-gray-700 text-sm">
1342
+ <!-- Files injected here -->
1343
+ </ul>
1344
+ </div>
1345
+
1346
+ <!-- RIGHT: Chat Panel -->
1347
+ <div class="w-3/4 bg-white rounded-2xl shadow-md p-6 flex flex-col">
1348
+ <div id="chat-box" class="overflow-y-scroll border rounded-lg p-4 bg-gray-50 space-y-3 mb-4">
1349
+ <!-- Chat messages will appear here -->
1350
+ </div>
1351
+
1352
+ <div class="flex flex-col sm:flex-row items-stretch sm:items-center gap-2 mt-auto">
1353
+ <input id="persona" type="text" placeholder="Enter persona..." value="You are a helpful assistant"
1354
+ class="px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500" />
1355
+
1356
+ <input id="message" type="text" placeholder="Type your message..."
1357
+ class="flex-grow px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" />
1358
+
1359
+ <button onclick="sendMessage()"
1360
+ class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg transition">
1361
+ Send
1362
+ </button>
1363
+
1364
+ <select id="summary-type"
1365
+ class="px-3 py-2 border rounded-lg bg-white focus:outline-none focus:ring-2 focus:ring-green-500">
1366
+ <option value="brief">Brief</option>
1367
+ <option value="detailed">Detailed</option>
1368
+ </select>
1369
+
1370
+ <button onclick="summarize()"
1371
+ class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-lg transition">
1372
+ Summarize
1373
+ </button>
1374
+ </div>
1375
+ </div>
1376
+ </div>
1377
+
1378
+ <!-- Upload Modal -->
1379
+ <div id="uploadModal" class="modal fixed z-50 inset-0 hidden bg-black bg-opacity-50 flex justify-center items-center">
1380
+ <div class="modal-content bg-white p-6 rounded-xl w-[90%] max-w-md relative">
1381
+ <span class="close absolute top-3 right-4 text-xl cursor-pointer" onclick="closeModal()">&times;</span>
1382
+ <h2 class="text-xl font-bold text-gray-800">NotebookLM</h2>
1383
+ <p class="text-sm text-gray-600 mt-1">Add sources (PDF only)</p>
1384
+ <div id="dropZone" class="drop-zone mt-4 border-2 border-dashed border-gray-300 p-6 rounded-lg text-gray-500 text-sm text-center cursor-pointer hover:border-blue-500 hover:text-blue-500"
1385
+ onclick="triggerFileInput()">Drag & drop or <u>choose files</u><br><small>Supported: PDF</small></div>
1386
+ <input type="file" id="fileInput" accept=".pdf" multiple class="hidden" />
1387
+ <div id="selectedFilesList" class="mt-4 text-gray-700 text-sm space-y-1 max-h-40 overflow-y-auto">
1388
+ <!-- File names shown here -->
1389
+ </div>
1390
+ <div class="flex justify-center mt-4">
1391
+ <button class="submit-btn bg-blue-600 text-white px-4 py-2 rounded" onclick="submitFile()">Upload</button>
1392
+ </div>
1393
+ </div>
1394
+ </div>
1395
+
1396
+ <!-- Delete File Confirmation Modal -->
1397
+ <div class="modal fade" id="deleteFileModal" tabindex="-1" aria-labelledby="deleteFileModalLabel" aria-hidden="true">
1398
+ <div class="modal-dialog">
1399
+ <div class="modal-content">
1400
+ <div class="modal-header">
1401
+ <h5 class="modal-title" id="deleteFileModalLabel">Confirm Delete</h5>
1402
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
1403
+ </div>
1404
+ <div class="modal-body">
1405
+ Are you sure you want to delete <strong id="deleteFileName"></strong>?
1406
+ </div>
1407
+ <div class="modal-footer">
1408
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
1409
+ <button type="button" class="btn btn-danger" onclick="confirmDeleteFile()">Delete</button>
1410
+ </div>
1411
+ </div>
1412
+ </div>
1413
+ </div>
1414
+ <!-- FEEDBACK MODAL (Bootstrap) -->
1415
+ <div class="modal fade" id="feedbackModal" tabindex="-1" aria-labelledby="feedbackModalLabel" aria-hidden="true">
1416
+ <div class="modal-dialog" role="document">
1417
+ <div class="modal-content" style="background-color: white; color: black;">
1418
+ <div class="modal-header">
1419
+ <h5 class="modal-title" id="feedbackModalLabel">Rate Your Experience</h5>
1420
+ <!-- Proper Bootstrap “close” button -->
1421
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
1422
+ </div>
1423
+ <div class="modal-body">
1424
+ <label for="feedbackLevel">Rating (optional):</label>
1425
+ <select id="feedbackLevel" class="form-control">
1426
+ <option value="">-- Select --</option>
1427
+ <option value="1">Very Bad</option>
1428
+ <option value="2">Bad</option>
1429
+ <option value="3">Neutral</option>
1430
+ <option value="4">Good</option>
1431
+ <option value="5">Very Good</option>
1432
+ </select>
1433
+
1434
+ <label for="comment" class="mt-2">Comments (optional):</label>
1435
+ <textarea id="comment" class="form-control" rows="3" placeholder="Write your feedback here..."></textarea>
1436
+ </div id ="updateNotice" class = "text-secondary mt-2" style="display:none;font-size:0.9em;">
1437
+ (Your Previously Submitted Feedback. You can update it needed.)
1438
+ </div>
1439
+ </div>
1440
+ <div class="modal-footer">
1441
+ <button id="submitFeedback" class="btn btn-primary">Submit</button>
1442
+ <button id="skipFeedback" type="button" class="btn btn-secondary">Skip</button>
1443
+ </div>
1444
+ </div>
1445
+ </div>
1446
+ </div>
1447
+
1448
+ <script>
1449
+ const userId = "{{ user_id }}";
1450
+ const effortId = "{{ effort_id }}";
1451
+ const notebookId = "{{ notebook_id }}"
1452
+ const effortName = "{{ effort_name }}";
1453
+ const notebookName = "{{ notebook_name}}"
1454
+ const preloadedFiles = {{ files | tojson }};
1455
+ const preloadedZones = {{ zones | tojson }};
1456
+ const globalZones = {{ global_ezones | tojson }};
1457
+ const chatHistory = {{ chat_history | tojson }};
1458
+ const chatBox = document.getElementById("chat-box");
1459
+ const zoneDropdown = document.getElementById("zone-dropdown");
1460
+ const fileList = document.getElementById("file-list");
1461
+ const modal = document.getElementById("uploadModal");
1462
+ const fileInput = document.getElementById("fileInput");
1463
+
1464
+ //Feedback modal -----
1465
+ const backButton = document.getElementById("backBtn");
1466
+ const feedbackModalEl = document.getElementById("feedbackModal");
1467
+ const bootstrapFeedbackModal = new bootstrap.Modal(feedbackModalEl);
1468
+
1469
+ fileInput.addEventListener("change", () => {
1470
+ const list = document.getElementById("selectedFilesList");
1471
+ list.innerHTML = "";
1472
+
1473
+ const files = fileInput.files;
1474
+ if (!files.length) {
1475
+ list.textContent = "No files selected.";
1476
+ return;
1477
+ }
1478
+
1479
+ for (let file of files) {
1480
+ const item = document.createElement("div");
1481
+ item.className = "flex items-center gap-2";
1482
+ item.innerHTML = `📄 <span class="truncate">${file.name}</span>`;
1483
+ list.appendChild(item);
1484
+ }
1485
+ });
1486
+
1487
+ let currentZoneId = null;
1488
+ let currentFileUri = "";
1489
+ let selectedFileElement = null;
1490
+
1491
+ function appendMessage(sender, message, color, alignRight = false) {
1492
+ const wrapper = document.createElement("div");
1493
+ wrapper.className = alignRight ? "flex justify-end" : "flex justify-start";
1494
+
1495
+ const bubble = document.createElement("div");
1496
+ bubble.className = `max-w-xl px-4 py-2 rounded-lg ${color} text-white`;
1497
+
1498
+ // Use <pre> tag to preserve formatting in the message
1499
+ bubble.innerHTML = `<strong>${sender}:</strong><br><pre class="whitespace-pre-wrap break-words">${message}</pre>`;
1500
+
1501
+ wrapper.appendChild(bubble);
1502
+ chatBox.appendChild(wrapper);
1503
+ chatBox.scrollTop = chatBox.scrollHeight;
1504
+ }
1505
+
1506
+
1507
+
1508
+ function loadZones() {
1509
+ zoneDropdown.innerHTML = "";
1510
+ const defaultOpt = document.createElement("option");
1511
+ defaultOpt.disabled = true;
1512
+ defaultOpt.selected = true;
1513
+ defaultOpt.textContent = "Select a Configuration";
1514
+ zoneDropdown.appendChild(defaultOpt);
1515
+
1516
+ try {
1517
+ const addedZoneIds = new Set();
1518
+
1519
+ preloadedZones.forEach(zone => {
1520
+ if (!addedZoneIds.has(zone.id)) {
1521
+ const opt = document.createElement("option");
1522
+ opt.value = zone.id;
1523
+ opt.textContent = zone.name;
1524
+ zoneDropdown.appendChild(opt);
1525
+ addedZoneIds.add(zone.id);
1526
+ }
1527
+ });
1528
+
1529
+ globalZones.forEach(zone => {
1530
+ if (!addedZoneIds.has(zone.id)) {
1531
+ const opt = document.createElement("option");
1532
+ opt.value = zone.id;
1533
+ opt.textContent = zone.name + " (Global)";
1534
+ zoneDropdown.appendChild(opt);
1535
+ addedZoneIds.add(zone.id);
1536
+ }
1537
+ });
1538
+
1539
+ const divider = document.createElement("option");
1540
+ divider.disabled = true;
1541
+ divider.textContent = "──────────";
1542
+ zoneDropdown.appendChild(divider);
1543
+ } catch (err) {
1544
+ console.error("Error loading zones:", err);
1545
+ }
1546
+
1547
+ const manage = document.createElement("option");
1548
+ manage.value = "manage";
1549
+ manage.textContent = "➕ Manage Configurations";
1550
+ zoneDropdown.appendChild(manage);
1551
+
1552
+ zoneDropdown.addEventListener("change", (e) => {
1553
+ if (e.target.value === "manage") {
1554
+ const form = document.createElement("form");
1555
+ form.method = "POST";
1556
+ form.action = "/chat_feature/manage_zones";
1557
+
1558
+ const userInput = document.createElement("input");
1559
+ userInput.type = "hidden";
1560
+ userInput.name = "user_id";
1561
+ userInput.value = userId;
1562
+ form.appendChild(userInput);
1563
+
1564
+ const effortInput = document.createElement("input");
1565
+ effortInput.type = "hidden";
1566
+ effortInput.name = "effort_id";
1567
+ effortInput.value = effortId;
1568
+ form.appendChild(effortInput);
1569
+
1570
+ const notebookInput = document.createElement("input");
1571
+ notebookInput.type = "hidden";
1572
+ notebookInput.name = "notebook_id";
1573
+ notebookInput.value = notebookId;
1574
+ form.appendChild(notebookInput);
1575
+
1576
+ document.body.appendChild(form);
1577
+ form.submit();
1578
+ } else {
1579
+ currentZoneId = parseInt(e.target.value);
1580
+ }
1581
+ });
1582
+ }
1583
+
1584
+ function loadFiles() {
1585
+ const files = preloadedFiles || [];
1586
+ fileList.innerHTML = "";
1587
+
1588
+ files.forEach(file => {
1589
+ const li = document.createElement("li");
1590
+ li.className =
1591
+ "flex items-center gap-3 p-4 rounded-2xl border border-gray-200 bg-white hover:bg-blue-50 hover:shadow-lg transition shadow group";
1592
+
1593
+ const fileIcon = document.createElement("div");
1594
+ fileIcon.className = "flex-shrink-0 text-blue-600 text-lg";
1595
+ fileIcon.textContent = "📄";
1596
+
1597
+ const fileName = document.createElement("div");
1598
+ fileName.className = "flex-1 min-w-0 cursor-pointer";
1599
+ fileName.innerHTML = `
1600
+ <p class="font-medium truncate group-hover:whitespace-normal group-hover:break-all" title="${file.name}">
1601
+ ${file.name}
1602
+ </p>
1603
+ `;
1604
+ fileName.onclick = () => {
1605
+ currentFileUri = file.uri;
1606
+ appendMessage("System", `Selected file: ${file.name}`, "bg-purple-600");
1607
+
1608
+ document.getElementById("notebook-path").textContent = notebookName;
1609
+ document.getElementById("effort-path").textContent = effortName;
1610
+ document.getElementById("file-path").textContent = file.name;
1611
+
1612
+ if (selectedFileElement) {
1613
+ selectedFileElement.classList.remove("border-blue-500", "bg-blue-100", "ring", "ring-blue-300", "shadow-lg", "transform", "scale-105");
1614
+ }
1615
+
1616
+ li.classList.add("border-blue-500", "bg-blue-100", "ring", "ring-blue-300", "shadow-lg", "transform", "scale-105");
1617
+ selectedFileElement = li;
1618
+ };
1619
+
1620
+ const deleteButton = document.createElement("button");
1621
+ deleteButton.className = "btn btn-sm btn-outline-danger";
1622
+ deleteButton.setAttribute("data-bs-toggle", "modal");
1623
+ deleteButton.setAttribute("data-bs-target", "#deleteFileModal");
1624
+ deleteButton.innerHTML = `<i class="bi bi-trash"></i>`;
1625
+ deleteButton.onclick = (e) => {
1626
+ e.stopPropagation();
1627
+ setDeleteFile(file.uri, file.name);
1628
+ };
1629
+
1630
+ li.appendChild(fileIcon);
1631
+ li.appendChild(fileName);
1632
+ li.appendChild(deleteButton);
1633
+
1634
+ fileList.appendChild(li);
1635
+ });
1636
+ }
1637
+
1638
+
1639
+ async function sendMessage() {
1640
+ const message = document.getElementById("message").value;
1641
+ const persona = document.getElementById("persona").value;
1642
+ if (!message.trim() || !currentFileUri) {
1643
+ alert("Please select a file and type a message.");
1644
+ return;
1645
+ }
1646
+ if (!currentZoneId) {
1647
+ alert("Please select a zone before sending a message.");
1648
+ return;
1649
+ }
1650
+
1651
+ appendMessage("You", message, "bg-blue-500", true); // Right aligned
1652
+
1653
+ const loadingId = "loading-msg";
1654
+ const loadingWrapper = document.createElement("div");
1655
+ loadingWrapper.id = loadingId;
1656
+ loadingWrapper.className = "flex justify-start";
1657
+
1658
+ const loadingBubble = document.createElement("div");
1659
+ loadingBubble.className = "max-w-xl px-4 py-2 rounded-lg bg-gray-300 text-gray-800";
1660
+ loadingBubble.innerHTML = `<strong>Assistant:</strong> <span class="loading-dots"><span></span><span></span><span></span></span>`;
1661
+ loadingWrapper.appendChild(loadingBubble);
1662
+ chatBox.appendChild(loadingWrapper);
1663
+ chatBox.scrollTop = chatBox.scrollHeight;
1664
+
1665
+
1666
+ const res = await fetch("/chat_feature/send_message", {
1667
+ method: "POST",
1668
+ headers: { "Content-Type": "application/json" },
1669
+ body: JSON.stringify({
1670
+ message,
1671
+ persona,
1672
+ file_uri: currentFileUri,
1673
+ experiment_zone_id: currentZoneId,
1674
+ effortId: effortId,
1675
+ notebookId: notebookId
1676
+ })
1677
+ });
1678
+
1679
+ const data = await res.json();
1680
+ document.getElementById(loadingId)?.remove();
1681
+ appendMessage("Assistant", data.message || "No response", "bg-gray-700");
1682
+ document.getElementById("message").value = "";
1683
+ }
1684
+
1685
+ async function summarize() {
1686
+ const summaryType = document.getElementById("summary-type").value;
1687
+ if (!currentFileUri) {
1688
+ alert("Please select a file and summarize.");
1689
+ return;
1690
+ }
1691
+
1692
+ if (!currentZoneId) {
1693
+ alert("Please select a zone before summarizing.");
1694
+ return;
1695
+ }
1696
+
1697
+ appendMessage("You", `Summarize (${summaryType})`, "bg-green-500", true); // Right aligned
1698
+
1699
+ const loadingId = "loading-msg";
1700
+ const loadingWrapper = document.createElement("div");
1701
+ loadingWrapper.id = loadingId;
1702
+ loadingWrapper.className = "flex justify-start";
1703
+
1704
+ const loadingBubble = document.createElement("div");
1705
+ loadingBubble.className = "max-w-xl px-4 py-2 rounded-lg bg-gray-300 text-gray-800";
1706
+ loadingBubble.innerHTML = `<strong>Assistant:</strong> <span class="loading-dots"><span></span><span></span><span></span></span>`;
1707
+ loadingWrapper.appendChild(loadingBubble);
1708
+ chatBox.appendChild(loadingWrapper);
1709
+ chatBox.scrollTop = chatBox.scrollHeight;
1710
+
1711
+
1712
+ const res = await fetch("/chat_feature/summarize", {
1713
+ method: "POST",
1714
+ headers: { "Content-Type": "application/json" },
1715
+ body: JSON.stringify({
1716
+ summary_type: summaryType,
1717
+ file_uri: currentFileUri,
1718
+ experiment_zone_id: currentZoneId,
1719
+ effortId: effortId,
1720
+ notebookId: notebookId
1721
+ })
1722
+ });
1723
+
1724
+ const data = await res.json();
1725
+ document.getElementById(loadingId)?.remove();
1726
+ appendMessage("Summary", data.message || "No summary", "bg-gray-700");
1727
+ }
1728
+
1729
+ function openModal() { modal.classList.remove("hidden"); }
1730
+ function closeModal() { modal.classList.add("hidden"); fileInput.value = ""; }
1731
+ function triggerFileInput() { fileInput.click(); }
1732
+
1733
+ async function submitFile() {
1734
+ const files = fileInput.files;
1735
+ if (!files.length) return alert("Choose at least one file.");
1736
+
1737
+ const formData = new FormData();
1738
+ for (let file of files) {
1739
+ if (file.type !== "application/pdf") {
1740
+ alert(`Invalid file type: ${file.name}`);
1741
+ return;
1742
+ }
1743
+ formData.append("files", file);
1744
+ }
1745
+
1746
+ formData.append("effort_id", effortId);
1747
+
1748
+ try {
1749
+ const res = await fetch("/chat_feature/upload_file", {
1750
+ method: "POST",
1751
+ body: formData
1752
+ });
1753
+
1754
+ const contentType = res.headers.get("content-type") || "";
1755
+ let responseData = contentType.includes("application/json") ? await res.json() : await res.text();
1756
+
1757
+ if (!res.ok) {
1758
+ const errorMessage = typeof responseData === "string"
1759
+ ? responseData
1760
+ : (responseData.error || JSON.stringify(responseData));
1761
+ alert(`Upload failed: ${errorMessage}`);
1762
+ return;
1763
+ }
1764
+
1765
+ alert("Upload successful!");
1766
+ location.reload(); // Refresh to get updated file list from server
1767
+ } catch (err) {
1768
+ alert("Error: " + err.message);
1769
+ }
1770
+ }
1771
+
1772
+ window.onclick = function (event) {
1773
+ if (event.target == modal) closeModal();
1774
+ }
1775
+
1776
+ let deleteFileUriToConfirm = "";
1777
+ let deleteFileName = ""
1778
+
1779
+ function setDeleteFile(fileUri, fileName) {
1780
+ deleteFileUriToConfirm = fileUri;
1781
+ deleteFileName = fileName
1782
+ document.getElementById("deleteFileName").textContent = fileName;
1783
+ }
1784
+
1785
+ async function confirmDeleteFile() {
1786
+ try {
1787
+ const res = await fetch("/chat_feature/delete_file", {
1788
+ method: "POST",
1789
+ headers: { "Content-Type": "application/json" },
1790
+ body: JSON.stringify({
1791
+ file_uri: deleteFileUriToConfirm,
1792
+ effort_id: effortId,
1793
+ filename: deleteFileName
1794
+ })
1795
+ });
1796
+
1797
+ if (!res.ok) {
1798
+ const text = await res.text();
1799
+ alert("Failed to delete: " + text);
1800
+ } else {
1801
+ const modal = bootstrap.Modal.getInstance(document.getElementById("deleteFileModal"));
1802
+ modal.hide();
1803
+ alert("File deleted.");
1804
+ location.reload();
1805
+ }
1806
+ } catch (err) {
1807
+ alert("Error deleting file: " + err.message);
1808
+ }
1809
+ }
1810
+
1811
+ const dropZone = document.querySelector(".drop-zone");
1812
+
1813
+ dropZone.addEventListener("dragover", (e) => {
1814
+ e.preventDefault();
1815
+ dropZone.classList.add("border-blue-500", "text-blue-500");
1816
+ });
1817
+
1818
+ dropZone.addEventListener("dragleave", () => {
1819
+ dropZone.classList.remove("border-blue-500", "text-blue-500");
1820
+ });
1821
+
1822
+ dropZone.addEventListener("drop", (e) => {
1823
+ e.preventDefault();
1824
+ dropZone.classList.remove("border-blue-500", "text-blue-500");
1825
+
1826
+ const files = e.dataTransfer.files;
1827
+ fileInput.files = files;
1828
+
1829
+ // Optional: Display file names immediately
1830
+ const list = document.getElementById("selectedFilesList");
1831
+ list.innerHTML = "";
1832
+ for (const file of files) {
1833
+ const p = document.createElement("p");
1834
+ p.textContent = file.name;
1835
+ list.appendChild(p);
1836
+ }
1837
+ });
1838
+
1839
+ window.onload = function () {
1840
+
1841
+ loadFiles();
1842
+ loadZones();
1843
+
1844
+ let historyFilename = ""
1845
+
1846
+ if (chatHistory && chatHistory.length > 0) {
1847
+ chatHistory.forEach(entry => {
1848
+
1849
+ if (entry.filename && (entry.filename !== historyFilename || historyFilename === "")) {
1850
+ historyFilename = entry.filename;
1851
+ appendMessage("System", `Selected file: ${historyFilename}`, "bg-purple-600");
1852
+ }
1853
+
1854
+ // User message
1855
+ const userMessage = entry.question;
1856
+ appendMessage("You", userMessage, "bg-blue-500", true);
1857
+ const personaMessage = entry.persona;
1858
+ appendMessage("Persona", personaMessage, "bg-blue-500", true);
1859
+ // Assistant response
1860
+ const botMessage = entry.response;
1861
+ appendMessage("Assistant", botMessage, "bg-gray-700", false);
1862
+ });
1863
+ }
1864
+ };
1865
+
1866
+ //feedback Modal POP-UP on back Button
1867
+ backButton.addEventListener("click", async function (event) {
1868
+ event.preventDefault();
1869
+
1870
+ if(!currentZoneId){
1871
+ console.log("no configuration selected, going back directly")
1872
+ window.location.href = `/notebook/?effort_id=${effortId}&user_id=${userId}`;
1873
+ return;
1874
+ }
1875
+
1876
+ console.log("Debug BackBtn:", {userId, notebookId, currentZoneId, effortId});
1877
+ try {
1878
+ const response = await fetch(`/chat_feature/user_feedback_eligibility?user_id=${userId}&notebook_id=${notebookId}&zone_id=${currentZoneId}`);
1879
+ const data = await response.json();
1880
+
1881
+ if (data.eligible_for_feedback) {
1882
+ if(data.has_feedback){
1883
+ document.getElementById("feedbackLevel").value = data.rating ||"";
1884
+ document.getElementById("comment").value = data.comment ||"";
1885
+ document.getElementById("updateNotice").style.display = "block";
1886
+
1887
+ }
1888
+ else{
1889
+ document.getElementById("feedbackLevel").value = data.rating ||"";
1890
+ document.getElementById("comment").value = data.comment ||"";
1891
+ document.getElementById("updateNotice").style.display = "None";
1892
+
1893
+ }
1894
+ bootstrapFeedbackModal.show();
1895
+ } else {
1896
+ window.location.href = `/notebook/?effort_id=${effortId}&user_id=${userId}`;
1897
+ }
1898
+ } catch (error) {
1899
+ console.error('Error checking eligibility:', error);
1900
+ window.location.href = `/notebook/?effort_id=${effortId}&user_id=${userId}`;
1901
+ }
1902
+ });
1903
+ // Feedback Submit
1904
+ document.getElementById("submitFeedback").addEventListener("click", async function () {
1905
+ const rating = document.getElementById("feedbackLevel").value;
1906
+ const comment = document.getElementById("comment").value;
1907
+
1908
+ const payload = {
1909
+ user_id: userId,
1910
+ notebook_id: parseInt(notebookId),
1911
+ effort_id: parseInt(effortId),
1912
+ zone_id: parseInt(currentZoneId),
1913
+ rating: rating ? parseInt(rating) : null,
1914
+ comment: comment
1915
+ };
1916
+
1917
+ try {
1918
+ const response = await fetch("/feedback", {
1919
+ method: "POST",
1920
+ headers: { "Content-Type": "application/json" },
1921
+ body: JSON.stringify(payload)
1922
+ });
1923
+
1924
+ if (response.ok) {
1925
+ bootstrapFeedbackModal.hide();
1926
+ window.location.href = `/notebook/?effort_id=${effortId}&user_id=${userId}`;
1927
+ } else {
1928
+ alert("Error submitting feedback");
1929
+ }
1930
+ } catch (error) {
1931
+ console.error("Error submitting feedback:", error);
1932
+ alert("Error submitting feedback");
1933
+ }
1934
+ });
1935
+
1936
+ // Skip feedback
1937
+ document.getElementById("skipFeedback").addEventListener("click", function () {
1938
+ bootstrapFeedbackModal.hide();
1939
+ window.location.href = `/notebook/?effort_id=${effortId}&user_id=${userId}`;
1940
+ });
1941
+ </script>
1942
+ </body>
1943
+ </html>