Spaces:
Sleeping
Sleeping
| /** | |
| * Main JavaScript file for the AI Learning Path Generator | |
| * Handles client-side interactivity for the web interface | |
| */ | |
| // Wait for DOM to be fully loaded | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Form submission handling for path generator | |
| const pathForm = document.getElementById('pathGeneratorForm'); | |
| if (pathForm) { | |
| setupPathGenerator(pathForm); | |
| } | |
| // Question handling for result page | |
| const askButton = document.getElementById('askButton'); | |
| if (askButton) { | |
| setupQuestionAsking(askButton); | |
| } | |
| // Initialize tooltips and other UI elements | |
| initUI(); | |
| }); | |
| /** | |
| * Set up path generator form submission | |
| * @param {HTMLElement} form - The form element | |
| */ | |
| function setupPathGenerator(form) { | |
| const loadingSpinner = document.getElementById('loadingSpinner'); | |
| form.addEventListener('submit', function(e) { | |
| e.preventDefault(); | |
| // Show loading spinner | |
| if (loadingSpinner) { | |
| loadingSpinner.style.display = 'block'; | |
| } | |
| // Gather form data | |
| const formData = new FormData(form); | |
| formData.append('type', 'human'); | |
| // Convert FormData to plain object for logging | |
| const formDataObj = {}; | |
| formData.forEach((value, key) => { | |
| formDataObj[key] = value; | |
| }); | |
| console.log('Submitting form data:', formDataObj); | |
| // Submit form data via fetch API | |
| fetch('/generate', { | |
| method: 'POST', | |
| body: formData, // Let the browser set the correct Content-Type with boundary | |
| // Don't set Content-Type header when sending FormData, let the browser handle it | |
| }) | |
| .then(response => response.json()) | |
| .then(data => { | |
| // Hide loading spinner | |
| if (loadingSpinner) { | |
| loadingSpinner.style.display = 'none'; | |
| } | |
| if (data.success) { | |
| // Redirect to result page if successful | |
| window.location.href = data.redirect; | |
| } else { | |
| // Show error message | |
| showAlert('Error: ' + data.message, 'error'); | |
| } | |
| }) | |
| .catch(error => { | |
| // Hide loading spinner | |
| if (loadingSpinner) { | |
| loadingSpinner.style.display = 'none'; | |
| } | |
| console.error('Error:', error); | |
| showAlert('An error occurred. Please try again.', 'error'); | |
| }); | |
| }); | |
| } | |
| /** | |
| * Set up question asking functionality | |
| * @param {HTMLElement} button - The ask button element | |
| */ | |
| function setupQuestionAsking(button) { | |
| const questionInput = document.getElementById('questionInput'); | |
| const questionSpinner = document.getElementById('questionSpinner'); | |
| const answerContainer = document.getElementById('answerContainer'); | |
| const answerText = document.getElementById('answerText'); | |
| button.addEventListener('click', function() { | |
| const question = questionInput.value.trim(); | |
| if (!question) { | |
| showAlert('Please enter a question', 'warning'); | |
| return; | |
| } | |
| // Show loading spinner | |
| if (questionSpinner) { | |
| questionSpinner.style.display = 'block'; | |
| } | |
| // Get path information from the page | |
| const pathId = document.querySelector('[data-path-id]')?.dataset.pathId; | |
| const topic = document.querySelector('[data-path-topic]')?.dataset.pathTopic; | |
| // Prepare request data | |
| const requestData = { | |
| type: "human", | |
| question: question, | |
| path_id: pathId, | |
| topic: topic | |
| }; | |
| // Submit question via fetch API | |
| fetch('/api/ask', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify(requestData) | |
| }) | |
| .then(response => response.json()) | |
| .then(data => { | |
| // Hide loading spinner | |
| if (questionSpinner) { | |
| questionSpinner.style.display = 'none'; | |
| } | |
| if (data.success !== false) { | |
| // Show answer container | |
| if (answerContainer) { | |
| answerContainer.classList.remove('hidden'); | |
| } | |
| if (answerText) { | |
| answerText.textContent = data.answer; | |
| } | |
| } else { | |
| // Show error message | |
| showAlert('Error: ' + data.message, 'error'); | |
| } | |
| }) | |
| .catch(error => { | |
| // Hide loading spinner | |
| if (questionSpinner) { | |
| questionSpinner.style.display = 'none'; | |
| } | |
| console.error('Error:', error); | |
| showAlert('An error occurred. Please try again.', 'error'); | |
| }); | |
| }); | |
| } | |
| /** | |
| * Initialize UI components | |
| */ | |
| function initUI() { | |
| // Add smooth scrolling for anchor links | |
| document.querySelectorAll('a[href^="#"]').forEach(anchor => { | |
| anchor.addEventListener('click', function (e) { | |
| e.preventDefault(); | |
| const target = document.querySelector(this.getAttribute('href')); | |
| if (target) { | |
| window.scrollTo({ | |
| top: target.offsetTop, | |
| behavior: 'smooth' | |
| }); | |
| } | |
| }); | |
| }); | |
| // Add fade-in animations for elements | |
| const fadeElems = document.querySelectorAll('.fade-in'); | |
| fadeElems.forEach((elem, index) => { | |
| setTimeout(() => { | |
| elem.style.opacity = '1'; | |
| elem.style.transform = 'translateY(0)'; | |
| }, 100 * index); | |
| }); | |
| } | |
| /** | |
| * Show an alert message | |
| * @param {string} message - The message to display | |
| * @param {string} type - The type of alert (success, error, warning, info) | |
| */ | |
| function showAlert(message, type = 'info') { | |
| // Check if an alert container exists, otherwise create one | |
| let alertContainer = document.getElementById('alertContainer'); | |
| if (!alertContainer) { | |
| alertContainer = document.createElement('div'); | |
| alertContainer.id = 'alertContainer'; | |
| alertContainer.style.position = 'fixed'; | |
| alertContainer.style.top = '1rem'; | |
| alertContainer.style.right = '1rem'; | |
| alertContainer.style.zIndex = '9999'; | |
| document.body.appendChild(alertContainer); | |
| } | |
| // Create the alert element | |
| const alert = document.createElement('div'); | |
| alert.className = 'alert-message fade-in'; | |
| alert.style.padding = '0.75rem 1rem'; | |
| alert.style.marginBottom = '0.5rem'; | |
| alert.style.borderRadius = '0.375rem'; | |
| alert.style.boxShadow = '0 2px 5px rgba(0,0,0,0.1)'; | |
| alert.style.opacity = '0'; | |
| alert.style.transform = 'translateY(10px)'; | |
| alert.style.transition = 'opacity 0.3s ease, transform 0.3s ease'; | |
| // Set styles based on type | |
| switch (type) { | |
| case 'success': | |
| alert.style.backgroundColor = '#d1fae5'; | |
| alert.style.color = '#065f46'; | |
| break; | |
| case 'error': | |
| alert.style.backgroundColor = '#fee2e2'; | |
| alert.style.color = '#991b1b'; | |
| break; | |
| case 'warning': | |
| alert.style.backgroundColor = '#fef3c7'; | |
| alert.style.color = '#92400e'; | |
| break; | |
| default: // info | |
| alert.style.backgroundColor = '#e0f2fe'; | |
| alert.style.color = '#0369a1'; | |
| } | |
| // Set the message | |
| alert.textContent = message; | |
| // Add a close button | |
| const closeButton = document.createElement('span'); | |
| closeButton.innerHTML = '×'; | |
| closeButton.style.float = 'right'; | |
| closeButton.style.cursor = 'pointer'; | |
| closeButton.style.fontWeight = 'bold'; | |
| closeButton.style.marginLeft = '1rem'; | |
| closeButton.addEventListener('click', () => { | |
| alert.remove(); | |
| }); | |
| alert.prepend(closeButton); | |
| // Add the alert to the container | |
| alertContainer.appendChild(alert); | |
| // Trigger animation | |
| setTimeout(() => { | |
| alert.style.opacity = '1'; | |
| alert.style.transform = 'translateY(0)'; | |
| }, 10); | |
| // Auto-remove after 5 seconds | |
| setTimeout(() => { | |
| alert.style.opacity = '0'; | |
| alert.style.transform = 'translateY(-10px)'; | |
| // Remove from DOM after animation completes | |
| setTimeout(() => { | |
| alert.remove(); | |
| }, 300); | |
| }, 5000); | |
| } | |