// === FILE: app/static/js/dashboard.js === // Global variables let selectedFile = null; let deleteFileId = null; // ======================================== // UPLOAD FUNCTIONALITY // ======================================== // Open upload modal document.getElementById('open-upload-modal').addEventListener('click', function() { const modal = document.getElementById('upload-modal'); modal.classList.remove('hidden'); modal.classList.add('flex', 'modal-enter'); resetUploadModal(); }); // Close upload modal document.getElementById('cancel-upload').addEventListener('click', function() { closeUploadModal(); }); // Close upload modal with X button document.getElementById('close-upload-modal').addEventListener('click', function() { closeUploadModal(); }); function closeUploadModal() { const modal = document.getElementById('upload-modal'); modal.classList.add('modal-exit'); setTimeout(() => { modal.classList.add('hidden'); modal.classList.remove('flex', 'modal-enter', 'modal-exit'); }, 200); } // Reset upload modal function resetUploadModal() { selectedFile = null; document.getElementById('file-input').value = ''; document.getElementById('selected-file').classList.add('hidden'); document.getElementById('progress-container').classList.add('hidden'); document.getElementById('upload-error').classList.add('hidden'); document.getElementById('progress-bar').style.width = '0%'; document.getElementById('upload-btn').disabled = true; } // Drop zone click document.getElementById('drop-zone').addEventListener('click', function() { document.getElementById('file-input').click(); }); // File input change document.getElementById('file-input').addEventListener('change', function(e) { if (e.target.files.length > 0) { handleFileSelect(e.target.files[0]); } }); // Drag and drop handlers const dropZone = document.getElementById('drop-zone'); dropZone.addEventListener('dragover', function(e) { e.preventDefault(); e.stopPropagation(); dropZone.classList.add('drag-over'); }); dropZone.addEventListener('dragleave', function(e) { e.preventDefault(); e.stopPropagation(); dropZone.classList.remove('drag-over'); }); dropZone.addEventListener('drop', function(e) { e.preventDefault(); e.stopPropagation(); dropZone.classList.remove('drag-over'); if (e.dataTransfer.files.length > 0) { handleFileSelect(e.dataTransfer.files[0]); } }); // Handle file selection function handleFileSelect(file) { // Check file size (100MB max) const maxSize = 100 * 1024 * 1024; // 100MB if (file.size > maxSize) { showUploadError('File size exceeds 100MB limit'); return; } selectedFile = file; document.getElementById('selected-file-name').textContent = file.name; document.getElementById('selected-file').classList.remove('hidden'); document.getElementById('upload-btn').disabled = false; document.getElementById('upload-error').classList.add('hidden'); } // Show upload error function showUploadError(message) { const errorDiv = document.getElementById('upload-error'); const errorText = document.getElementById('upload-error-text'); if (errorText) { errorText.textContent = message; } else { errorDiv.textContent = message; } errorDiv.classList.remove('hidden'); } // Upload button click document.getElementById('upload-btn').addEventListener('click', function() { if (!selectedFile) { showUploadError('Please select a file'); return; } uploadFile(selectedFile); }); // Upload file function function uploadFile(file) { const formData = new FormData(); formData.append('file', file); // Show progress bar document.getElementById('progress-container').classList.remove('hidden'); document.getElementById('upload-btn').disabled = true; document.getElementById('cancel-upload').disabled = true; // Use XMLHttpRequest for progress tracking const xhr = new XMLHttpRequest(); // Progress handler xhr.upload.addEventListener('progress', function(e) { if (e.lengthComputable) { const percentage = (e.loaded / e.total) * 100; const roundedPercent = Math.round(percentage); document.getElementById('progress-bar').style.width = percentage + '%'; document.getElementById('progress-text').textContent = `Uploading...`; const progressPercent = document.getElementById('progress-percent'); if (progressPercent) { progressPercent.textContent = roundedPercent + '%'; } } }); // Load handler (upload complete) xhr.addEventListener('load', function() { if (xhr.status === 200) { try { const response = JSON.parse(xhr.responseText); if (response.success) { // Success - reload page to show new file window.location.reload(); } else { showUploadError(response.message || 'Upload failed'); document.getElementById('upload-btn').disabled = false; document.getElementById('cancel-upload').disabled = false; } } catch (e) { showUploadError('Upload failed'); document.getElementById('upload-btn').disabled = false; document.getElementById('cancel-upload').disabled = false; } } else { showUploadError('Upload failed. Please try again.'); document.getElementById('upload-btn').disabled = false; document.getElementById('cancel-upload').disabled = false; } }); // Error handler xhr.addEventListener('error', function() { showUploadError('Network error. Please try again.'); document.getElementById('upload-btn').disabled = false; document.getElementById('cancel-upload').disabled = false; }); // Send request xhr.open('POST', '/files/upload'); xhr.send(formData); } // ======================================== // DELETE FUNCTIONALITY // ======================================== function confirmDelete(fileId, fileName) { deleteFileId = fileId; document.getElementById('delete-file-name').textContent = fileName; const modal = document.getElementById('delete-modal'); modal.classList.remove('hidden'); modal.classList.add('flex', 'modal-enter'); } // Cancel delete document.getElementById('cancel-delete').addEventListener('click', function() { closeDeleteModal(); }); function closeDeleteModal() { const modal = document.getElementById('delete-modal'); modal.classList.add('modal-exit'); setTimeout(() => { modal.classList.add('hidden'); modal.classList.remove('flex', 'modal-enter', 'modal-exit'); deleteFileId = null; }, 200); } // Confirm delete document.getElementById('confirm-delete-btn').addEventListener('click', function() { if (!deleteFileId) return; const btn = this; btn.classList.add('btn-loading'); btn.disabled = true; fetch(`/files/delete/${deleteFileId}`, { method: 'POST', headers: { 'Content-Type': 'application/json' } }) .then(response => response.json()) .then(data => { if (data.success) { // Remove file card from DOM const fileCard = document.querySelector(`.file-card[data-file-id="${deleteFileId}"]`); if (fileCard) { fileCard.style.opacity = '0'; fileCard.style.transform = 'scale(0.9)'; setTimeout(() => { fileCard.remove(); // Check if no files left const filesGrid = document.getElementById('files-grid'); if (filesGrid && filesGrid.children.length === 0) { window.location.reload(); } }, 300); } closeDeleteModal(); showFlashMessage('File deleted successfully', 'success'); } else { showFlashMessage(data.message || 'Failed to delete file', 'error'); btn.classList.remove('btn-loading'); btn.disabled = false; } }) .catch(error => { console.error('Error:', error); showFlashMessage('Failed to delete file', 'error'); btn.classList.remove('btn-loading'); btn.disabled = false; }); }); // ======================================== // SHARE FUNCTIONALITY // ======================================== function shareFile(fileId) { fetch(`/files/share/${fileId}`, { method: 'POST', headers: { 'Content-Type': 'application/json' } }) .then(response => response.json()) .then(data => { if (data.success) { document.getElementById('share-url').value = data.share_url; const modal = document.getElementById('share-modal'); modal.classList.remove('hidden'); modal.classList.add('flex', 'modal-enter'); } else { showFlashMessage(data.message || 'Failed to generate share link', 'error'); } }) .catch(error => { console.error('Error:', error); showFlashMessage('Failed to generate share link', 'error'); }); } // Close share modal document.getElementById('close-share-modal').addEventListener('click', function() { closeShareModal(); }); function closeShareModal() { const modal = document.getElementById('share-modal'); modal.classList.add('modal-exit'); setTimeout(() => { modal.classList.add('hidden'); modal.classList.remove('flex', 'modal-enter', 'modal-exit'); document.getElementById('copy-feedback').classList.add('hidden'); }, 200); } // Copy link button document.getElementById('copy-link-btn').addEventListener('click', function() { const shareUrl = document.getElementById('share-url'); shareUrl.select(); shareUrl.setSelectionRange(0, 99999); // For mobile devices // Copy to clipboard navigator.clipboard.writeText(shareUrl.value) .then(() => { const feedback = document.getElementById('copy-feedback'); feedback.classList.remove('hidden'); // Hide after 2 seconds setTimeout(() => { feedback.classList.add('hidden'); }, 2000); }) .catch(err => { console.error('Failed to copy:', err); showFlashMessage('Failed to copy link', 'error'); }); }); // ======================================== // PREVIEW FUNCTIONALITY // ======================================== function previewFile(fileId) { fetch(`/files/preview/${fileId}`) .then(response => response.json()) .then(data => { if (data.success && data.preview_url) { window.open(data.preview_url, '_blank'); } else { showFlashMessage('Preview not available', 'error'); } }) .catch(error => { console.error('Error:', error); showFlashMessage('Failed to generate preview', 'error'); }); } // ======================================== // HELPER FUNCTIONS // ======================================== function showFlashMessage(message, category) { const flashContainer = document.getElementById('flash-messages') || createFlashContainer(); const messageDiv = document.createElement('div'); messageDiv.className = `flash-message px-6 py-3 rounded-lg shadow-lg text-white`; if (category === 'success') { messageDiv.classList.add('bg-[#22c55e]'); } else if (category === 'error') { messageDiv.classList.add('bg-[#ef4444]'); } else { messageDiv.classList.add('bg-[#6366f1]'); } messageDiv.textContent = message; flashContainer.appendChild(messageDiv); // Auto-dismiss after 4 seconds setTimeout(() => { messageDiv.style.opacity = '0'; messageDiv.style.transform = 'translateX(100%)'; messageDiv.style.transition = 'all 0.3s ease-out'; setTimeout(() => { messageDiv.remove(); }, 300); }, 4000); } function createFlashContainer() { const container = document.createElement('div'); container.id = 'flash-messages'; container.className = 'fixed top-4 right-4 z-50 space-y-2'; document.body.appendChild(container); return container; } // ======================================== // INITIALIZATION // ======================================== document.addEventListener('DOMContentLoaded', function() { console.log('Dashboard loaded'); });