diff --git a/data_analyzer/templates/data_analyzer/detail.html b/data_analyzer/templates/data_analyzer/detail.html index edfbf94..1625558 100644 --- a/data_analyzer/templates/data_analyzer/detail.html +++ b/data_analyzer/templates/data_analyzer/detail.html @@ -4,6 +4,17 @@ {% block title %}Data Analyzer Agent - NetCop AI Hub{% endblock %} {% block extra_css %} + + + + + + + + + + + + + {% endblock %} {% block content %} -
-
+
+
{% if messages %} {% for message in messages %} @@ -642,35 +211,14 @@ // Copy job posting to clipboard function copyJobPosting() { - const jobText = generateJobText(); - navigator.clipboard.writeText(jobText).then(() => { - showToast('📋 Job posting copied to clipboard!', 'success'); - }).catch(() => { - showToast('Failed to copy job posting', 'error'); - }); + const jobText = AgentUtils.generateTextForExport('jobContent'); + AgentUtils.copyToClipboard(jobText, 'Job posting copied to clipboard!'); } // Download job posting as text file function downloadJobPosting() { - const jobText = generateJobText(); - const blob = new Blob([jobText], { type: 'text/plain' }); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = 'job-posting-' + Date.now() + '.txt'; - a.click(); - URL.revokeObjectURL(url); - showToast('💾 Job posting downloaded!', 'success'); - } - - // Generate job text for copy/download - function generateJobText() { - const content = document.querySelector('#jobContent'); - if (content) { - // Get text content without HTML tags for clean copying - return content.innerText || content.textContent || ''; - } - return 'No job posting content available'; + const jobText = AgentUtils.generateTextForExport('jobContent'); + AgentUtils.downloadAsFile(jobText, `job-posting-${Date.now()}.txt`, 'Job posting downloaded!'); } // Reset form for creating another job posting @@ -685,77 +233,24 @@ // Reset form and UI document.getElementById('jobPostingForm').reset(); document.getElementById('jobResults').style.display = 'none'; - document.getElementById('processingStatus').style.display = 'none'; - const processButton = document.getElementById('processButton'); - processButton.disabled = false; - processButton.classList.remove('loading'); - processButton.innerHTML = '💼 Generate Job Posting (4.00 AED)'; + resetUIState(); - showToast('Form reset! Ready for another job posting.', 'success'); + AgentUtils.showToast('Form reset! Ready for another job posting.', 'success'); } - // Toast management - prevent all duplicates - let toastTimeout = null; - let lastToastMessage = ''; - let currentToast = null; - - function showToast(message, type = 'info') { - // Prevent duplicate messages - if (message === lastToastMessage && currentToast) { - return; - } - - // Clear existing toast - if (currentToast) { - currentToast.remove(); - currentToast = null; - } - - // Clear existing timeout - if (toastTimeout) { - clearTimeout(toastTimeout); - } - - // Store message to prevent duplicates - lastToastMessage = message; - - // Create new toast - currentToast = document.createElement('div'); - currentToast.style.cssText = ` - position: fixed; - top: 16px; - right: 16px; - padding: 8px 12px; - border-radius: 4px; - color: white; - font-size: 13px; - z-index: 1000; - max-width: 300px; - ${type === 'success' ? 'background: var(--primary-color);' : 'background: var(--accent-color);'} - `; - currentToast.textContent = message; - document.body.appendChild(currentToast); - - // Auto remove after 2 seconds - toastTimeout = setTimeout(() => { - if (currentToast) { - currentToast.remove(); - currentToast = null; - } - lastToastMessage = ''; - }, 2000); - } - // Form validation with visual feedback + // Enhanced form validation with visual feedback function validateField(field) { const isValid = field.value.trim() !== ''; const container = field.closest('.form-group') || field.parentElement; if (isValid) { field.style.borderColor = 'var(--success-color)'; + field.style.boxShadow = '0 0 0 2px rgba(16, 185, 129, 0.1)'; container.classList.remove('error'); } else { field.style.borderColor = 'var(--error-color)'; + field.style.boxShadow = '0 0 0 2px rgba(239, 68, 68, 0.1)'; container.classList.add('error'); } @@ -773,6 +268,7 @@ // Reset validation on focus field.addEventListener('focus', () => { field.style.borderColor = 'var(--primary-color)'; + field.style.boxShadow = '0 0 0 2px rgba(0, 0, 0, 0.1)'; field.closest('.form-group')?.classList.remove('error'); }); @@ -798,55 +294,24 @@ window.currentWalletBalance = newBalance; } - // Simple markdown to HTML converter - function parseMarkdown(text) { - return text - // Headers - .replace(/^### (.*$)/gm, '

$1

') - .replace(/^## (.*$)/gm, '

$1

') - .replace(/^# (.*$)/gm, '

$1

') - // Bold - .replace(/\*\*(.*?)\*\*/g, '$1') - // Italic - .replace(/\*(.*?)\*/g, '$1') - // Lists - .replace(/^- (.*$)/gm, '
  • $1
  • ') - .replace(/(
  • .*<\/li>)/s, '
      $1
    ') - // Line breaks - .replace(/\n\n/g, '

    ') - .replace(/\n/g, '
    '); - } // Display job posting results with markdown formatting function displayResults(result) { - const resultsContainer = document.getElementById('jobResults'); - const contentContainer = document.getElementById('jobContent'); - - if (result.success && result.status === 'completed') { - const content = result.content || result.job_posting_content || result.output_text || 'Job posting generated successfully!'; - - // Parse and display as HTML with markdown formatting - const formattedContent = parseMarkdown(content); - contentContainer.innerHTML = '

    ' + formattedContent + '

    '; - - resultsContainer.style.display = 'block'; - - // Update wallet balance if provided - if (result.wallet_balance !== undefined) { - updateWalletBalance(result.wallet_balance); - } - - showToast('✅ Job posting created and payment processed!', 'success'); - } else { - showToast('❌ Failed to generate job posting - no charge applied', 'error'); - } + AgentUtils.displayResults({ + result: result, + resultsId: 'jobResults', + contentId: 'jobContent', + defaultMessage: 'Job posting generated successfully!', + successMessage: '✅ Job posting created and payment processed!', + errorMessage: '❌ Failed to generate job posting - no charge applied' + }); } // Track if results have been displayed to prevent duplicates let resultsDisplayed = false; let currentPollInterval = null; - // Poll for results + // Poll for results with improved error handling function pollForResults(requestId) { let pollCount = 0; const maxPolls = 30; // 30 seconds maximum @@ -855,13 +320,19 @@ // Clear any existing polling if (currentPollInterval) { clearInterval(currentPollInterval); + currentPollInterval = null; } currentPollInterval = setInterval(() => { pollCount++; fetch(`/agents/job-posting-generator/status/${requestId}/`) - .then(response => response.json()) + .then(response => { + if (!response.ok) { + throw new Error(`HTTP ${response.status}`); + } + return response.json(); + }) .then(result => { if (result.status === 'completed' || result.status === 'failed') { // Stop polling immediately @@ -869,11 +340,7 @@ currentPollInterval = null; // Reset UI - document.getElementById('processingStatus').style.display = 'none'; - const processButton = document.getElementById('processButton'); - processButton.disabled = false; - processButton.classList.remove('loading'); - processButton.innerHTML = '💼 Generate Job Posting (4.00 AED)'; + resetUIState(); // Display results only once if (!resultsDisplayed) { @@ -883,36 +350,35 @@ } else if (pollCount >= maxPolls) { clearInterval(currentPollInterval); currentPollInterval = null; - document.getElementById('processingStatus').style.display = 'none'; - const processButton = document.getElementById('processButton'); - processButton.disabled = false; - processButton.classList.remove('loading'); - processButton.innerHTML = '💼 Generate Job Posting (4.00 AED)'; - showToast('❌ Processing timeout - please try again', 'error'); + resetUIState(); + AgentUtils.showToast('❌ Processing timeout - please try again', 'error'); } }) .catch(error => { console.error('Error polling results:', error); - if (pollCount >= maxPolls) { - clearInterval(currentPollInterval); - currentPollInterval = null; - document.getElementById('processingStatus').style.display = 'none'; - const processButton = document.getElementById('processButton'); - processButton.disabled = false; - processButton.classList.remove('loading'); - processButton.innerHTML = '💼 Generate Job Posting (4.00 AED)'; - showToast('❌ Network error - please try again', 'error'); - } + clearInterval(currentPollInterval); + currentPollInterval = null; + resetUIState(); + AgentUtils.showToast('❌ Network error during processing - please try again', 'error'); }); }, 1000); } + // Helper function to reset UI state + function resetUIState() { + document.getElementById('processingStatus').style.display = 'none'; + const processButton = document.getElementById('processButton'); + processButton.disabled = false; + processButton.classList.remove('loading'); + processButton.innerHTML = '💼 Generate Job Posting (4.00 AED)'; + } + // Handle form submission document.getElementById('jobPostingForm').addEventListener('submit', function(e) { e.preventDefault(); if (!isFormValid()) { - showToast('Please fill in all required fields', 'error'); + AgentUtils.showToast('Please fill in all required fields', 'error'); return; } @@ -931,7 +397,7 @@ // Check wallet balance const balance = {{ user.wallet_balance|default:0 }}; if (balance < 4.00) { - showToast('Insufficient balance! You need 4.00 AED.', 'error'); + AgentUtils.showToast('Insufficient balance! You need 4.00 AED.', 'error'); setTimeout(() => { window.location.href = "{% url 'core:wallet' %}"; }, 2000); @@ -953,11 +419,11 @@ document.getElementById('jobResults').style.display = 'none'; const steps = [ - 'Analyzing job requirements...', - 'Structuring job description...', - 'Optimizing for recruitment...', - 'Adding company branding...', - 'Finalizing professional format...' + 'Analyzing job requirements and company details...', + 'Structuring professional job description...', + 'Optimizing content for recruitment platforms...', + 'Adding company branding and tone...', + 'Finalizing professional format and review...' ]; let currentStep = 0; @@ -988,14 +454,10 @@ pollForResults(result.request_id); } else { // Handle immediate response - document.getElementById('processingStatus').style.display = 'none'; - const processButton = document.getElementById('processButton'); - processButton.disabled = false; - processButton.classList.remove('loading'); - processButton.innerHTML = '💼 Generate Job Posting (4.00 AED)'; + resetUIState(); if (result.error) { - showToast(`❌ ${result.error}`, 'error'); + AgentUtils.showToast(`❌ ${result.error}`, 'error'); } else { displayResults(result); } @@ -1004,12 +466,8 @@ .catch(error => { clearInterval(stepInterval); console.error('Error:', error); - document.getElementById('processingStatus').style.display = 'none'; - const processButton = document.getElementById('processButton'); - processButton.disabled = false; - processButton.classList.remove('loading'); - processButton.innerHTML = '💼 Generate Job Posting (4.00 AED)'; - showToast('❌ Network error - please try again', 'error'); + resetUIState(); + AgentUtils.showToast('❌ Network error - please try again', 'error'); }); }); diff --git a/social_ads_generator/templates/social_ads_generator/detail.html b/social_ads_generator/templates/social_ads_generator/detail.html index aca550e..b7182e0 100644 --- a/social_ads_generator/templates/social_ads_generator/detail.html +++ b/social_ads_generator/templates/social_ads_generator/detail.html @@ -4,295 +4,22 @@ {% block title %}Social Ads Generator Agent - NetCop AI Hub{% endblock %} {% block extra_css %} - + + + + + + + + + + + {% endblock %} {% block content %} -