mirror of
https://github.com/thecyberlearn/quantum-ai-v3.git
synced 2026-08-18 07:52:57 +00:00
- Complete Django project structure with apps/ organization - Working homepage with exact Next.js recreation (1039 lines) - User authentication system with wallet balance - Agent system with processors and models - Stripe payment integration setup - All Django migrations and URL routing - Comprehensive .gitignore file - Homepage loads successfully (HTTP 200) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
486 lines
22 KiB
HTML
486 lines
22 KiB
HTML
{% extends 'base.html' %}
|
|
|
|
{% block title %}{{ agent.name }} - NetCop AI Hub{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
|
<!-- Main Content -->
|
|
<div class="lg:col-span-2">
|
|
<div class="bg-white rounded-lg shadow-lg p-6">
|
|
<div class="flex items-center mb-6">
|
|
<div class="w-16 h-16 bg-gradient-to-r {{ agent.get_gradient_class }} rounded-lg flex items-center justify-center text-white text-2xl mr-4">
|
|
{{ agent.icon }}
|
|
</div>
|
|
<div>
|
|
<h1 class="text-2xl font-bold text-gray-900">{{ agent.name }}</h1>
|
|
<p class="text-gray-600">{{ agent.description }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Agent-specific interfaces -->
|
|
{% if agent.slug == 'five-whys' %}
|
|
<!-- Advanced Chat Interface for 5 Whys -->
|
|
<div id="chatContainer" class="h-96 overflow-y-auto border border-gray-200 rounded-lg p-4 mb-4" style="background: linear-gradient(to bottom, #f9fafb, #ffffff);">
|
|
<div id="chatMessages">
|
|
<div class="mb-4 flex">
|
|
<div class="w-8 h-8 bg-gradient-to-r from-purple-500 to-blue-500 rounded-full flex items-center justify-center text-white text-sm mr-3 flex-shrink-0">
|
|
🤖
|
|
</div>
|
|
<div class="bg-gray-100 rounded-lg p-3 max-w-md">
|
|
<div class="markdown-content">
|
|
<p>👋 Welcome to the 5 Whys Root Cause Analysis!</p>
|
|
<p>I'll help you systematically analyze your problem using the proven 5 Whys methodology.</p>
|
|
<p><strong>To get started, please describe the problem you're experiencing.</strong></p>
|
|
<p>For example:</p>
|
|
<ul>
|
|
<li>"Our customer complaints increased by 40% this month"</li>
|
|
<li>"Production quality has decreased recently"</li>
|
|
<li>"Website performance is slower than usual"</li>
|
|
</ul>
|
|
<p>What problem would you like to analyze?</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex gap-2">
|
|
<input type="text" id="chatInput" placeholder="Describe your problem..." class="flex-1 p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent">
|
|
<button id="sendButton" class="px-6 py-3 bg-gradient-to-r from-purple-600 to-blue-600 text-white rounded-lg hover:from-purple-700 hover:to-blue-700 transition-all duration-300">
|
|
Send
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Report Display -->
|
|
<div id="reportSection" class="mt-6 hidden">
|
|
<h3 class="text-lg font-semibold mb-4">5 Whys Analysis Report</h3>
|
|
<div id="reportContent" class="bg-gray-50 p-4 rounded-lg"></div>
|
|
<div class="mt-4 flex gap-2">
|
|
<button onclick="copyReport()" class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">Copy Report</button>
|
|
<button onclick="downloadReport()" class="px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700">Download PDF</button>
|
|
</div>
|
|
</div>
|
|
|
|
{% elif agent.slug == 'data-analyzer' %}
|
|
<!-- File Upload Interface -->
|
|
<div class="border-2 border-dashed border-gray-300 rounded-lg p-6 text-center" id="fileDropZone">
|
|
<div class="mb-4">
|
|
<svg class="mx-auto h-12 w-12 text-gray-400" stroke="currentColor" fill="none" viewBox="0 0 48 48">
|
|
<path d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
</div>
|
|
<p class="text-lg text-gray-600 mb-2">Drop your file here or click to browse</p>
|
|
<p class="text-sm text-gray-500">Supports CSV, Excel, JSON files up to 10MB</p>
|
|
<input type="file" id="fileInput" accept=".csv,.xlsx,.xls,.json" class="hidden">
|
|
<button onclick="document.getElementById('fileInput').click()" class="mt-4 px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
|
|
Choose File
|
|
</button>
|
|
</div>
|
|
|
|
<div id="filePreview" class="mt-4 hidden">
|
|
<div class="bg-gray-50 p-4 rounded-lg">
|
|
<p class="font-medium">Selected File:</p>
|
|
<p id="fileName" class="text-gray-600"></p>
|
|
<p id="fileSize" class="text-sm text-gray-500"></p>
|
|
</div>
|
|
</div>
|
|
|
|
{% elif agent.slug == 'weather-reporter' %}
|
|
<div class="mb-4">
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Location</label>
|
|
<input type="text" name="location" id="locationInput" required class="w-full p-3 border border-gray-300 rounded-md focus:ring-2 focus:ring-purple-500" placeholder="Enter city name (e.g., Dubai, UAE)">
|
|
</div>
|
|
|
|
{% elif agent.slug == 'job-posting-generator' %}
|
|
<div class="space-y-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Job Title</label>
|
|
<input type="text" name="title" required class="w-full p-3 border border-gray-300 rounded-md">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Company</label>
|
|
<input type="text" name="company" required class="w-full p-3 border border-gray-300 rounded-md">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Job Description</label>
|
|
<textarea name="description" rows="4" required class="w-full p-3 border border-gray-300 rounded-md"></textarea>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Requirements</label>
|
|
<textarea name="requirements" rows="4" required class="w-full p-3 border border-gray-300 rounded-md"></textarea>
|
|
</div>
|
|
</div>
|
|
|
|
{% elif agent.slug == 'social-ads-generator' %}
|
|
<div class="space-y-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Product/Service</label>
|
|
<input type="text" name="product" required class="w-full p-3 border border-gray-300 rounded-md">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Platform</label>
|
|
<select name="platform" required class="w-full p-3 border border-gray-300 rounded-md">
|
|
<option value="facebook">Facebook</option>
|
|
<option value="instagram">Instagram</option>
|
|
<option value="twitter">Twitter</option>
|
|
<option value="linkedin">LinkedIn</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Target Audience</label>
|
|
<input type="text" name="target_audience" required class="w-full p-3 border border-gray-300 rounded-md">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Tone</label>
|
|
<select name="tone" required class="w-full p-3 border border-gray-300 rounded-md">
|
|
<option value="professional">Professional</option>
|
|
<option value="casual">Casual</option>
|
|
<option value="humorous">Humorous</option>
|
|
<option value="urgent">Urgent</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
{% elif agent.slug == 'faq-generator' %}
|
|
<div class="mb-4">
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Content Source</label>
|
|
<textarea name="content_source" rows="6" required class="w-full p-3 border border-gray-300 rounded-md" placeholder="Paste your content or URL here..."></textarea>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Processing Status -->
|
|
<div id="processingStatus" class="mt-6 hidden">
|
|
<div class="flex items-center justify-center p-6">
|
|
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-purple-600 mr-3"></div>
|
|
<span class="text-gray-600">Processing your request...</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Results Display -->
|
|
<div id="results" class="mt-6 hidden">
|
|
<h3 class="text-lg font-semibold mb-4">Results</h3>
|
|
<div id="resultsContent" class="bg-gray-50 p-4 rounded-md">
|
|
<!-- Results will be inserted here -->
|
|
</div>
|
|
<div class="mt-4 flex gap-2">
|
|
<button onclick="copyResults()" class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">Copy Results</button>
|
|
<button onclick="downloadResults()" class="px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700">Download</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sidebar -->
|
|
<div class="lg:col-span-1">
|
|
<!-- Wallet Balance Component -->
|
|
<div class="bg-white rounded-lg shadow-lg p-6 mb-6">
|
|
<h3 class="text-lg font-semibold mb-4">Cost</h3>
|
|
<div class="mb-4">
|
|
<div class="flex justify-between items-center mb-2">
|
|
<span class="text-gray-600">Current Balance:</span>
|
|
<span class="font-semibold">{{ user.wallet_balance }} AED</span>
|
|
</div>
|
|
<div class="flex justify-between items-center mb-2">
|
|
<span class="text-gray-600">Cost:</span>
|
|
<span class="font-semibold text-red-600">-{{ agent.price_display }}</span>
|
|
</div>
|
|
<div class="border-t pt-2">
|
|
<div class="flex justify-between items-center">
|
|
<span class="font-semibold">After Processing:</span>
|
|
<span class="font-semibold {% if has_sufficient_balance %}text-green-600{% else %}text-red-600{% endif %}">
|
|
{% if has_sufficient_balance %}
|
|
{{ user.wallet_balance|floatformat:2 }} AED
|
|
{% else %}
|
|
Insufficient Balance
|
|
{% endif %}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button id="processBtn" type="button"
|
|
class="w-full py-3 px-4 rounded-md font-semibold transition-colors
|
|
{% if has_sufficient_balance %}
|
|
bg-blue-600 text-white hover:bg-blue-700
|
|
{% else %}
|
|
bg-gray-300 text-gray-500 cursor-not-allowed
|
|
{% endif %}"
|
|
{% if not has_sufficient_balance %}disabled{% endif %}>
|
|
{% if has_sufficient_balance %}
|
|
Process Agent ({{ agent.price_display }})
|
|
{% else %}
|
|
Insufficient Balance
|
|
{% endif %}
|
|
</button>
|
|
|
|
{% if not has_sufficient_balance %}
|
|
<p class="text-center mt-4 text-sm">
|
|
<a href="{% url 'pricing' %}" class="text-blue-600 hover:text-blue-800 underline">
|
|
Top up wallet
|
|
</a> to use this agent
|
|
</p>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Agent Stats -->
|
|
<div class="bg-white rounded-lg shadow-lg p-6">
|
|
<h3 class="text-lg font-semibold mb-4">Agent Statistics</h3>
|
|
<div class="space-y-3">
|
|
<div class="flex justify-between">
|
|
<span class="text-gray-600">Rating:</span>
|
|
<div class="flex items-center">
|
|
<span class="text-yellow-400">★</span>
|
|
<span class="ml-1">{{ agent.rating }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span class="text-gray-600">Reviews:</span>
|
|
<span>{{ agent.review_count }}</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span class="text-gray-600">Category:</span>
|
|
<span class="capitalize">{{ agent.get_category_display }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Agent processing logic
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const agentSlug = '{{ agent.slug }}';
|
|
const processBtn = document.getElementById('processBtn');
|
|
const processingStatus = document.getElementById('processingStatus');
|
|
const results = document.getElementById('results');
|
|
const resultsContent = document.getElementById('resultsContent');
|
|
|
|
// File upload for data analyzer
|
|
if (agentSlug === 'data-analyzer') {
|
|
const fileInput = document.getElementById('fileInput');
|
|
const fileDropZone = document.getElementById('fileDropZone');
|
|
const filePreview = document.getElementById('filePreview');
|
|
const fileName = document.getElementById('fileName');
|
|
const fileSize = document.getElementById('fileSize');
|
|
|
|
fileInput.addEventListener('change', function(e) {
|
|
const file = e.target.files[0];
|
|
if (file) {
|
|
fileName.textContent = file.name;
|
|
fileSize.textContent = `${(file.size / 1024 / 1024).toFixed(2)} MB`;
|
|
filePreview.classList.remove('hidden');
|
|
}
|
|
});
|
|
|
|
// Drag and drop functionality
|
|
fileDropZone.addEventListener('dragover', function(e) {
|
|
e.preventDefault();
|
|
fileDropZone.classList.add('border-blue-400', 'bg-blue-50');
|
|
});
|
|
|
|
fileDropZone.addEventListener('dragleave', function(e) {
|
|
e.preventDefault();
|
|
fileDropZone.classList.remove('border-blue-400', 'bg-blue-50');
|
|
});
|
|
|
|
fileDropZone.addEventListener('drop', function(e) {
|
|
e.preventDefault();
|
|
fileDropZone.classList.remove('border-blue-400', 'bg-blue-50');
|
|
const files = e.dataTransfer.files;
|
|
if (files.length > 0) {
|
|
fileInput.files = files;
|
|
fileInput.dispatchEvent(new Event('change'));
|
|
}
|
|
});
|
|
}
|
|
|
|
// Chat functionality for 5 Whys
|
|
if (agentSlug === 'five-whys') {
|
|
const chatInput = document.getElementById('chatInput');
|
|
const sendButton = document.getElementById('sendButton');
|
|
const chatMessages = document.getElementById('chatMessages');
|
|
const chatContainer = document.getElementById('chatContainer');
|
|
|
|
function addMessage(content, sender = 'user') {
|
|
const messageDiv = document.createElement('div');
|
|
messageDiv.className = 'mb-4 flex';
|
|
|
|
if (sender === 'user') {
|
|
messageDiv.innerHTML = `
|
|
<div class="ml-auto flex">
|
|
<div class="bg-blue-600 text-white rounded-lg p-3 max-w-md">
|
|
${content}
|
|
</div>
|
|
<div class="w-8 h-8 bg-blue-600 rounded-full flex items-center justify-center text-white text-sm ml-3 flex-shrink-0">
|
|
👤
|
|
</div>
|
|
</div>
|
|
`;
|
|
} else {
|
|
messageDiv.innerHTML = `
|
|
<div class="w-8 h-8 bg-gradient-to-r from-purple-500 to-blue-500 rounded-full flex items-center justify-center text-white text-sm mr-3 flex-shrink-0">
|
|
🤖
|
|
</div>
|
|
<div class="bg-gray-100 rounded-lg p-3 max-w-md">
|
|
<div class="markdown-content">${content}</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
chatMessages.appendChild(messageDiv);
|
|
chatContainer.scrollTop = chatContainer.scrollHeight;
|
|
}
|
|
|
|
function sendMessage() {
|
|
const message = chatInput.value.trim();
|
|
if (!message) return;
|
|
|
|
addMessage(message, 'user');
|
|
chatInput.value = '';
|
|
|
|
// Here you would send the message to your Django backend
|
|
// For now, we'll simulate a response
|
|
setTimeout(() => {
|
|
addMessage('Thank you for sharing that problem. Let me ask you the first "Why" question...', 'bot');
|
|
}, 1000);
|
|
}
|
|
|
|
sendButton.addEventListener('click', sendMessage);
|
|
chatInput.addEventListener('keypress', function(e) {
|
|
if (e.key === 'Enter') {
|
|
sendMessage();
|
|
}
|
|
});
|
|
}
|
|
|
|
// Main process button functionality
|
|
processBtn.addEventListener('click', async function() {
|
|
if (processBtn.disabled) return;
|
|
|
|
processBtn.disabled = true;
|
|
processBtn.textContent = 'Processing...';
|
|
processingStatus.classList.remove('hidden');
|
|
|
|
try {
|
|
const formData = new FormData();
|
|
|
|
// Collect form data based on agent type
|
|
if (agentSlug === 'data-analyzer') {
|
|
const fileInput = document.getElementById('fileInput');
|
|
if (fileInput.files[0]) {
|
|
formData.append('file', fileInput.files[0]);
|
|
} else {
|
|
throw new Error('Please select a file');
|
|
}
|
|
} else if (agentSlug === 'weather-reporter') {
|
|
const location = document.getElementById('locationInput').value;
|
|
if (!location) throw new Error('Please enter a location');
|
|
formData.append('location', location);
|
|
} else {
|
|
// For other agents, collect all form inputs
|
|
const inputs = document.querySelectorAll('input[name], textarea[name], select[name]');
|
|
inputs.forEach(input => {
|
|
if (input.value) {
|
|
formData.append(input.name, input.value);
|
|
}
|
|
});
|
|
}
|
|
|
|
const response = await fetch(`{% url 'process_agent' agent.slug %}`, {
|
|
method: 'POST',
|
|
body: formData,
|
|
headers: {
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
|
|
}
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
// Display results
|
|
resultsContent.innerHTML = `<pre>${JSON.stringify(data.result, null, 2)}</pre>`;
|
|
results.classList.remove('hidden');
|
|
|
|
// Update balance display (you might want to refresh the page or update via AJAX)
|
|
location.reload();
|
|
} else {
|
|
alert('Error: ' + data.error);
|
|
}
|
|
} catch (error) {
|
|
alert('Error: ' + error.message);
|
|
} finally {
|
|
processBtn.disabled = false;
|
|
processBtn.textContent = 'Process Agent ({{ agent.price_display }})';
|
|
processingStatus.classList.add('hidden');
|
|
}
|
|
});
|
|
});
|
|
|
|
// Utility functions
|
|
function copyResults() {
|
|
const content = document.getElementById('resultsContent').textContent;
|
|
navigator.clipboard.writeText(content).then(() => {
|
|
alert('Results copied to clipboard!');
|
|
});
|
|
}
|
|
|
|
function downloadResults() {
|
|
const content = document.getElementById('resultsContent').textContent;
|
|
const blob = new Blob([content], { type: 'text/plain' });
|
|
const url = window.URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = '{{ agent.slug }}_results.txt';
|
|
a.click();
|
|
window.URL.revokeObjectURL(url);
|
|
}
|
|
|
|
function copyReport() {
|
|
const content = document.getElementById('reportContent').textContent;
|
|
navigator.clipboard.writeText(content).then(() => {
|
|
alert('Report copied to clipboard!');
|
|
});
|
|
}
|
|
|
|
function downloadReport() {
|
|
const content = document.getElementById('reportContent').textContent;
|
|
const blob = new Blob([content], { type: 'text/plain' });
|
|
const url = window.URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = 'five_whys_analysis_report.txt';
|
|
a.click();
|
|
window.URL.revokeObjectURL(url);
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.markdown-content ul {
|
|
list-style-type: disc;
|
|
margin-left: 20px;
|
|
margin-top: 8px;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.markdown-content li {
|
|
margin: 4px 0;
|
|
}
|
|
|
|
.markdown-content p {
|
|
margin: 8px 0;
|
|
}
|
|
|
|
.markdown-content strong {
|
|
font-weight: 600;
|
|
}
|
|
|
|
@keyframes spin {
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
|
|
.animate-spin {
|
|
animation: spin 1s linear infinite;
|
|
}
|
|
</style>
|
|
{% endblock %}
|