bvproperty's picture
## 1. UI/UX Enhancements
c918845 verified
raw
history blame
15.4 kB
// 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 = `<i class="fas fa-exclamation-circle mr-2"></i>${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 = `
<div class="flex items-center">
<i class="fas ${stat.icon} text-primary text-xl mr-3"></i>
<div>
<div class="text-sm text-gray-500">${stat.label}</div>
<div class="text-lg font-semibold">${stat.value}</div>
</div>
</div>
`;
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 = `
<h4 class="text-lg font-medium text-gray-800 mb-4">${viz.title}</h4>
<div class="aspect-w-16 aspect-h-9">
<img src="${viz.image_url}" alt="${viz.title}" class="rounded-lg">
</div>
<p class="mt-2 text-sm text-gray-600">${viz.description}</p>
`;
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 = `
<div class="flex items-start">
<i class="fas fa-lightbulb text-yellow-500 mt-1 mr-3"></i>
<div>
<div class="font-medium text-blue-900">${insight.title}</div>
<p class="mt-1 text-sm text-blue-700">${insight.description}</p>
</div>
</div>
`;
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];
}