mirror of
https://github.com/thecyberlearn/quantum-ai-v2.git
synced 2026-08-18 20:32:58 +00:00
- Add missing {% csrf_token %} to data analyzer template
- Resolves "Cannot read properties of null (reading 'value')" error
- JavaScript now properly accesses CSRF token for form submission
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
781 lines
27 KiB
HTML
781 lines
27 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static %}
|
|
|
|
{% block title %}Data Analyzer Agent - NetCop AI Hub{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
* {
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
/* Override main-container for full-width sections */
|
|
.main-container {
|
|
max-width: none;
|
|
padding: 0;
|
|
margin-top: 0;
|
|
}
|
|
|
|
/* Page background */
|
|
.data-analyzer-page {
|
|
background: var(--gradient-hero);
|
|
min-height: calc(100vh - 80px);
|
|
padding: clamp(20px, 5vw, 40px);
|
|
width: 100vw;
|
|
margin-left: calc(-50vw + 50%);
|
|
}
|
|
|
|
.container {
|
|
max-width: 1280px;
|
|
margin: 0 auto;
|
|
padding: 0 clamp(16px, 4vw, 24px);
|
|
}
|
|
|
|
/* Main grid layout */
|
|
.main-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(min(350px, 100%), 1fr));
|
|
gap: clamp(16px, 4vw, 24px);
|
|
align-items: start;
|
|
}
|
|
|
|
/* Card styles with glassmorphism */
|
|
.card {
|
|
background: rgba(255, 255, 255, 0.9);
|
|
border-radius: clamp(12px, 3vw, 16px);
|
|
padding: clamp(16px, 4vw, 24px);
|
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
backdrop-filter: blur(20px);
|
|
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
|
|
margin-bottom: clamp(16px, 4vw, 24px);
|
|
}
|
|
|
|
.section-title {
|
|
font-size: clamp(16px, 4vw, 18px);
|
|
font-weight: 600;
|
|
color: var(--text-primary);
|
|
margin-bottom: clamp(12px, 3vw, 16px);
|
|
}
|
|
|
|
/* Form inputs */
|
|
.form-input {
|
|
width: 100%;
|
|
padding: clamp(12px, 3vw, 16px) clamp(16px, 4vw, 20px);
|
|
border: 2px solid var(--border-medium);
|
|
border-radius: clamp(8px, 2vw, 12px);
|
|
font-size: clamp(14px, 3.5vw, 16px);
|
|
transition: border-color 0.2s ease;
|
|
min-height: 48px;
|
|
margin-bottom: clamp(12px, 3vw, 16px);
|
|
}
|
|
|
|
.form-input:focus {
|
|
outline: none;
|
|
border-color: var(--success-green);
|
|
}
|
|
|
|
.help-text {
|
|
font-size: clamp(12px, 3vw, 14px);
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
/* File upload zone */
|
|
.file-upload-zone {
|
|
border: 2px dashed var(--border-medium);
|
|
border-radius: clamp(12px, 3vw, 16px);
|
|
padding: clamp(24px, 6vw, 40px);
|
|
text-align: center;
|
|
transition: all 0.3s ease;
|
|
background: var(--background-light);
|
|
cursor: pointer;
|
|
margin-bottom: clamp(12px, 3vw, 16px);
|
|
}
|
|
|
|
.file-upload-zone.dragover {
|
|
border-color: var(--success-green);
|
|
background: rgba(16, 185, 129, 0.1);
|
|
}
|
|
|
|
.file-upload-zone:hover {
|
|
border-color: var(--success-green);
|
|
background: rgba(16, 185, 129, 0.1);
|
|
}
|
|
|
|
.file-preview {
|
|
background: rgba(16, 185, 129, 0.1);
|
|
border: 1px solid rgba(16, 185, 129, 0.5);
|
|
border-radius: 8px;
|
|
padding: 12px;
|
|
margin-top: 12px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
}
|
|
|
|
/* Radio button grids */
|
|
.radio-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr));
|
|
gap: clamp(8px, 2vw, 12px);
|
|
}
|
|
|
|
.radio-option {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: clamp(8px, 2vw, 12px);
|
|
padding: clamp(12px, 3vw, 16px);
|
|
border: 2px solid var(--border-light);
|
|
border-radius: clamp(8px, 2vw, 12px);
|
|
cursor: pointer;
|
|
background: white;
|
|
transition: all 0.2s ease;
|
|
min-height: 44px;
|
|
}
|
|
|
|
.radio-option.selected {
|
|
border-color: var(--success-green);
|
|
background: rgba(16, 185, 129, 0.1);
|
|
}
|
|
|
|
.radio-option input[type="radio"] {
|
|
margin: 0;
|
|
}
|
|
|
|
/* Processing status */
|
|
.processing-status {
|
|
padding: clamp(16px, 4vw, 20px);
|
|
background: rgba(16, 185, 129, 0.1);
|
|
border: 1px solid var(--success-green);
|
|
border-radius: clamp(8px, 2vw, 12px);
|
|
color: var(--success-dark);
|
|
font-weight: 600;
|
|
text-align: center;
|
|
margin-bottom: clamp(16px, 4vw, 24px);
|
|
}
|
|
|
|
/* Results card */
|
|
.results-card {
|
|
background: rgba(255, 255, 255, 0.9);
|
|
border-radius: 16px;
|
|
padding: 24px;
|
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
backdrop-filter: blur(20px);
|
|
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
|
|
margin-top: 24px;
|
|
}
|
|
|
|
.results-header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.results-content {
|
|
background: var(--background-page);
|
|
border: 1px solid var(--border-light);
|
|
border-radius: 12px;
|
|
padding: 24px;
|
|
margin-bottom: 20px;
|
|
white-space: pre-line;
|
|
line-height: 1.7;
|
|
color: var(--text-primary);
|
|
font-size: 15px;
|
|
}
|
|
|
|
.action-buttons {
|
|
display: flex;
|
|
gap: 12px;
|
|
margin-top: 20px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
/* Button styles */
|
|
.btn {
|
|
padding: clamp(12px, 3vw, 16px) clamp(20px, 5vw, 32px);
|
|
border: none;
|
|
border-radius: clamp(8px, 2vw, 12px);
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
font-size: clamp(14px, 3.5vw, 16px);
|
|
min-height: 48px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
transition: transform 0.1s ease;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.btn-primary {
|
|
background: var(--gradient-success);
|
|
color: white;
|
|
flex: 1;
|
|
min-width: 120px;
|
|
}
|
|
|
|
.btn-secondary {
|
|
background: var(--background-subtle);
|
|
color: var(--text-primary);
|
|
border: 2px solid var(--border-medium);
|
|
flex: 1;
|
|
min-width: 120px;
|
|
}
|
|
|
|
.btn:hover {
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
.btn:disabled {
|
|
background: var(--border-strong);
|
|
color: white;
|
|
cursor: not-allowed;
|
|
transform: none;
|
|
opacity: 0.6;
|
|
}
|
|
|
|
/* Wallet sidebar */
|
|
.wallet-section {
|
|
position: sticky;
|
|
top: 20px;
|
|
}
|
|
|
|
.wallet-balance {
|
|
font-size: clamp(24px, 6vw, 28px);
|
|
font-weight: 700;
|
|
color: var(--text-primary);
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.balance-label {
|
|
font-size: clamp(14px, 3.5vw, 16px);
|
|
color: var(--text-secondary);
|
|
margin-bottom: clamp(16px, 4vw, 20px);
|
|
}
|
|
|
|
.process-btn {
|
|
width: 100%;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.usage-info {
|
|
padding: 16px;
|
|
background: rgba(16, 185, 129, 0.1);
|
|
border-radius: 12px;
|
|
border: 1px solid rgba(16, 185, 129, 0.2);
|
|
}
|
|
|
|
.usage-info h4 {
|
|
margin: 0 0 8px 0;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
color: var(--success-dark);
|
|
}
|
|
|
|
.usage-info ul {
|
|
margin: 0;
|
|
font-size: 12px;
|
|
color: var(--text-primary);
|
|
line-height: 1.4;
|
|
list-style: none;
|
|
padding-left: 0;
|
|
}
|
|
|
|
.usage-info li {
|
|
margin: 4px 0;
|
|
padding-left: 16px;
|
|
position: relative;
|
|
}
|
|
|
|
.usage-info li::before {
|
|
content: "•";
|
|
position: absolute;
|
|
left: 0;
|
|
color: var(--success-green);
|
|
}
|
|
|
|
/* Responsive */
|
|
@media (max-width: 768px) {
|
|
.main-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.radio-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.action-buttons {
|
|
flex-direction: column;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="data-analyzer-page">
|
|
<div class="container">
|
|
{% csrf_token %}
|
|
<!-- Main Content Grid -->
|
|
<div class="main-grid">
|
|
<!-- Input Form -->
|
|
<div>
|
|
<!-- File Upload Section -->
|
|
<div class="card">
|
|
<h3 class="section-title">📁 Upload Your Data File</h3>
|
|
|
|
<div class="file-upload-zone" id="fileUploadZone" onclick="document.getElementById('fileInput').click()">
|
|
<div style="font-size: clamp(32px, 8vw, 48px); margin-bottom: 12px;">📊</div>
|
|
<div style="font-size: clamp(16px, 4vw, 18px); font-weight: 600; color: var(--text-primary); margin-bottom: 8px;">
|
|
Choose or drag your data file here
|
|
</div>
|
|
<div style="font-size: clamp(14px, 3.5vw, 16px); color: var(--text-secondary);">
|
|
Supports PDF, CSV, Excel files (up to 10MB)
|
|
</div>
|
|
</div>
|
|
|
|
<input
|
|
type="file"
|
|
id="fileInput"
|
|
accept=".pdf,.csv,.xlsx,.xls"
|
|
style="display: none;"
|
|
/>
|
|
|
|
<div id="filePreview" class="file-preview" style="display: none;">
|
|
<div style="font-size: 24px;">📄</div>
|
|
<div style="flex: 1;">
|
|
<div style="font-weight: 600; color: var(--text-primary);" id="fileName"></div>
|
|
<div style="font-size: 14px; color: var(--text-secondary);" id="fileSize"></div>
|
|
</div>
|
|
<button type="button" onclick="removeFile()" style="
|
|
background: var(--error-red);
|
|
color: white;
|
|
border: none;
|
|
border-radius: 4px;
|
|
padding: 4px 8px;
|
|
cursor: pointer;
|
|
font-size: 12px;
|
|
">
|
|
Remove
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Analysis Type Selection -->
|
|
<div class="card">
|
|
<h3 class="section-title">🔍 Analysis Type</h3>
|
|
|
|
<div class="radio-grid">
|
|
<label class="radio-option selected" data-value="summary">
|
|
<input type="radio" name="analysisType" value="summary" checked />
|
|
<div style="font-size: clamp(16px, 4vw, 20px);">📋</div>
|
|
<div>
|
|
<div style="font-weight: 600; margin-bottom: clamp(2px, 1vw, 4px); font-size: clamp(14px, 3.5vw, 16px);">
|
|
Summary Analysis
|
|
</div>
|
|
<div style="font-size: clamp(12px, 3vw, 14px); color: var(--text-secondary);">
|
|
Quick overview and key insights
|
|
</div>
|
|
</div>
|
|
</label>
|
|
|
|
<label class="radio-option" data-value="detailed">
|
|
<input type="radio" name="analysisType" value="detailed" />
|
|
<div style="font-size: clamp(16px, 4vw, 20px);">📈</div>
|
|
<div>
|
|
<div style="font-weight: 600; margin-bottom: clamp(2px, 1vw, 4px); font-size: clamp(14px, 3.5vw, 16px);">
|
|
Detailed Analysis
|
|
</div>
|
|
<div style="font-size: clamp(12px, 3vw, 14px); color: var(--text-secondary);">
|
|
Comprehensive statistical analysis
|
|
</div>
|
|
</div>
|
|
</label>
|
|
|
|
<label class="radio-option" data-value="statistical">
|
|
<input type="radio" name="analysisType" value="statistical" />
|
|
<div style="font-size: clamp(16px, 4vw, 20px);">📊</div>
|
|
<div>
|
|
<div style="font-weight: 600; margin-bottom: clamp(2px, 1vw, 4px); font-size: clamp(14px, 3.5vw, 16px);">
|
|
Statistical Analysis
|
|
</div>
|
|
<div style="font-size: clamp(12px, 3vw, 14px); color: var(--text-secondary);">
|
|
Advanced statistics and trends
|
|
</div>
|
|
</div>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Processing Status -->
|
|
<div id="processingStatus" class="processing-status" style="display: none;">
|
|
<div style="font-size: clamp(16px, 4vw, 18px); margin-bottom: 8px;">
|
|
⏳ Processing...
|
|
</div>
|
|
<div style="font-size: clamp(14px, 3.5vw, 16px);" id="statusText">
|
|
Analyzing your data file...
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Results -->
|
|
<div id="analysisResults" class="results-card" style="display: none;">
|
|
<div class="results-header">
|
|
<div style="font-size: 24px;">✅</div>
|
|
<h3 style="font-size: 20px; font-weight: 600; color: var(--text-primary); margin: 0;">
|
|
Data Analysis Results
|
|
</h3>
|
|
<div style="
|
|
background: var(--success-green);
|
|
color: white;
|
|
padding: 6px 12px;
|
|
border-radius: 6px;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
margin-left: auto;
|
|
">
|
|
✅ Complete
|
|
</div>
|
|
</div>
|
|
|
|
<div class="results-content" id="analysisContent">
|
|
<!-- Analysis data will be displayed here -->
|
|
</div>
|
|
|
|
<div class="action-buttons">
|
|
<button onclick="copyAnalysisReport()" class="btn btn-primary">
|
|
📋 Copy Report
|
|
</button>
|
|
<button onclick="downloadAnalysisReport()" class="btn btn-secondary">
|
|
💾 Download Report
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Wallet Sidebar -->
|
|
<div class="wallet-section">
|
|
<div class="card">
|
|
<h3 class="section-title">💳 Your Wallet</h3>
|
|
|
|
<div class="wallet-balance" id="walletBalance">{{ user.wallet_balance|floatformat:2 }} AED</div>
|
|
<div class="balance-label">Available Balance</div>
|
|
|
|
<button
|
|
class="btn btn-primary process-btn"
|
|
id="processButton"
|
|
onclick="analyzeData()"
|
|
>
|
|
📊 Analyze Data (5.00 AED)
|
|
</button>
|
|
</div>
|
|
|
|
<div class="usage-info">
|
|
<h4>💡 How it works</h4>
|
|
<ul>
|
|
<li>Upload PDF, CSV, or Excel files</li>
|
|
<li>Choose your analysis depth</li>
|
|
<li>Get AI-powered insights</li>
|
|
<li>Download comprehensive reports</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
let selectedFile = null;
|
|
|
|
// File upload handling
|
|
const fileInput = document.getElementById('fileInput');
|
|
const fileUploadZone = document.getElementById('fileUploadZone');
|
|
const filePreview = document.getElementById('filePreview');
|
|
|
|
// Handle file input change
|
|
fileInput.addEventListener('change', function(e) {
|
|
if (e.target.files.length > 0) {
|
|
selectedFile = e.target.files[0];
|
|
showFilePreview(selectedFile);
|
|
}
|
|
});
|
|
|
|
// Drag and drop functionality
|
|
fileUploadZone.addEventListener('dragover', function(e) {
|
|
e.preventDefault();
|
|
fileUploadZone.classList.add('dragover');
|
|
});
|
|
|
|
fileUploadZone.addEventListener('dragleave', function(e) {
|
|
e.preventDefault();
|
|
fileUploadZone.classList.remove('dragover');
|
|
});
|
|
|
|
fileUploadZone.addEventListener('drop', function(e) {
|
|
e.preventDefault();
|
|
fileUploadZone.classList.remove('dragover');
|
|
|
|
const files = e.dataTransfer.files;
|
|
if (files.length > 0) {
|
|
const file = files[0];
|
|
if (isValidFile(file)) {
|
|
selectedFile = file;
|
|
fileInput.files = files;
|
|
showFilePreview(file);
|
|
} else {
|
|
showToast('Please select a valid data file (PDF, CSV, Excel)', 'error');
|
|
}
|
|
}
|
|
});
|
|
|
|
// Show file preview
|
|
function showFilePreview(file) {
|
|
document.getElementById('fileName').textContent = file.name;
|
|
document.getElementById('fileSize').textContent = formatFileSize(file.size);
|
|
filePreview.style.display = 'flex';
|
|
fileUploadZone.style.display = 'none';
|
|
}
|
|
|
|
// Remove file
|
|
function removeFile() {
|
|
selectedFile = null;
|
|
fileInput.value = '';
|
|
filePreview.style.display = 'none';
|
|
fileUploadZone.style.display = 'block';
|
|
}
|
|
|
|
// Validate file type
|
|
function isValidFile(file) {
|
|
const validTypes = [
|
|
'application/pdf',
|
|
'text/csv',
|
|
'application/vnd.ms-excel',
|
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
];
|
|
return validTypes.includes(file.type) || file.name.match(/\.(pdf|csv|xlsx|xls)$/i);
|
|
}
|
|
|
|
// Format file size
|
|
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];
|
|
}
|
|
|
|
// Radio button handling
|
|
document.querySelectorAll('.radio-option').forEach(option => {
|
|
option.addEventListener('click', function() {
|
|
document.querySelectorAll('.radio-option').forEach(opt => opt.classList.remove('selected'));
|
|
this.classList.add('selected');
|
|
this.querySelector('input[type="radio"]').checked = true;
|
|
});
|
|
});
|
|
|
|
// Analysis function
|
|
function analyzeData() {
|
|
if (!selectedFile) {
|
|
showToast('Please select a data file', 'error');
|
|
return;
|
|
}
|
|
|
|
// Check user authentication
|
|
{% if not user.is_authenticated %}
|
|
window.location.href = "{% url 'authentication:login' %}";
|
|
return;
|
|
{% endif %}
|
|
|
|
// Check wallet balance
|
|
const balance = {{ user.wallet_balance|default:0 }};
|
|
if (balance < 5.00) {
|
|
showToast('Insufficient balance! You need 5.00 AED.', 'error');
|
|
setTimeout(() => {
|
|
window.location.href = "{% url 'core:wallet' %}";
|
|
}, 2000);
|
|
return;
|
|
}
|
|
|
|
const analysisType = document.querySelector('input[name="analysisType"]:checked').value;
|
|
|
|
// Show processing status
|
|
document.getElementById('processingStatus').style.display = 'block';
|
|
document.getElementById('processButton').disabled = true;
|
|
document.getElementById('processButton').innerHTML = '⏳ Processing...';
|
|
document.getElementById('analysisResults').style.display = 'none';
|
|
|
|
// Processing steps for user feedback
|
|
const steps = [
|
|
'Reading file structure...',
|
|
'Extracting data patterns...',
|
|
'Performing statistical analysis...',
|
|
'Generating insights...',
|
|
'Finalizing report...'
|
|
];
|
|
|
|
let currentStep = 0;
|
|
const stepInterval = setInterval(() => {
|
|
if (currentStep < steps.length) {
|
|
document.getElementById('statusText').textContent = steps[currentStep];
|
|
currentStep++;
|
|
} else {
|
|
clearInterval(stepInterval);
|
|
}
|
|
}, 1000);
|
|
|
|
// Submit form data to backend
|
|
const formData = new FormData();
|
|
formData.append('file', selectedFile);
|
|
formData.append('analysis_type', analysisType);
|
|
formData.append('csrfmiddlewaretoken', document.querySelector('[name=csrfmiddlewaretoken]').value);
|
|
|
|
fetch('/agents/data-analyzer/process/', {
|
|
method: 'POST',
|
|
body: formData,
|
|
headers: {
|
|
'X-Requested-With': 'XMLHttpRequest'
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(result => {
|
|
clearInterval(stepInterval);
|
|
if (result.success && result.request_id) {
|
|
// Start polling for results
|
|
pollForResults(result.request_id);
|
|
} else {
|
|
// Handle immediate response
|
|
document.getElementById('processingStatus').style.display = 'none';
|
|
document.getElementById('processButton').disabled = false;
|
|
document.getElementById('processButton').innerHTML = '📊 Analyze Data (5.00 AED)';
|
|
|
|
if (result.error) {
|
|
showToast(`❌ ${result.error}`, 'error');
|
|
} else {
|
|
displayResults(result);
|
|
}
|
|
}
|
|
})
|
|
.catch(error => {
|
|
clearInterval(stepInterval);
|
|
console.error('Error:', error);
|
|
document.getElementById('processingStatus').style.display = 'none';
|
|
document.getElementById('processButton').disabled = false;
|
|
document.getElementById('processButton').innerHTML = '📊 Analyze Data (5.00 AED)';
|
|
showToast('❌ Network error - please try again', 'error');
|
|
});
|
|
}
|
|
|
|
// Display analysis results
|
|
function displayResults(result) {
|
|
const resultsContainer = document.getElementById('analysisResults');
|
|
const contentContainer = document.getElementById('analysisContent');
|
|
|
|
if (result.success && result.status === 'completed') {
|
|
// Use the analysis content from backend
|
|
const content = result.report_text || result.analysis_results || result.insights_summary || 'Data analysis completed successfully!';
|
|
contentContainer.textContent = content;
|
|
resultsContainer.style.display = 'block';
|
|
|
|
// Update wallet balance if provided
|
|
if (result.wallet_balance !== undefined) {
|
|
updateWalletBalance(result.wallet_balance);
|
|
}
|
|
|
|
showToast('✅ Data analysis completed and payment processed!', 'success');
|
|
} else {
|
|
showToast('❌ Failed to analyze data - no charge applied', 'error');
|
|
}
|
|
}
|
|
|
|
// Poll for results
|
|
function pollForResults(requestId) {
|
|
let pollCount = 0;
|
|
const maxPolls = 60; // 60 seconds maximum for data analysis
|
|
|
|
const pollInterval = setInterval(() => {
|
|
pollCount++;
|
|
|
|
fetch(`/agents/data-analyzer/result/${requestId}/`)
|
|
.then(response => response.json())
|
|
.then(result => {
|
|
if (result.status === 'completed' || result.status === 'failed') {
|
|
clearInterval(pollInterval);
|
|
document.getElementById('processingStatus').style.display = 'none';
|
|
document.getElementById('processButton').disabled = false;
|
|
document.getElementById('processButton').innerHTML = '📊 Analyze Data (5.00 AED)';
|
|
|
|
displayResults(result);
|
|
} else if (pollCount >= maxPolls) {
|
|
clearInterval(pollInterval);
|
|
document.getElementById('processingStatus').style.display = 'none';
|
|
document.getElementById('processButton').disabled = false;
|
|
document.getElementById('processButton').innerHTML = '📊 Analyze Data (5.00 AED)';
|
|
showToast('❌ Processing timeout - please try again', 'error');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error polling results:', error);
|
|
if (pollCount >= maxPolls) {
|
|
clearInterval(pollInterval);
|
|
document.getElementById('processingStatus').style.display = 'none';
|
|
document.getElementById('processButton').disabled = false;
|
|
document.getElementById('processButton').innerHTML = '📊 Analyze Data (5.00 AED)';
|
|
showToast('❌ Network error - please try again', 'error');
|
|
}
|
|
});
|
|
}, 1000);
|
|
}
|
|
|
|
|
|
function updateWalletBalance(newBalance) {
|
|
// Update wallet balance display
|
|
const balanceElements = document.querySelectorAll('[data-wallet-balance]');
|
|
balanceElements.forEach(element => {
|
|
element.textContent = `${newBalance.toFixed(2)} AED`;
|
|
});
|
|
window.currentWalletBalance = newBalance;
|
|
}
|
|
|
|
function copyAnalysisReport() {
|
|
const reportText = document.getElementById('analysisContent').textContent;
|
|
navigator.clipboard.writeText(reportText).then(() => {
|
|
showToast('📋 Analysis report copied to clipboard!', 'success');
|
|
}).catch(() => {
|
|
showToast('Failed to copy report', 'error');
|
|
});
|
|
}
|
|
|
|
function downloadAnalysisReport() {
|
|
const reportText = document.getElementById('analysisContent').textContent;
|
|
const blob = new Blob([reportText], { type: 'text/plain' });
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = `data-analysis-report-${Date.now()}.txt`;
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
showToast('💾 Analysis report downloaded!', 'success');
|
|
}
|
|
|
|
function showToast(message, type = 'info') {
|
|
const toast = document.createElement('div');
|
|
toast.style.cssText = `
|
|
position: fixed;
|
|
top: 20px;
|
|
right: 20px;
|
|
padding: 12px 20px;
|
|
border-radius: 8px;
|
|
color: white;
|
|
font-weight: 600;
|
|
z-index: 1000;
|
|
${type === 'success' ? 'background: var(--success-green);' : 'background: var(--error-red);'}
|
|
`;
|
|
toast.textContent = message;
|
|
document.body.appendChild(toast);
|
|
|
|
setTimeout(() => {
|
|
toast.remove();
|
|
}, 3000);
|
|
}
|
|
</script>
|
|
{% endblock %} |