mirror of
https://github.com/thecyberlearn/quantum-ai-v3.git
synced 2026-08-18 15:32:57 +00:00
## Unified Agent Architecture - Create agent-base.css with theme support (professional, creative, minimal) - Implement shared JavaScript utilities in agent-utils.js - Standardize form submission patterns across all agents - Reduce code duplication by ~600 lines ## CSS Theme System - Professional theme: Job posting, data analyzer, weather reporter - Creative theme: Social ads generator (glassmorphism effects) - Minimal theme: Available for future agents - Consistent component library with CSS custom properties ## Text Display Standardization - Replace complex markdown parsing with simple text formatting - Remove external dependencies (Marked.js + DOMPurify) - Implement basic text cleaning: remove **, convert line breaks to <br> - Fix layout stretching issues in job posting results ## Data Analyzer Fixes - Fix network errors by converting from button click to form submission - Update to use unified CSS system and professional theme - Improve file upload handling with conditional checks - Standardize template structure with other agents ## Form Submission Improvements - Unified form submission pattern across all agents - Automatic CSRF token handling via FormData(form) - Consistent error handling and user feedback - Reliable polling mechanism for webhook-based agents ## Documentation Updates - Document unified CSS system and theme architecture - Add text display standardization guidelines - Update agent examples with current features - Include form submission best practices 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
225 lines
7.5 KiB
JavaScript
225 lines
7.5 KiB
JavaScript
/**
|
|
* NetCop AI Agents - Shared Utilities
|
|
* Contains common functions used across all agent templates
|
|
*/
|
|
|
|
window.AgentUtils = {
|
|
/**
|
|
* Simple Text Formatter
|
|
* Cleans AI-generated text and converts to readable HTML
|
|
*/
|
|
parseMarkdown(text) {
|
|
if (!text) return '';
|
|
|
|
return text
|
|
.replace(/\*\*/g, '') // Remove markdown bold syntax
|
|
.replace(/\#{1,3}\s/g, '') // Remove header syntax
|
|
.replace(/\n{3,}/g, '\n\n') // Reduce excessive line breaks
|
|
.replace(/\n/g, '<br>') // Convert line breaks to HTML
|
|
.trim();
|
|
},
|
|
|
|
/**
|
|
* Update wallet balance display across all agents
|
|
*/
|
|
updateWalletBalance(newBalance) {
|
|
const balanceElement = document.querySelector('[data-wallet-balance]') ||
|
|
document.getElementById('walletBalance');
|
|
if (balanceElement) {
|
|
balanceElement.textContent = `${newBalance.toFixed(2)} AED`;
|
|
}
|
|
window.currentWalletBalance = newBalance;
|
|
},
|
|
|
|
/**
|
|
* Reset UI to initial state
|
|
*/
|
|
resetUI(config) {
|
|
const elements = {
|
|
processingStatus: document.getElementById(config.processingStatusId || 'processingStatus'),
|
|
processButton: document.getElementById(config.processButtonId || 'processButton'),
|
|
results: document.getElementById(config.resultsId)
|
|
};
|
|
|
|
if (elements.processingStatus) {
|
|
elements.processingStatus.style.display = 'none';
|
|
}
|
|
|
|
if (elements.processButton) {
|
|
elements.processButton.disabled = false;
|
|
elements.processButton.innerHTML = config.buttonText || 'Process';
|
|
elements.processButton.classList.remove('loading');
|
|
}
|
|
|
|
if (elements.results) {
|
|
elements.results.style.display = 'none';
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Show processing status
|
|
*/
|
|
showProcessing(config) {
|
|
const elements = {
|
|
processingStatus: document.getElementById(config.processingStatusId || 'processingStatus'),
|
|
processButton: document.getElementById(config.processButtonId || 'processButton'),
|
|
results: document.getElementById(config.resultsId)
|
|
};
|
|
|
|
if (elements.processingStatus) {
|
|
elements.processingStatus.style.display = 'block';
|
|
}
|
|
|
|
if (elements.processButton) {
|
|
elements.processButton.disabled = true;
|
|
elements.processButton.classList.add('loading');
|
|
elements.processButton.innerHTML = config.processingText || '⏳ Processing...';
|
|
}
|
|
|
|
if (elements.results) {
|
|
elements.results.style.display = 'none';
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Show toast notification with duplicate prevention
|
|
*/
|
|
showToast(message, type = 'info') {
|
|
// Prevent duplicate toasts
|
|
const existingToast = document.querySelector('.agent-toast');
|
|
if (existingToast) {
|
|
existingToast.remove();
|
|
}
|
|
|
|
const toast = document.createElement('div');
|
|
toast.className = 'agent-toast';
|
|
toast.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;
|
|
font-weight: 500;
|
|
${type === 'success' ? 'background: #10b981;' : 'background: #ef4444;'}
|
|
`;
|
|
toast.textContent = message;
|
|
document.body.appendChild(toast);
|
|
|
|
setTimeout(() => {
|
|
if (toast.parentNode) {
|
|
toast.remove();
|
|
}
|
|
}, 2000);
|
|
},
|
|
|
|
/**
|
|
* Display results with markdown parsing
|
|
* Standardized across all agents
|
|
*/
|
|
displayResults(config) {
|
|
const resultsContainer = document.getElementById(config.resultsId);
|
|
const contentContainer = document.getElementById(config.contentId);
|
|
|
|
if (config.result.success && config.result.status === 'completed') {
|
|
// Get content from various possible fields
|
|
const content = config.result.content ||
|
|
config.result.job_posting_content ||
|
|
config.result.ad_copy_content ||
|
|
config.result.analysis_results ||
|
|
config.result.insights_summary ||
|
|
config.result.report_text ||
|
|
config.result.weather_data ||
|
|
config.result.formatted_report ||
|
|
config.result.output_text ||
|
|
config.defaultMessage ||
|
|
'Content generated successfully!';
|
|
|
|
// Parse markdown and display as HTML
|
|
const formattedContent = this.parseMarkdown(content);
|
|
contentContainer.innerHTML = formattedContent;
|
|
|
|
resultsContainer.style.display = 'block';
|
|
|
|
// Update wallet balance if provided
|
|
if (config.result.wallet_balance !== undefined) {
|
|
this.updateWalletBalance(config.result.wallet_balance);
|
|
}
|
|
|
|
this.showToast(config.successMessage || '✅ Content generated and payment processed!', 'success');
|
|
} else {
|
|
this.showToast(config.errorMessage || '❌ Failed to generate content - no charge applied', 'error');
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Generate text for copy/download functionality
|
|
*/
|
|
generateTextForExport(contentElementId) {
|
|
const content = document.getElementById(contentElementId);
|
|
if (content) {
|
|
return content.innerText || content.textContent || '';
|
|
}
|
|
return 'No content available';
|
|
},
|
|
|
|
/**
|
|
* Copy content to clipboard
|
|
*/
|
|
copyToClipboard(text, successMessage = 'Content copied to clipboard!') {
|
|
navigator.clipboard.writeText(text).then(() => {
|
|
this.showToast(`📋 ${successMessage}`, 'success');
|
|
}).catch(() => {
|
|
this.showToast('Failed to copy content', 'error');
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Download content as text file
|
|
*/
|
|
downloadAsFile(text, filename, successMessage = 'File downloaded!') {
|
|
const blob = new Blob([text], { type: 'text/plain' });
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = filename || `content-${Date.now()}.txt`;
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
this.showToast(`💾 ${successMessage}`, 'success');
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Progressive status steps for better UX
|
|
*/
|
|
window.StatusStepper = class {
|
|
constructor(steps, statusTextElementId, interval = 800) {
|
|
this.steps = steps;
|
|
this.statusTextElement = document.getElementById(statusTextElementId);
|
|
this.interval = interval;
|
|
this.currentStep = 0;
|
|
this.stepInterval = null;
|
|
}
|
|
|
|
start() {
|
|
this.currentStep = 0;
|
|
this.stepInterval = setInterval(() => {
|
|
if (this.currentStep < this.steps.length && this.statusTextElement) {
|
|
this.statusTextElement.textContent = this.steps[this.currentStep];
|
|
this.currentStep++;
|
|
} else {
|
|
this.stop();
|
|
}
|
|
}, this.interval);
|
|
}
|
|
|
|
stop() {
|
|
if (this.stepInterval) {
|
|
clearInterval(this.stepInterval);
|
|
this.stepInterval = null;
|
|
}
|
|
}
|
|
}; |