document.addEventListener('DOMContentLoaded', () => { const navLinks = document.querySelectorAll('.nav-link'); const pages = document.querySelectorAll('.page'); const sidebar = document.querySelector('.sidebar'); const mainContent = document.querySelector('.main-content'); const sidebarToggle = document.getElementById('sidebar-toggle'); // Unified toggle button const overlay = document.querySelector('.overlay'); const loader = document.querySelector('.loader'); function animateNavText() { const navTexts = document.querySelectorAll('.nav-text'); navTexts.forEach(text => { text.style.animation = 'none'; text.offsetHeight; // Trigger reflow text.style.animation = ''; }); } navLinks.forEach(link => { link.addEventListener('click', (e) => { e.preventDefault(); const pageId = link.dataset.page; pages.forEach(page => { page.classList.remove('active'); }); navLinks.forEach(navLink => { navLink.classList.remove('active'); }); document.getElementById(pageId).classList.add('active'); link.classList.add('active'); // Close sidebar and overlay after navigation on mobile if (isMobileView() && sidebar.classList.contains('active')) { sidebar.classList.remove('active'); overlay.classList.remove('active'); sidebarToggle.querySelector('i').classList.remove('fa-times'); sidebarToggle.querySelector('i').classList.add('fa-bars'); } }); }); // Function to check if it's a mobile view function isMobileView() { return window.innerWidth <= 768; } // Initial setup for sidebar based on screen size function setupSidebarState() { if (isMobileView()) { sidebar.classList.remove('collapsed'); // Ensure desktop collapsed state is removed sidebar.classList.remove('active'); // Start hidden on mobile overlay.classList.remove('active'); mainContent.classList.remove('collapsed'); // Main content always full width on mobile sidebarToggle.classList.add('mobile-toggle'); // Ensure mobile toggle styles are applied sidebarToggle.querySelector('i').classList.remove('fa-times'); // Ensure bars icon sidebarToggle.querySelector('i').classList.add('fa-bars'); } else { // Desktop default state sidebar.classList.remove('active'); // Ensure mobile active state is removed overlay.classList.remove('active'); sidebarToggle.classList.remove('mobile-toggle'); // Remove mobile toggle styles sidebarToggle.querySelector('i').classList.remove('fa-bars'); // Ensure arrow icon sidebarToggle.querySelector('i').classList.add('fa-angle-left'); // Set initial desktop collapsed state if desired, or leave expanded // sidebar.classList.add('collapsed'); // mainContent.classList.add('collapsed'); } } setupSidebarState(); // Call on initial load // Unified event listener for sidebar toggle sidebarToggle.addEventListener('click', () => { if (isMobileView()) { // Mobile behavior: sidebar as overlay sidebar.classList.toggle('active'); overlay.classList.toggle('active'); // Change icon based on sidebar state if (sidebar.classList.contains('active')) { sidebarToggle.querySelector('i').classList.remove('fa-bars'); sidebarToggle.querySelector('i').classList.add('fa-times'); } else { sidebarToggle.querySelector('i').classList.remove('fa-times'); sidebarToggle.querySelector('i').classList.add('fa-bars'); } } else { // Desktop behavior: sidebar collapses/expands sidebar.classList.toggle('collapsed'); mainContent.classList.toggle('collapsed'); // Change icon based on sidebar state if (sidebar.classList.contains('collapsed')) { sidebarToggle.querySelector('i').classList.remove('fa-angle-left'); sidebarToggle.querySelector('i').classList.add('fa-angle-right'); } else { sidebarToggle.querySelector('i').classList.remove('fa-angle-right'); sidebarToggle.querySelector('i').classList.add('fa-angle-left'); } if (!sidebar.classList.contains('collapsed')) { animateNavText(); } } }); // Event listener for overlay click (to close sidebar on mobile) overlay.addEventListener('click', () => { if (isMobileView() && sidebar.classList.contains('active')) { sidebar.classList.remove('active'); overlay.classList.remove('active'); sidebarToggle.querySelector('i').classList.remove('fa-times'); sidebarToggle.querySelector('i').classList.add('fa-bars'); } }); // Adjust sidebar on window resize window.addEventListener('resize', setupSidebarState); function formatAIResponse(text) { text = text.replace(/\*\*(.*?)\*\*/g, '$1<\/strong>'); text = text.replace(/^\d+\.\s+(.*)/gm, '
  • $1<\/li>'); text = text.replace(/(
  • .*<\/li>)/s, '
      $1<\/ol>'); text = text.replace(/^\*\s+(.*)/gm, '
    1. $1<\/li>'); text = text.replace(/(
    2. .*<\/li>)/s, '
        $1<\/ul>'); return text; } const csvUpload = document.getElementById('csv-upload'); const uploadStatus = document.getElementById('upload-status'); const columnList = document.getElementById('column-list'); const plotType = document.getElementById('plot-type'); const plotCol1 = document.getElementById('plot-col1'); const plotCol2 = document.getElementById('plot-col2'); const scatterColorContainer = document.getElementById('scatter-color-container'); const scatterColor = document.getElementById('scatter-color'); const generatePlot = document.getElementById('generate-plot'); const plotImg = document.getElementById('plot-img'); const plotError = document.getElementById('plot-error'); const learningType = document.getElementById('learning-type'); const modelDropdown = document.getElementById('model-dropdown'); const targetColumnDropdown = document.getElementById('target-column-dropdown'); const trainModel = document.getElementById('train-model'); const trainOutput = document.getElementById('train-output'); const aiQuestion = document.getElementById('ai-question'); const askAi = document.getElementById('ask-ai'); const aiAnswer = document.getElementById('ai-answer'); csvUpload.addEventListener('change', async (event) => { const file = event.target.files[0]; if (!file) return; uploadStatus.textContent = 'Uploading...'; const formData = new FormData(); formData.append('file', file); try { const response = await fetch('/api/upload', { method: 'POST', body: formData }); const result = await response.json(); uploadStatus.textContent = result.message || result.error; if (response.ok) { updateColumnSelectors(); setLearningType(); updatePlotOptions(); } } catch (error) { uploadStatus.textContent = `Error: ${error.message}`; } }); async function updateColumnSelectors() { try { const response = await fetch('/api/columns'); const result = await response.json(); const columns = result.columns || []; [columnList, plotCol1, plotCol2, targetColumnDropdown, scatterColor].forEach(selector => { selector.innerHTML = ''; const defaultOption = document.createElement('option'); defaultOption.value = 'None'; defaultOption.textContent = 'None'; selector.appendChild(defaultOption); columns.forEach(col => { const option = document.createElement('option'); option.value = col; option.textContent = col; selector.appendChild(option); }); }); } catch (error) { console.error('Error updating column selectors:', error); } } plotType.addEventListener('change', () => { if (plotType.value === 'Scatter') { scatterColorContainer.style.display = 'block'; } else { scatterColorContainer.style.display = 'none'; } }); generatePlot.addEventListener('click', async () => { if (!plotType.value) { plotError.textContent = 'Please select a plot type.'; return; } loader.style.display = 'block'; plotImg.src = ''; plotError.textContent = ''; const body = { plot_type: plotType.value, col1: plotCol1.value, col2: plotCol2.value, color_col: scatterColor.value }; try { const response = await fetch('/api/plot', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); const result = await response.json(); if (result.image) { plotImg.src = `data:image/png;base64,${result.image}`; } else { plotError.textContent = result.error; } } catch (error) { plotError.textContent = `Error: ${error.message}`; } finally { loader.style.display = 'none'; } }); function formatMetrics(metrics) { let formatted = '\n'; for (const [key, value] of Object.entries(metrics)) { if (typeof value === 'object' && value !== null) { formatted += `${key}:<\/strong>\n`; for (const [subKey, subValue] of Object.entries(value)) { formatted += ` ${subKey}: ${subValue}\n`; } } else { formatted += `${key}:<\/strong> ${value}\n`; } } return formatted; } async function setLearningType() { try { const response = await fetch('/api/learning_type'); const result = await response.json(); if (result.learning_type) { learningType.disabled = false; learningType.value = result.learning_type; learningType.dispatchEvent(new Event('change')); learningType.disabled = true; if (result.learning_type === 'Supervised' && result.target_column) { targetColumnDropdown.value = result.target_column; } } } catch (error) { console.error('Error setting learning type:', error); } } async function updatePlotOptions() { try { const response = await fetch('/api/plot_options'); const result = await response.json(); const plots = result.plots || []; plotType.innerHTML = ''; const defaultOption = document.createElement('option'); defaultOption.value = ''; defaultOption.textContent = 'Select Plot Type'; plotType.appendChild(defaultOption); plots.forEach(plotName => { const option = document.createElement('option'); option.value = plotName; option.textContent = plotName; plotType.appendChild(option); }); } catch (error) { console.error('Error updating plot options:', error); } } trainModel.addEventListener('click', async () => { trainOutput.textContent = 'Training in progress...'; loader.style.display = 'block'; const body = { learning_type: learningType.value, model_name: modelDropdown.value, target_col: targetColumnDropdown.value }; try { const response = await fetch('/api/train', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); const result = await response.json(); let output = result.message || result.error; if (result.metrics) { output += formatMetrics(result.metrics); } if (result.result) { output += `\nResult:<\/strong> ${JSON.stringify(result.result, null, 2)}`; } trainOutput.innerHTML = output; } catch (error) { trainOutput.textContent = `Error: ${error.message}`; } }); askAi.addEventListener('click', async () => { aiAnswer.textContent = 'Thinking...'; loader.style.display = 'block'; const body = { user_query: aiQuestion.value }; try { const response = await fetch('/api/ask', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); const result = await response.json(); aiAnswer.innerHTML = formatAIResponse(result.answer || result.error); } catch (error) { aiAnswer.textContent = `Error: ${error.message}`; } finally { loader.style.display = 'none'; } }); learningType.addEventListener('change', () => { const supervisedModels = ["Logistic Regression", "Naive Bayes", "Decision Tree", "Random Forest", "SVM", "KNN", "XGBoost", "CatBoost", "Linear Regression"]; const unsupervisedModels = ["KMeans", "DBSCAN", "PCA"]; const models = learningType.value === 'Supervised' ? supervisedModels : unsupervisedModels; modelDropdown.innerHTML = ''; models.forEach(model => { const option = document.createElement('option'); option.value = model; option.textContent = model; modelDropdown.appendChild(option); }); }); learningType.dispatchEvent(new Event('change')); // Add click-to-copy functionality to output boxes [trainOutput, aiAnswer, uploadStatus, plotError].forEach(el => { el.addEventListener('click', () => { const textToCopy = el.textContent; navigator.clipboard.writeText(textToCopy).then(() => { const originalText = el.textContent; el.textContent = 'Copied!'; setTimeout(() => { el.textContent = originalText; }, 1000); }); }); }); });