/** * Data Analyzer - Agent-Specific JavaScript * Handles unique functionality for Data Analyzer agent * Uses WorkflowsCore architecture like other agents */ class DataAnalyzerProcessor extends WorkflowsCore { constructor() { super(); this.agentSlug = 'data-analyzer'; this.webhookUrl = 'http://localhost:5678/webhook/simple-pdf-processor'; this.price = 8.0; // Will be overridden by template data this.sessionId = this.constructor.generateSessionId(); // Initialize on page load this.initialize(); } initialize() { // Set data attributes from page const priceElement = document.body.getAttribute('data-agent-price'); if (priceElement) { this.price = parseFloat(priceElement); } // Initialize form submission const form = document.getElementById('agentForm'); if (form) { form.addEventListener('submit', this.handleFormSubmission.bind(this)); } // Initialize file upload functionality this.initializeFileUpload(); // Initialize form validation this.initializeFormValidation(); // Set initial radio selection const firstRadio = document.querySelector('.radio-card'); if (firstRadio && !document.querySelector('.radio-card.selected')) { firstRadio.classList.add('selected'); const input = firstRadio.querySelector('input[type="radio"]'); if (input) input.checked = true; } } /** * Initialize file upload functionality */ initializeFileUpload() { const fileInput = document.getElementById('dataFile'); if (fileInput) { fileInput.addEventListener('change', this.handleFileChange.bind(this)); } // Initialize drag and drop const uploadArea = document.querySelector('.file-upload-area'); if (uploadArea && fileInput) { this.constructor.setupDragAndDrop(uploadArea, fileInput); } } /** * Handle form submission with hybrid N8N/Django approach */ async handleFormSubmission(e) { e.preventDefault(); if (!this.isFormValid()) { this.constructor.showToast('Please upload a file and select analysis type', 'error'); return; } // Check authentication and balance if (!this.constructor.checkAuthentication()) return; if (!this.constructor.checkBalance(this.price)) return; // Show processing status and disable submit button this.constructor.showProcessing('Analyzing your data file...'); const submitBtn = document.getElementById('generateBtn'); if (submitBtn) { submitBtn.disabled = true; submitBtn.textContent = '⏳ Analyzing...'; } try { // Try direct N8N integration for better performance (with Django fallback) const useDirectN8N = false; // Feature flag - disabled for file uploads (complex) if (useDirectN8N) { await this.processViaDirectN8N(e.target); } else { // For file uploads, use immediate Django processing (N8N direct upload is complex) await this.processViaDjangoImmediate(e.target); } } catch (error) { console.error('Form submission error:', error); this.constructor.hideProcessing(); this.constructor.showToast('❌ Connection error. Please try again.', 'error'); this.resetSubmitButton(); } } /** * Django processing for file uploads (immediate response for data analyzer) */ async processViaDjangoImmediate(form) { const formData = new FormData(form); const response = await fetch(window.location.href, { method: 'POST', body: formData, headers: { 'X-Requested-With': 'XMLHttpRequest' } }); const result = await response.json(); if (result.success && result.analysis_results) { // Data analyzer returns results immediately, no polling needed this.constructor.hideProcessing(); if (result.wallet_balance !== undefined) { this.constructor.updateWalletBalance(result.wallet_balance); } // Display results immediately const analysisData = result.analysis_results; const formattedHtml = this.formatAnalysisResults(analysisData); WorkflowsCore.showResults(formattedHtml, 'Analysis Results'); this.constructor.showToast('✅ Data analysis completed successfully!', 'success'); this.resetSubmitButton(); } else { this.constructor.hideProcessing(); this.constructor.showToast(`❌ ${result.error || 'Processing failed'}`, 'error'); this.resetSubmitButton(); } } /** * Form validation specific to Data Analyzer */ initializeFormValidation() { const fileInput = document.getElementById('dataFile'); const analysisTypeInputs = document.querySelectorAll('input[name="analysisType"]'); if (fileInput) { fileInput.addEventListener('change', () => this.validateField('dataFile')); } analysisTypeInputs.forEach(input => { input.addEventListener('change', () => this.validateField('analysisType')); }); } validateField(fieldName) { switch (fieldName) { case 'dataFile': const fileInput = document.getElementById('dataFile'); if (!fileInput.files || fileInput.files.length === 0) { this.constructor.showFieldError('dataFile', 'Please select a data file'); return false; } const file = fileInput.files[0]; const maxSize = 10 * 1024 * 1024; // 10MB if (file.size > maxSize) { this.constructor.showFieldError('dataFile', 'File too large. Maximum size is 10MB'); return false; } const allowedExtensions = ['.pdf']; const fileExtension = '.' + file.name.split('.').pop().toLowerCase(); if (!allowedExtensions.includes(fileExtension)) { this.constructor.showFieldError('dataFile', 'Unsupported file type. Please use PDF files only'); return false; } break; case 'analysisType': const analysisType = document.querySelector('input[name="analysisType"]:checked'); if (!analysisType) { this.constructor.showFieldError('analysisType', 'Please select an analysis type'); return false; } break; } this.constructor.clearFieldError(fieldName); return true; } isFormValid() { const fileValid = this.validateField('dataFile'); const analysisValid = this.validateField('analysisType'); return fileValid && analysisValid; } /** * Handle file change events with enhanced UX */ handleFileChange(event) { const file = event.target.files[0]; const uploadArea = document.getElementById('fileUploadArea'); const filePreview = document.getElementById('filePreview'); const validationMessage = document.getElementById('validationMessage'); if (file) { // Validate file first const validation = this.validateFileUpload(file); if (!validation.valid) { this.showValidationMessage(validation.message, 'error'); uploadArea.classList.add('upload-error'); uploadArea.classList.remove('file-selected'); this.hideFilePreview(); return; } // Show success validation this.showValidationMessage(validation.message, 'success'); // Update upload area uploadArea.classList.remove('upload-error'); uploadArea.classList.add('file-selected'); // Show file preview with enhanced info this.showFilePreview(file); // Clear any previous errors this.constructor.clearFieldError('dataFile'); } else { // Reset all states this.resetFileUploadState(); } } /** * Validate file upload with detailed feedback */ validateFileUpload(file) { const maxSize = 10 * 1024 * 1024; // 10MB const allowedTypes = ['application/pdf']; const allowedExtensions = ['.pdf']; // Check file type if (!allowedTypes.includes(file.type) && !allowedExtensions.includes('.' + file.name.split('.').pop().toLowerCase())) { return { valid: false, message: 'Invalid file type. Please upload a PDF file only.' }; } // Check file size if (file.size > maxSize) { return { valid: false, message: `File too large (${this.formatFileSize(file.size)}). Maximum size is 10MB.` }; } // Check for empty file if (file.size === 0) { return { valid: false, message: 'File appears to be empty. Please select a valid PDF file.' }; } return { valid: true, message: `✅ File validated successfully (${this.formatFileSize(file.size)})` }; } /** * Show file preview with enhanced information */ showFilePreview(file) { const filePreview = document.getElementById('filePreview'); const previewFileName = document.getElementById('previewFileName'); const previewFileSize = document.getElementById('previewFileSize'); const previewTimestamp = document.getElementById('previewTimestamp'); if (filePreview && previewFileName && previewFileSize && previewTimestamp) { previewFileName.textContent = file.name; previewFileSize.textContent = this.formatFileSize(file.size); previewTimestamp.textContent = `Added ${new Date().toLocaleTimeString()}`; filePreview.classList.add('show'); } } /** * Hide file preview */ hideFilePreview() { const filePreview = document.getElementById('filePreview'); if (filePreview) { filePreview.classList.remove('show'); } } /** * Show validation message with type */ showValidationMessage(message, type = 'info') { const validationMessage = document.getElementById('validationMessage'); if (validationMessage) { validationMessage.textContent = message; validationMessage.className = `validation-message show ${type}`; } } /** * Hide validation message */ hideValidationMessage() { const validationMessage = document.getElementById('validationMessage'); if (validationMessage) { validationMessage.classList.remove('show'); } } /** * Reset file upload state */ resetFileUploadState() { const uploadArea = document.getElementById('fileUploadArea'); const filePreview = document.getElementById('filePreview'); if (uploadArea) { uploadArea.classList.remove('file-selected', 'upload-error', 'uploading'); } this.hideFilePreview(); this.hideValidationMessage(); } /** * Show upload progress */ showUploadProgress() { const uploadProgress = document.getElementById('uploadProgress'); const progressFill = document.getElementById('progressFill'); const progressText = document.getElementById('progressText'); if (uploadProgress) { uploadProgress.classList.add('show'); } // Simulate progress for visual feedback let progress = 0; const interval = setInterval(() => { progress += Math.random() * 15; if (progress > 90) progress = 90; if (progressFill) progressFill.style.width = `${progress}%`; if (progressText) progressText.textContent = `Uploading... ${Math.round(progress)}%`; if (progress >= 90) { clearInterval(interval); if (progressText) progressText.textContent = 'Processing file...'; } }, 200); this.uploadProgressInterval = interval; } /** * Hide upload progress */ hideUploadProgress() { const uploadProgress = document.getElementById('uploadProgress'); if (uploadProgress) { uploadProgress.classList.remove('show'); } if (this.uploadProgressInterval) { clearInterval(this.uploadProgressInterval); this.uploadProgressInterval = null; } } /** * Format file size for display */ 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]; } /** * Format analysis results for HTML display */ formatAnalysisResults(analysisData) { let resultsHtml = '
Analysis completed: ${new Date(analysisData.timestamp).toLocaleString()}
`; } else { resultsHtml += `Analysis completed: ${new Date().toLocaleString()}
`; } return resultsHtml; } /** * Escape HTML to prevent XSS */ escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } /** * Reset submit button to original state */ resetSubmitButton() { const submitBtn = document.getElementById('generateBtn'); if (submitBtn) { submitBtn.disabled = false; submitBtn.textContent = `🚀 Analyze Data (${this.price} AED)`; } } } // Data Analyzer specific functions (global for template onclick handlers) function selectRadio(value) { // Remove selected class from all cards document.querySelectorAll('.radio-card').forEach(card => { card.classList.remove('selected'); }); // Add selected class to clicked card const selectedCard = document.querySelector(`input[value="${value}"]`).closest('.radio-card'); if (selectedCard) { selectedCard.classList.add('selected'); } // Select the radio button const radioInput = document.getElementById(value); if (radioInput) { radioInput.checked = true; } } // File management functions (global for template onclick handlers) function triggerFileSelect() { const fileInput = document.getElementById('dataFile'); if (fileInput) { fileInput.click(); } } function replaceFile() { const fileInput = document.getElementById('dataFile'); if (fileInput) { fileInput.value = ''; fileInput.click(); } } function removeFile() { const fileInput = document.getElementById('dataFile'); const uploadArea = document.getElementById('fileUploadArea'); const filePreview = document.getElementById('filePreview'); const validationMessage = document.getElementById('validationMessage'); if (fileInput) { fileInput.value = ''; // Reset upload area if (uploadArea) { uploadArea.classList.remove('file-selected', 'upload-error'); } // Hide preview and validation if (filePreview) { filePreview.classList.remove('show'); } if (validationMessage) { validationMessage.classList.remove('show'); } // Clear any form errors if (window.dataAnalyzerProcessor) { window.dataAnalyzerProcessor.constructor.clearFieldError('dataFile'); } } } // Drag and drop functionality function setupDragAndDrop() { const uploadArea = document.getElementById('fileUploadArea'); const fileInput = document.getElementById('dataFile'); if (!uploadArea || !fileInput) return; // Prevent default drag behaviors ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { uploadArea.addEventListener(eventName, preventDefaults, false); document.body.addEventListener(eventName, preventDefaults, false); }); // Highlight drop area when item is dragged over it ['dragenter', 'dragover'].forEach(eventName => { uploadArea.addEventListener(eventName, highlight, false); }); ['dragleave', 'drop'].forEach(eventName => { uploadArea.addEventListener(eventName, unhighlight, false); }); // Handle dropped files uploadArea.addEventListener('drop', handleDrop, false); function preventDefaults(e) { e.preventDefault(); e.stopPropagation(); } function highlight(e) { uploadArea.classList.add('dragover'); } function unhighlight(e) { uploadArea.classList.remove('dragover'); } function handleDrop(e) { const dt = e.dataTransfer; const files = dt.files; if (files.length > 0) { fileInput.files = files; fileInput.dispatchEvent(new Event('change', { bubbles: true })); } } } // Result action functions (global for button onclick handlers) function copyResults() { const content = document.getElementById('resultsContent'); if (content) { const text = content.textContent || ''; WorkflowsCore.copyToClipboard(text, 'Analysis results copied to clipboard!'); } } function downloadResults() { const content = document.getElementById('resultsContent'); if (content) { const text = content.textContent || ''; WorkflowsCore.downloadAsFile(text, 'data-analysis-results.txt', 'Analysis results downloaded!'); } } function resetForm() { const form = document.getElementById('agentForm'); if (form) { form.reset(); } const resultsContainer = document.getElementById('resultsContainer'); const processingStatus = document.getElementById('processingStatus'); if (resultsContainer) resultsContainer.style.display = 'none'; if (processingStatus) processingStatus.style.display = 'none'; // Clear file display const fileNameDisplay = document.getElementById('fileName'); const fileSizeDisplay = document.getElementById('fileSize'); if (fileNameDisplay) { fileNameDisplay.textContent = ''; fileNameDisplay.style.display = 'none'; } if (fileSizeDisplay) { fileSizeDisplay.textContent = ''; fileSizeDisplay.style.display = 'none'; } // Clear validation errors WorkflowsCore.clearFieldError('dataFile'); WorkflowsCore.clearFieldError('analysisType'); // Reset radio selection const firstRadio = document.querySelector('.radio-card'); if (firstRadio) { document.querySelectorAll('.radio-card').forEach(card => card.classList.remove('selected')); firstRadio.classList.add('selected'); const input = firstRadio.querySelector('input[type="radio"]'); if (input) input.checked = true; } // Scroll back to form const formSection = document.getElementById('agentForm'); if (formSection) { formSection.scrollIntoView({ behavior: 'smooth', block: 'start' }); } } // Initialize Data Analyzer Processor when DOM is ready document.addEventListener('DOMContentLoaded', function() { // Initialize processor (data attributes set by template) window.dataAnalyzerProcessor = new DataAnalyzerProcessor(); // Setup drag and drop functionality setupDragAndDrop(); });