Spaces:
Sleeping
Sleeping
Update static/test.js
Browse files- static/test.js +334 -255
static/test.js
CHANGED
@@ -213,7 +213,6 @@ function showSection(sectionId) {
|
|
213 |
|
214 |
|
215 |
// Function to show the selected section and hide the others
|
216 |
-
// Main navigation and shared utility functions
|
217 |
function showSection(sectionId) {
|
218 |
// Show the selected section (in case it was hidden)
|
219 |
const activeSection = document.getElementById(sectionId);
|
@@ -227,7 +226,6 @@ function showSection(sectionId) {
|
|
227 |
}
|
228 |
}
|
229 |
|
230 |
-
|
231 |
function showCards() {
|
232 |
// Show cards section
|
233 |
document.querySelector('.cards').style.display = 'grid'; // or 'flex' depending on your layout
|
@@ -242,8 +240,6 @@ function showCards() {
|
|
242 |
window.scrollTo({ top: 0, behavior: 'smooth' });
|
243 |
}
|
244 |
|
245 |
-
|
246 |
-
|
247 |
document.addEventListener('DOMContentLoaded', () => {
|
248 |
// Set up card click handlers
|
249 |
document.querySelectorAll('.card').forEach(card => {
|
@@ -258,11 +254,62 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
258 |
sections.forEach(section => {
|
259 |
section.style.display = 'none';
|
260 |
});
|
261 |
-
});
|
262 |
|
263 |
// Show first section by default
|
264 |
showSection('document-image-analysis');
|
265 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
266 |
// Document Analysis Section Setup
|
267 |
const summarizeDropArea = document.getElementById('upload-area');
|
268 |
const summarizeFileInput = document.getElementById('file-input');
|
@@ -273,17 +320,20 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
273 |
const summarizeError = document.getElementById('error-message');
|
274 |
const languageSelect = document.getElementById('language-select');
|
275 |
|
|
|
|
|
|
|
276 |
// Set up drag and drop for file upload
|
277 |
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
278 |
summarizeDropArea.addEventListener(eventName, preventDefaults, false);
|
279 |
});
|
280 |
|
281 |
['dragenter', 'dragover'].forEach(eventName => {
|
282 |
-
summarizeDropArea.addEventListener(eventName, highlight, false);
|
283 |
});
|
284 |
|
285 |
['dragleave', 'drop'].forEach(eventName => {
|
286 |
-
summarizeDropArea.addEventListener(eventName, unhighlight, false);
|
287 |
});
|
288 |
|
289 |
summarizeDropArea.addEventListener('drop', handleDrop, false);
|
@@ -291,6 +341,53 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
291 |
summarizeFileInput.addEventListener('change', handleFileSelect);
|
292 |
textInput.addEventListener('input', validateInputs);
|
293 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
294 |
// Summarize button click handler
|
295 |
summarizeBtn.addEventListener('click', async () => {
|
296 |
const file = summarizeFileInput.files[0];
|
@@ -303,17 +400,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
303 |
}
|
304 |
|
305 |
try {
|
306 |
-
|
307 |
-
setLoading(true);
|
308 |
hideError(summarizeError);
|
309 |
summarizeResult.textContent = '';
|
310 |
|
311 |
const formData = new FormData();
|
312 |
-
if (file)
|
313 |
-
|
314 |
-
} else {
|
315 |
-
formData.append('text', text);
|
316 |
-
}
|
317 |
formData.append('language', language);
|
318 |
|
319 |
const response = await fetch('/summarize', {
|
@@ -332,45 +425,75 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
332 |
showError(summarizeError, error.message);
|
333 |
console.error("Summarization error:", error);
|
334 |
} finally {
|
335 |
-
setLoading(false);
|
336 |
}
|
337 |
});
|
338 |
|
339 |
-
//
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
344 |
|
345 |
-
|
346 |
-
|
|
|
|
|
|
|
|
|
|
|
347 |
}
|
348 |
|
349 |
-
|
350 |
-
|
|
|
351 |
}
|
352 |
|
353 |
-
function
|
354 |
const dt = e.dataTransfer;
|
355 |
const files = dt.files;
|
356 |
-
|
357 |
}
|
358 |
|
359 |
-
function
|
360 |
const files = e.target.files;
|
361 |
if (files.length) {
|
362 |
-
|
363 |
}
|
364 |
}
|
365 |
|
366 |
-
function
|
367 |
const file = files[0];
|
368 |
-
|
369 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
370 |
return;
|
371 |
}
|
372 |
|
373 |
-
|
374 |
<div class="file-info">
|
375 |
<span class="file-icon">${getFileIcon(file)}</span>
|
376 |
<div>
|
@@ -379,263 +502,219 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
379 |
</div>
|
380 |
</div>
|
381 |
`;
|
382 |
-
|
383 |
}
|
384 |
|
385 |
-
function
|
386 |
-
const hasFile =
|
387 |
-
const
|
388 |
-
|
389 |
}
|
390 |
|
391 |
-
function
|
392 |
-
const
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
file.name.endsWith('.pdf') ||
|
401 |
-
file.name.endsWith('.docx') ||
|
402 |
-
file.name.endsWith('.txt');
|
403 |
-
}
|
404 |
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
return '📁';
|
410 |
-
}
|
411 |
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
|
|
419 |
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
|
|
|
|
427 |
|
428 |
-
|
429 |
-
|
430 |
-
|
|
|
|
|
|
|
|
|
|
|
431 |
}
|
432 |
|
433 |
-
function
|
434 |
-
|
435 |
}
|
436 |
-
});
|
437 |
-
|
438 |
-
|
439 |
-
// Add this after your existing summarization code, inside the DOMContentLoaded event listener
|
440 |
-
// Add this after your existing summarization code, inside the DOMContentLoaded event listener
|
441 |
-
|
442 |
-
// Question Answering Section Setup
|
443 |
-
const qaDropArea = document.getElementById('qa-upload-area');
|
444 |
-
const qaFileInput = document.getElementById('qa-file-input');
|
445 |
-
const qaPreview = document.getElementById('qa-file-preview');
|
446 |
-
const qaQuestionInput = document.getElementById('qa-question');
|
447 |
-
const qaSubmitBtn = document.getElementById('qa-submit-btn');
|
448 |
-
const qaResult = document.getElementById('qa-answer');
|
449 |
-
const qaError = document.getElementById('qa-error-message');
|
450 |
-
const qaLanguageSelect = document.getElementById('qa-language');
|
451 |
-
|
452 |
-
// Define QA-specific versions of the drag-drop functions
|
453 |
-
function qaPreventDefaults(e) {
|
454 |
-
e.preventDefault();
|
455 |
-
e.stopPropagation();
|
456 |
-
}
|
457 |
-
|
458 |
-
function qaHighlight() {
|
459 |
-
qaDropArea.classList.add('highlight');
|
460 |
-
}
|
461 |
-
|
462 |
-
function qaUnhighlight() {
|
463 |
-
qaDropArea.classList.remove('highlight');
|
464 |
-
}
|
465 |
-
|
466 |
-
// Debug: Verify elements exist
|
467 |
-
console.log('QA Elements:', {
|
468 |
-
qaDropArea, qaFileInput, qaPreview, qaQuestionInput,
|
469 |
-
qaSubmitBtn, qaResult, qaError, qaLanguageSelect
|
470 |
-
});
|
471 |
-
|
472 |
-
// Setup event listeners
|
473 |
-
if (qaDropArea && qaFileInput) {
|
474 |
-
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
475 |
-
qaDropArea.addEventListener(eventName, qaPreventDefaults, false);
|
476 |
-
});
|
477 |
-
|
478 |
-
['dragenter', 'dragover'].forEach(eventName => {
|
479 |
-
qaDropArea.addEventListener(eventName, qaHighlight, false);
|
480 |
-
});
|
481 |
|
482 |
-
|
483 |
-
|
484 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
485 |
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
}
|
490 |
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
}
|
495 |
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
const file = qaFileInput.files[0];
|
500 |
-
const question = qaQuestionInput.value.trim();
|
501 |
-
const language = qaLanguageSelect.value;
|
502 |
-
|
503 |
-
console.log('Submission data:', { file, question, language });
|
504 |
-
|
505 |
-
if (!file) {
|
506 |
-
showError(qaError, "Please upload a document first");
|
507 |
-
return;
|
508 |
}
|
509 |
|
510 |
-
if (
|
511 |
-
|
512 |
-
return;
|
513 |
}
|
514 |
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
async function submitQARequest(file, question, language) {
|
519 |
-
try {
|
520 |
-
setQALoading(true);
|
521 |
-
hideError(qaError);
|
522 |
-
|
523 |
-
const formData = new FormData();
|
524 |
-
formData.append('file', file);
|
525 |
-
formData.append('question', question);
|
526 |
-
formData.append('language', language);
|
527 |
-
|
528 |
-
console.log('Sending request to /qa...');
|
529 |
-
const response = await fetch('/qa', {
|
530 |
-
method: 'POST',
|
531 |
-
body: formData
|
532 |
-
});
|
533 |
-
|
534 |
-
console.log('Response status:', response.status);
|
535 |
-
|
536 |
-
if (!response.ok) {
|
537 |
-
const error = await response.json();
|
538 |
-
throw new Error(error.detail || "Failed to get answer");
|
539 |
-
}
|
540 |
-
|
541 |
-
const result = await response.json();
|
542 |
-
console.log('API Response:', result);
|
543 |
-
|
544 |
-
displayQAResult(result);
|
545 |
-
} catch (error) {
|
546 |
-
console.error("QA Error:", error);
|
547 |
-
showError(qaError, error.message);
|
548 |
-
} finally {
|
549 |
-
setQALoading(false);
|
550 |
}
|
551 |
-
}
|
552 |
-
|
553 |
-
// QA-specific helper functions
|
554 |
-
function handleQADrop(e) {
|
555 |
-
const dt = e.dataTransfer;
|
556 |
-
const files = dt.files;
|
557 |
-
handleQAFiles(files);
|
558 |
-
}
|
559 |
|
560 |
-
function
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
}
|
565 |
-
}
|
566 |
|
567 |
-
function
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
}
|
573 |
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
580 |
</div>
|
581 |
-
|
582 |
-
|
583 |
-
validateQAInputs();
|
584 |
-
}
|
585 |
|
586 |
-
function
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
|
|
|
|
|
|
|
|
591 |
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
const spinner = qaSubmitBtn.querySelector('.spinner');
|
597 |
-
spinner.classList.toggle('hidden', !isLoading);
|
598 |
-
}
|
599 |
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
}
|
609 |
|
610 |
-
|
611 |
-
|
612 |
-
|
|
|
613 |
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
file.name.endsWith('.txt');
|
624 |
-
}
|
625 |
|
626 |
-
function
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
631 |
|
632 |
-
function
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
637 |
-
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
638 |
-
}
|
639 |
|
|
|
|
|
|
|
640 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
641 |
|
|
|
213 |
|
214 |
|
215 |
// Function to show the selected section and hide the others
|
|
|
216 |
function showSection(sectionId) {
|
217 |
// Show the selected section (in case it was hidden)
|
218 |
const activeSection = document.getElementById(sectionId);
|
|
|
226 |
}
|
227 |
}
|
228 |
|
|
|
229 |
function showCards() {
|
230 |
// Show cards section
|
231 |
document.querySelector('.cards').style.display = 'grid'; // or 'flex' depending on your layout
|
|
|
240 |
window.scrollTo({ top: 0, behavior: 'smooth' });
|
241 |
}
|
242 |
|
|
|
|
|
243 |
document.addEventListener('DOMContentLoaded', () => {
|
244 |
// Set up card click handlers
|
245 |
document.querySelectorAll('.card').forEach(card => {
|
|
|
254 |
sections.forEach(section => {
|
255 |
section.style.display = 'none';
|
256 |
});
|
|
|
257 |
|
258 |
// Show first section by default
|
259 |
showSection('document-image-analysis');
|
260 |
|
261 |
+
// Helper functions
|
262 |
+
function preventDefaults(e) {
|
263 |
+
e.preventDefault();
|
264 |
+
e.stopPropagation();
|
265 |
+
}
|
266 |
+
|
267 |
+
function highlight(element) {
|
268 |
+
element.classList.add('highlight');
|
269 |
+
}
|
270 |
+
|
271 |
+
function unhighlight(element) {
|
272 |
+
element.classList.remove('highlight');
|
273 |
+
}
|
274 |
+
|
275 |
+
function isValidFileType(file, types, extensions) {
|
276 |
+
return types.includes(file.type) || extensions.some(ext => file.name.endsWith(ext));
|
277 |
+
}
|
278 |
+
|
279 |
+
function getFileIcon(file) {
|
280 |
+
if (file.type.startsWith('image/')) return '🖼️';
|
281 |
+
if (file.type === 'application/pdf') return '📄';
|
282 |
+
if (file.type.includes('wordprocessingml')) return '📝';
|
283 |
+
if (file.type.includes('spreadsheetml') || file.type === 'text/csv') return '📊';
|
284 |
+
return '📁';
|
285 |
+
}
|
286 |
+
|
287 |
+
function formatFileSize(bytes) {
|
288 |
+
if (bytes === 0) return '0 Bytes';
|
289 |
+
const k = 1024;
|
290 |
+
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
291 |
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
292 |
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
293 |
+
}
|
294 |
+
|
295 |
+
function showError(element, message) {
|
296 |
+
element.textContent = message;
|
297 |
+
element.classList.remove('hidden');
|
298 |
+
}
|
299 |
+
|
300 |
+
function hideError(element) {
|
301 |
+
element.classList.add('hidden');
|
302 |
+
}
|
303 |
+
|
304 |
+
function setLoading(button, isLoading, loadingText = 'Processing...') {
|
305 |
+
const btnText = button.querySelector('span:not(.spinner)');
|
306 |
+
const spinner = button.querySelector('.spinner');
|
307 |
+
|
308 |
+
button.disabled = isLoading;
|
309 |
+
btnText.textContent = isLoading ? loadingText : btnText.dataset.defaultText;
|
310 |
+
spinner.classList.toggle('hidden', !isLoading);
|
311 |
+
}
|
312 |
+
|
313 |
// Document Analysis Section Setup
|
314 |
const summarizeDropArea = document.getElementById('upload-area');
|
315 |
const summarizeFileInput = document.getElementById('file-input');
|
|
|
320 |
const summarizeError = document.getElementById('error-message');
|
321 |
const languageSelect = document.getElementById('language-select');
|
322 |
|
323 |
+
// Store default button text
|
324 |
+
summarizeBtn.querySelector('span').dataset.defaultText = 'Summarize Document';
|
325 |
+
|
326 |
// Set up drag and drop for file upload
|
327 |
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
328 |
summarizeDropArea.addEventListener(eventName, preventDefaults, false);
|
329 |
});
|
330 |
|
331 |
['dragenter', 'dragover'].forEach(eventName => {
|
332 |
+
summarizeDropArea.addEventListener(eventName, () => highlight(summarizeDropArea), false);
|
333 |
});
|
334 |
|
335 |
['dragleave', 'drop'].forEach(eventName => {
|
336 |
+
summarizeDropArea.addEventListener(eventName, () => unhighlight(summarizeDropArea), false);
|
337 |
});
|
338 |
|
339 |
summarizeDropArea.addEventListener('drop', handleDrop, false);
|
|
|
341 |
summarizeFileInput.addEventListener('change', handleFileSelect);
|
342 |
textInput.addEventListener('input', validateInputs);
|
343 |
|
344 |
+
function handleDrop(e) {
|
345 |
+
const dt = e.dataTransfer;
|
346 |
+
const files = dt.files;
|
347 |
+
handleFiles(files);
|
348 |
+
}
|
349 |
+
|
350 |
+
function handleFileSelect(e) {
|
351 |
+
const files = e.target.files;
|
352 |
+
if (files.length) {
|
353 |
+
handleFiles(files);
|
354 |
+
}
|
355 |
+
}
|
356 |
+
|
357 |
+
function handleFiles(files) {
|
358 |
+
const file = files[0];
|
359 |
+
const validTypes = [
|
360 |
+
'application/pdf',
|
361 |
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
362 |
+
'text/plain',
|
363 |
+
'image/jpeg',
|
364 |
+
'image/png'
|
365 |
+
];
|
366 |
+
const validExtensions = ['.pdf', '.docx', '.txt'];
|
367 |
+
|
368 |
+
if (!isValidFileType(file, validTypes, validExtensions)) {
|
369 |
+
showError(summarizeError, "Unsupported file type. Please upload PDF, DOCX, TXT, or image files.");
|
370 |
+
return;
|
371 |
+
}
|
372 |
+
|
373 |
+
summarizePreview.innerHTML = `
|
374 |
+
<div class="file-info">
|
375 |
+
<span class="file-icon">${getFileIcon(file)}</span>
|
376 |
+
<div>
|
377 |
+
<strong>${file.name}</strong>
|
378 |
+
<span>${formatFileSize(file.size)}</span>
|
379 |
+
</div>
|
380 |
+
</div>
|
381 |
+
`;
|
382 |
+
validateInputs();
|
383 |
+
}
|
384 |
+
|
385 |
+
function validateInputs() {
|
386 |
+
const hasFile = summarizeFileInput.files.length > 0;
|
387 |
+
const hasText = textInput.value.trim() !== '';
|
388 |
+
summarizeBtn.disabled = !(hasFile || hasText);
|
389 |
+
}
|
390 |
+
|
391 |
// Summarize button click handler
|
392 |
summarizeBtn.addEventListener('click', async () => {
|
393 |
const file = summarizeFileInput.files[0];
|
|
|
400 |
}
|
401 |
|
402 |
try {
|
403 |
+
setLoading(summarizeBtn, true);
|
|
|
404 |
hideError(summarizeError);
|
405 |
summarizeResult.textContent = '';
|
406 |
|
407 |
const formData = new FormData();
|
408 |
+
if (file) formData.append('file', file);
|
409 |
+
if (text) formData.append('text', text);
|
|
|
|
|
|
|
410 |
formData.append('language', language);
|
411 |
|
412 |
const response = await fetch('/summarize', {
|
|
|
425 |
showError(summarizeError, error.message);
|
426 |
console.error("Summarization error:", error);
|
427 |
} finally {
|
428 |
+
setLoading(summarizeBtn, false);
|
429 |
}
|
430 |
});
|
431 |
|
432 |
+
// Question Answering Section Setup
|
433 |
+
const qaDropArea = document.getElementById('qa-upload-area');
|
434 |
+
const qaFileInput = document.getElementById('qa-file-input');
|
435 |
+
const qaPreview = document.getElementById('qa-file-preview');
|
436 |
+
const qaQuestionInput = document.getElementById('qa-question');
|
437 |
+
const qaSubmitBtn = document.getElementById('qa-submit-btn');
|
438 |
+
const qaResult = document.getElementById('qa-answer');
|
439 |
+
const qaError = document.getElementById('qa-error-message');
|
440 |
+
const qaLanguageSelect = document.getElementById('qa-language');
|
441 |
+
|
442 |
+
// Store default button text
|
443 |
+
qaSubmitBtn.querySelector('span').dataset.defaultText = 'Get Answer';
|
444 |
+
|
445 |
+
// Setup event listeners
|
446 |
+
if (qaDropArea && qaFileInput) {
|
447 |
+
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
448 |
+
qaDropArea.addEventListener(eventName, preventDefaults, false);
|
449 |
+
});
|
450 |
+
|
451 |
+
['dragenter', 'dragover'].forEach(eventName => {
|
452 |
+
qaDropArea.addEventListener(eventName, () => highlight(qaDropArea), false);
|
453 |
+
});
|
454 |
|
455 |
+
['dragleave', 'drop'].forEach(eventName => {
|
456 |
+
qaDropArea.addEventListener(eventName, () => unhighlight(qaDropArea), false);
|
457 |
+
});
|
458 |
+
|
459 |
+
qaDropArea.addEventListener('drop', handleQADrop, false);
|
460 |
+
qaDropArea.addEventListener('click', () => qaFileInput.click());
|
461 |
+
qaFileInput.addEventListener('change', handleQAFileSelect);
|
462 |
}
|
463 |
|
464 |
+
if (qaQuestionInput && qaSubmitBtn) {
|
465 |
+
qaQuestionInput.addEventListener('input', validateQAInputs);
|
466 |
+
qaSubmitBtn.addEventListener('click', handleQASubmit);
|
467 |
}
|
468 |
|
469 |
+
function handleQADrop(e) {
|
470 |
const dt = e.dataTransfer;
|
471 |
const files = dt.files;
|
472 |
+
handleQAFiles(files);
|
473 |
}
|
474 |
|
475 |
+
function handleQAFileSelect(e) {
|
476 |
const files = e.target.files;
|
477 |
if (files.length) {
|
478 |
+
handleQAFiles(files);
|
479 |
}
|
480 |
}
|
481 |
|
482 |
+
function handleQAFiles(files) {
|
483 |
const file = files[0];
|
484 |
+
const validTypes = [
|
485 |
+
'application/pdf',
|
486 |
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
487 |
+
'text/plain'
|
488 |
+
];
|
489 |
+
const validExtensions = ['.pdf', '.docx', '.txt'];
|
490 |
+
|
491 |
+
if (!isValidFileType(file, validTypes, validExtensions)) {
|
492 |
+
showError(qaError, "Unsupported file type. Please upload PDF, DOCX, or TXT files.");
|
493 |
return;
|
494 |
}
|
495 |
|
496 |
+
qaPreview.innerHTML = `
|
497 |
<div class="file-info">
|
498 |
<span class="file-icon">${getFileIcon(file)}</span>
|
499 |
<div>
|
|
|
502 |
</div>
|
503 |
</div>
|
504 |
`;
|
505 |
+
validateQAInputs();
|
506 |
}
|
507 |
|
508 |
+
function validateQAInputs() {
|
509 |
+
const hasFile = qaFileInput.files.length > 0;
|
510 |
+
const hasQuestion = qaQuestionInput.value.trim() !== '';
|
511 |
+
qaSubmitBtn.disabled = !(hasFile && hasQuestion);
|
512 |
}
|
513 |
|
514 |
+
async function handleQASubmit() {
|
515 |
+
const file = qaFileInput.files[0];
|
516 |
+
const question = qaQuestionInput.value.trim();
|
517 |
+
const language = qaLanguageSelect.value;
|
518 |
+
|
519 |
+
if (!file) {
|
520 |
+
showError(qaError, "Please upload a document first");
|
521 |
+
return;
|
522 |
+
}
|
|
|
|
|
|
|
|
|
523 |
|
524 |
+
if (!question) {
|
525 |
+
showError(qaError, "Please enter your question");
|
526 |
+
return;
|
527 |
+
}
|
|
|
|
|
528 |
|
529 |
+
try {
|
530 |
+
setLoading(qaSubmitBtn, true);
|
531 |
+
hideError(qaError);
|
532 |
+
|
533 |
+
const formData = new FormData();
|
534 |
+
formData.append('file', file);
|
535 |
+
formData.append('question', question);
|
536 |
+
formData.append('language', language);
|
537 |
|
538 |
+
const response = await fetch('/qa', {
|
539 |
+
method: 'POST',
|
540 |
+
body: formData
|
541 |
+
});
|
542 |
+
|
543 |
+
if (!response.ok) {
|
544 |
+
const error = await response.json();
|
545 |
+
throw new Error(error.detail || "Failed to get answer");
|
546 |
+
}
|
547 |
|
548 |
+
const result = await response.json();
|
549 |
+
displayQAResult(result);
|
550 |
+
} catch (error) {
|
551 |
+
showError(qaError, error.message);
|
552 |
+
console.error("QA Error:", error);
|
553 |
+
} finally {
|
554 |
+
setLoading(qaSubmitBtn, false);
|
555 |
+
}
|
556 |
}
|
557 |
|
558 |
+
function displayQAResult(result) {
|
559 |
+
qaResult.textContent = result.answer;
|
560 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
561 |
|
562 |
+
// Data Visualization Section Setup
|
563 |
+
const dataDropArea = document.getElementById('data-upload-area');
|
564 |
+
const dataFileInput = document.getElementById('data-file-input');
|
565 |
+
const dataPreview = document.getElementById('data-file-preview');
|
566 |
+
const visualizeBtn = document.getElementById('visualize-btn');
|
567 |
+
const visualizationPrompt = document.getElementById('visualization-prompt');
|
568 |
+
const chartTypeSelect = document.getElementById('chart-type-select');
|
569 |
+
const visualizationResult = document.getElementById('visualization-results');
|
570 |
+
const visualizationError = document.getElementById('visualization-error');
|
571 |
+
const visualizationImage = document.getElementById('generated-visualization');
|
572 |
+
const pythonCodeOutput = document.getElementById('python-code-output');
|
573 |
+
const dataPreviewContainer = document.getElementById('data-preview-container');
|
574 |
+
const copyCodeBtn = document.getElementById('copy-code-btn');
|
575 |
+
|
576 |
+
// Store default button text
|
577 |
+
visualizeBtn.querySelector('span').dataset.defaultText = 'Generate Visualization';
|
578 |
+
|
579 |
+
// Setup drag and drop for data visualization
|
580 |
+
if (dataDropArea && dataFileInput) {
|
581 |
+
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
582 |
+
dataDropArea.addEventListener(eventName, preventDefaults, false);
|
583 |
+
});
|
584 |
|
585 |
+
['dragenter', 'dragover'].forEach(eventName => {
|
586 |
+
dataDropArea.addEventListener(eventName, () => highlight(dataDropArea), false);
|
587 |
+
});
|
|
|
588 |
|
589 |
+
['dragleave', 'drop'].forEach(eventName => {
|
590 |
+
dataDropArea.addEventListener(eventName, () => unhighlight(dataDropArea), false);
|
591 |
+
});
|
|
|
592 |
|
593 |
+
dataDropArea.addEventListener('drop', handleDataDrop, false);
|
594 |
+
dataDropArea.addEventListener('click', () => dataFileInput.click());
|
595 |
+
dataFileInput.addEventListener('change', handleDataFileSelect);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
596 |
}
|
597 |
|
598 |
+
if (visualizeBtn) {
|
599 |
+
visualizeBtn.addEventListener('click', handleVisualizationSubmit);
|
|
|
600 |
}
|
601 |
|
602 |
+
if (copyCodeBtn) {
|
603 |
+
copyCodeBtn.addEventListener('click', copyPythonCode);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
604 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
605 |
|
606 |
+
function handleDataDrop(e) {
|
607 |
+
const dt = e.dataTransfer;
|
608 |
+
const files = dt.files;
|
609 |
+
handleDataFiles(files);
|
610 |
}
|
|
|
611 |
|
612 |
+
function handleDataFileSelect(e) {
|
613 |
+
const files = e.target.files;
|
614 |
+
if (files.length) {
|
615 |
+
handleDataFiles(files);
|
616 |
+
}
|
617 |
}
|
618 |
|
619 |
+
function handleDataFiles(files) {
|
620 |
+
const file = files[0];
|
621 |
+
const validTypes = [
|
622 |
+
'application/vnd.ms-excel',
|
623 |
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
624 |
+
'text/csv'
|
625 |
+
];
|
626 |
+
const validExtensions = ['.xlsx', '.xls', '.csv'];
|
627 |
+
|
628 |
+
if (!isValidFileType(file, validTypes, validExtensions)) {
|
629 |
+
showVisualizationError("Unsupported file type. Please upload Excel or CSV files.");
|
630 |
+
return;
|
631 |
+
}
|
632 |
+
|
633 |
+
dataPreview.innerHTML = `
|
634 |
+
<div class="file-info">
|
635 |
+
<span class="file-icon">${getFileIcon(file)}</span>
|
636 |
+
<div>
|
637 |
+
<strong>${file.name}</strong>
|
638 |
+
<span>${formatFileSize(file.size)}</span>
|
639 |
+
</div>
|
640 |
</div>
|
641 |
+
`;
|
642 |
+
}
|
|
|
|
|
643 |
|
644 |
+
async function handleVisualizationSubmit() {
|
645 |
+
const file = dataFileInput.files[0];
|
646 |
+
const prompt = visualizationPrompt.value.trim();
|
647 |
+
const chartType = chartTypeSelect.value;
|
648 |
+
|
649 |
+
if (!file) {
|
650 |
+
showVisualizationError("Please upload a file first");
|
651 |
+
return;
|
652 |
+
}
|
653 |
|
654 |
+
try {
|
655 |
+
setLoading(visualizeBtn, true, 'Generating...');
|
656 |
+
hideVisualizationError();
|
657 |
+
visualizationResult.classList.add('hidden');
|
|
|
|
|
|
|
658 |
|
659 |
+
const formData = new FormData();
|
660 |
+
formData.append('file', file);
|
661 |
+
formData.append('request', prompt);
|
662 |
+
formData.append('chart_type', chartType);
|
663 |
|
664 |
+
const response = await fetch('/generate-visualization', {
|
665 |
+
method: 'POST',
|
666 |
+
body: formData
|
667 |
+
});
|
668 |
|
669 |
+
if (!response.ok) {
|
670 |
+
const error = await response.json();
|
671 |
+
throw new Error(error.detail || "Failed to generate visualization");
|
672 |
+
}
|
673 |
|
674 |
+
const result = await response.json();
|
675 |
+
displayVisualizationResult(result);
|
676 |
+
} catch (error) {
|
677 |
+
showVisualizationError(error.message);
|
678 |
+
console.error("Visualization error:", error);
|
679 |
+
} finally {
|
680 |
+
setLoading(visualizeBtn, false);
|
681 |
+
}
|
682 |
+
}
|
|
|
|
|
683 |
|
684 |
+
function displayVisualizationResult(result) {
|
685 |
+
visualizationImage.src = result.image_url;
|
686 |
+
pythonCodeOutput.textContent = result.python_code;
|
687 |
+
|
688 |
+
// Display data preview
|
689 |
+
dataPreviewContainer.innerHTML = `
|
690 |
+
<p>Columns: ${result.columns.join(', ')}</p>
|
691 |
+
<small>Note: Only showing first few rows of data</small>
|
692 |
+
`;
|
693 |
+
|
694 |
+
visualizationResult.classList.remove('hidden');
|
695 |
+
}
|
696 |
|
697 |
+
function showVisualizationError(message) {
|
698 |
+
visualizationError.textContent = message;
|
699 |
+
visualizationError.classList.remove('hidden');
|
700 |
+
}
|
|
|
|
|
|
|
701 |
|
702 |
+
function hideVisualizationError() {
|
703 |
+
visualizationError.classList.add('hidden');
|
704 |
+
}
|
705 |
|
706 |
+
function copyPythonCode() {
|
707 |
+
const code = pythonCodeOutput.textContent;
|
708 |
+
navigator.clipboard.writeText(code)
|
709 |
+
.then(() => {
|
710 |
+
copyCodeBtn.textContent = 'Copied!';
|
711 |
+
setTimeout(() => {
|
712 |
+
copyCodeBtn.textContent = 'Copy Code';
|
713 |
+
}, 2000);
|
714 |
+
})
|
715 |
+
.catch(err => {
|
716 |
+
console.error('Failed to copy code:', err);
|
717 |
+
});
|
718 |
+
}
|
719 |
+
});
|
720 |
|