// API Configuration const API_URL = 'https://nolanzandi-virtual-data-analyst.hf.space'; const PREDICT_ENDPOINT = `${API_URL}/api/predict`; const SAMPLE_ENDPOINT = `${API_URL}/api/sample`; // File Upload and API Integration async function handleFileUpload(file) { try { // Show loading state document.querySelector('.upload-icon').style.display = 'none'; document.querySelector('.loading-spinner').style.display = 'block'; document.querySelector('.progress-bar').style.display = 'block'; // Create FormData const formData = new FormData(); formData.append('file', file); // Update file info updateFileInfo(file); // Simulate progress while actually uploading const progressInterval = simulateProgress(); // Make API request const response = await fetch(API_URL, { method: 'POST', body: formData }); if (!response.ok) { throw new Error('API request failed'); } const data = await response.json(); // Clear progress simulation clearInterval(progressInterval); // Show success state showSuccessState(); // Handle API response handleApiResponse(data); } catch (error) { console.error('Error:', error); showErrorState(error.message); } } function updateFileInfo(file) { const fileInfo = document.getElementById('fileInfo'); const fileName = fileInfo.querySelector('.file-name'); const fileSize = fileInfo.querySelector('.file-size'); const fileIcon = fileInfo.querySelector('.file-type-icon'); const fileType = file.name.split('.').pop().toLowerCase(); const iconClass = getFileTypeIcon(fileType); fileIcon.className = `file-type-icon fas ${iconClass}`; fileName.textContent = file.name; fileSize.textContent = formatFileSize(file.size); fileInfo.classList.remove('hidden'); } function simulateProgress() { const progressBar = document.querySelector('.progress-bar-fill'); let progress = 0; return setInterval(() => { if (progress < 90) { // Only go up to 90% until we get actual completion progress += 5; progressBar.style.width = `${progress}%`; } }, 100); } function showSuccessState() { document.querySelector('.loading-spinner').style.display = 'none'; document.querySelector('.success-checkmark').style.display = 'block'; document.querySelector('.progress-bar-fill').style.width = '100%'; setTimeout(() => { resetUploadState(); }, 2000); } function showErrorState(message) { // Reset upload UI resetUploadState(); // Show error message const errorDiv = document.createElement('div'); errorDiv.className = 'text-red-500 mt-4'; errorDiv.innerHTML = `${message}`; document.querySelector('.drop-zone').appendChild(errorDiv); setTimeout(() => { errorDiv.remove(); }, 5000); } function resetUploadState() { document.querySelector('.success-checkmark').style.display = 'none'; document.querySelector('.upload-icon').style.display = 'block'; document.querySelector('.progress-bar').style.display = 'none'; document.querySelector('.progress-bar-fill').style.width = '0%'; document.getElementById('fileInfo').classList.add('hidden'); } function handleSampleDataClick(datasetName) { // Show loading state in results section const resultsSection = document.getElementById('results'); const resultsLoading = document.getElementById('resultsLoading'); const resultsContent = document.getElementById('resultsContent'); const resultsError = document.getElementById('resultsError'); resultsSection.classList.remove('hidden'); resultsLoading.classList.remove('hidden'); resultsContent.classList.add('hidden'); resultsError.classList.add('hidden'); // Simulate API delay setTimeout(() => { try { // Mock data based on dataset type const mockData = datasetName === 'marketing_campaign' ? { statistics: { rows: 10000, columns: 15, missing_values: 120, data_types: ['numeric', 'categorical', 'datetime'] }, preview: { columns: ['Campaign ID', 'Customer ID', 'Response', 'Channel'], data: [ ['CAM001', 'C001', 'Converted', 'Email'], ['CAM001', 'C002', 'No Response', 'SMS'], ['CAM002', 'C003', 'Converted', 'Social Media'] ] }, visualizations: [ { title: 'Response Rate by Channel', description: 'Conversion rates across different marketing channels', image_url: 'https://via.placeholder.com/400x300' }, { title: 'Campaign Performance', description: 'Success metrics for each campaign', image_url: 'https://via.placeholder.com/400x300' } ], insights: [ { title: 'Best Performing Channel', description: 'Email campaigns show highest conversion rate at 28%' }, { title: 'Optimal Send Time', description: 'Campaigns sent between 2 PM - 4 PM have better engagement' } ] } : { statistics: { rows: 50000, columns: 12, missing_values: 85, data_types: ['numeric', 'categorical', 'datetime'] }, preview: { columns: ['Order ID', 'Product', 'Quantity', 'Price'], data: [ ['ORD001', 'Laptop', '1', '$999.99'], ['ORD002', 'Mouse', '2', '$29.99'], ['ORD003', 'Monitor', '1', '$299.99'] ] }, visualizations: [ { title: 'Sales by Category', description: 'Distribution of sales across product categories', image_url: 'https://via.placeholder.com/400x300' }, { title: 'Monthly Revenue', description: 'Revenue trends over the past 12 months', image_url: 'https://via.placeholder.com/400x300' } ], insights: [ { title: 'Top Products', description: 'Electronics category generates 45% of total revenue' }, { title: 'Customer Behavior', description: 'Average order value increased by 15% in Q4' } ] }; handleApiResponse(mockData); } catch (error) { console.error('Error:', error); showErrorState('Failed to process sample dataset'); } }, 1000); // 1 second delay to show loading state } function handleApiResponse(data) { const resultsSection = document.getElementById('results'); const resultsLoading = document.getElementById('resultsLoading'); const resultsContent = document.getElementById('resultsContent'); const resultsError = document.getElementById('resultsError'); // Show results section resultsSection.classList.remove('hidden'); resultsLoading.classList.add('hidden'); resultsError.classList.add('hidden'); resultsContent.classList.remove('hidden'); // Update Basic Statistics updateBasicStats(data.statistics); // Update Data Preview updateDataPreview(data.preview); // Update Visualizations updateVisualizations(data.visualizations); // Update Insights updateInsights(data.insights); } function updateBasicStats(statistics) { const statsContainer = document.getElementById('basicStats'); statsContainer.innerHTML = ''; const stats = [ { label: 'Rows', value: statistics.rows, icon: 'fa-list' }, { label: 'Columns', value: statistics.columns, icon: 'fa-columns' }, { label: 'Missing Values', value: statistics.missing_values, icon: 'fa-exclamation-triangle' }, { label: 'Data Types', value: statistics.data_types.length, icon: 'fa-code' } ]; stats.forEach(stat => { const statDiv = document.createElement('div'); statDiv.className = 'bg-gray-50 rounded-lg p-4'; statDiv.innerHTML = `
${stat.label}
${stat.value}
`; statsContainer.appendChild(statDiv); }); } function updateDataPreview(preview) { const table = document.getElementById('dataPreview'); table.innerHTML = ''; // Add header const thead = document.createElement('thead'); thead.className = 'bg-gray-50'; const headerRow = document.createElement('tr'); preview.columns.forEach(column => { const th = document.createElement('th'); th.className = 'px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider'; th.textContent = column; headerRow.appendChild(th); }); thead.appendChild(headerRow); table.appendChild(thead); // Add body const tbody = document.createElement('tbody'); tbody.className = 'bg-white divide-y divide-gray-200'; preview.data.forEach(row => { const tr = document.createElement('tr'); row.forEach(cell => { const td = document.createElement('td'); td.className = 'px-6 py-4 whitespace-nowrap text-sm text-gray-500'; td.textContent = cell; tr.appendChild(td); }); tbody.appendChild(tr); }); table.appendChild(tbody); } function updateVisualizations(visualizations) { const container = document.getElementById('visualizations'); container.innerHTML = ''; visualizations.forEach(viz => { const vizDiv = document.createElement('div'); vizDiv.className = 'bg-white rounded-lg p-4 shadow'; vizDiv.innerHTML = `

${viz.title}

${viz.title}

${viz.description}

`; container.appendChild(vizDiv); }); } function updateInsights(insights) { const insightsList = document.getElementById('insights'); insightsList.innerHTML = ''; insights.forEach(insight => { const li = document.createElement('li'); li.className = 'bg-blue-50 rounded-lg p-4'; li.innerHTML = `
${insight.title}

${insight.description}

`; insightsList.appendChild(li); }); } function closeResults() { document.getElementById('results').classList.add('hidden'); } function showErrorState(message) { const resultsSection = document.getElementById('results'); const resultsLoading = document.getElementById('resultsLoading'); const resultsContent = document.getElementById('resultsContent'); const resultsError = document.getElementById('resultsError'); const errorMessage = document.getElementById('errorMessage'); resultsSection.classList.remove('hidden'); resultsLoading.classList.add('hidden'); resultsContent.classList.add('hidden'); resultsError.classList.remove('hidden'); errorMessage.textContent = message; } // Event Listeners document.addEventListener('DOMContentLoaded', () => { // File Upload Handling const dropZone = document.querySelector('.drop-zone'); const fileInput = document.getElementById('fileInput'); // Prevent default drag behaviors ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { dropZone.addEventListener(eventName, preventDefaults, false); document.body.addEventListener(eventName, preventDefaults, false); }); // Highlight drop zone when dragging over it ['dragenter', 'dragover'].forEach(eventName => { dropZone.addEventListener(eventName, highlight, false); }); ['dragleave', 'drop'].forEach(eventName => { dropZone.addEventListener(eventName, unhighlight, false); }); // Handle dropped files dropZone.addEventListener('drop', (e) => { const dt = e.dataTransfer; const files = dt.files; if (files.length > 0) { handleFileUpload(files[0]); } }); fileInput.addEventListener('change', (e) => { if (e.target.files.length > 0) { handleFileUpload(e.target.files[0]); } }); // Sample Data Button Handlers const marketingBtn = document.querySelector('.sample-btn:nth-child(1)'); const retailBtn = document.querySelector('.sample-btn:nth-child(2)'); if (marketingBtn) { marketingBtn.addEventListener('click', () => { console.log('Marketing campaign button clicked'); handleSampleDataClick('marketing_campaign'); }); } if (retailBtn) { retailBtn.addEventListener('click', () => { console.log('Online retail button clicked'); handleSampleDataClick('online_retail'); }); } }); // Utility Functions function preventDefaults(e) { e.preventDefault(); e.stopPropagation(); } function highlight(e) { document.querySelector('.drop-zone').classList.add('border-primary', 'bg-blue-50'); } function unhighlight(e) { document.querySelector('.drop-zone').classList.remove('border-primary', 'bg-blue-50'); } function getFileTypeIcon(fileType) { const icons = { 'csv': 'fa-file-csv', 'tsv': 'fa-file-alt', 'txt': 'fa-file-alt', 'xls': 'fa-file-excel', 'xlsx': 'fa-file-excel', 'xml': 'fa-file-code', 'json': 'fa-file-code' }; return icons[fileType] || 'fa-file'; } function formatFileSize(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; }