quantum-ai/docs/agent-template
Claude b072e2e1ee Fix Weather Reporter connection errors and restore API-based pattern
Major fixes:
- Weather Reporter: Restore correct API-based processing pattern
  * Fix form submission URL to use /agents/weather-reporter/process/
  * Remove incorrect polling logic (API agents return immediate results)
  * Fix response field mapping to use formatted_report instead of analysis_text
  * Implement immediate response display without polling

- Agent CSS System: Enhance unified styling system
  * Add missing CSS classes for status display (.status-title, .status-subtitle)
  * Add enhanced typography for results content formatting
  * Include modern info boxes (.key-points, .insights, .summary)
  * Fix animation keyframes for proper loading states

- Template Consistency: Update agents to use shared components
  * Job Posting Generator: Add cache-busted CSS and shared header/panel
  * Social Ads Generator: Add unified CSS link and shared components
  * Create component templates for consistent agent layouts

Key architectural insight: Weather Reporter is API-based (immediate response)
while Data Analyzer is webhook-based (async polling). Fixed Weather Reporter
to use correct pattern based on git history commit 82fc051.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-20 09:13:48 +05:30

575 lines
13 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Perfect! Let's proceed with **inline code generation** for manual copying. Here's how we'll break it down:
---
## ✅ Step 1: Unified Base Template — `base_agent.html`
Create this file in your templates folder:
```html
{% extends "base.html" %}
{% load static %}
{% block title %}{{ agent_title }} - NetCop AI Hub{% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="{% static 'css/theme.css' %}">
{% endblock %}
{% block content %}
<div class="agent-page">
<div class="agent-container">
{% include "components/agent_header.html" %}
{% block agent_main %}{% endblock %}
{% include "components/processing_status.html" %}
{% include "components/results_block.html" %}
{% include "components/quick_agents_panel.html" %}
</div>
</div>
{% endblock %}
```
---
## 🧱 Step 2: Agent Header Component — `components/agent_header.html`
```html
<div class="agent-header">
<div>
<h1 class="agent-title">{{ agent_title }}</h1>
<p class="agent-subtitle">{{ agent_subtitle }}</p>
</div>
<div class="header-controls">
{% include "components/wallet_card.html" %}
</div>
</div>
```
---
## 💳 Step 3: Wallet Card Component — `components/wallet_card.html`
```html
<div class="wallet-card widget-small" style="margin-bottom: 0;">
<div class="wallet-header">
<h3 class="wallet-title">Your Wallet</h3>
<div class="wallet-icon">💳</div>
</div>
<div class="balance-display">
<div class="balance-amount">
<span id="walletBalance">{{ user.wallet_balance|floatformat:2 }}</span> AED
</div>
<div class="balance-label">Available Balance</div>
</div>
</div>
```
---
## 🧭 Step 4: Quick Agents Panel — `components/quick_agents_panel.html`
```html
<div class="quick-agents-overlay" id="quickAgentsOverlay" onclick="closeQuickAgents()" aria-hidden="true"></div>
<div class="quick-agents-panel" id="quickAgentsPanel" role="dialog" aria-labelledby="quickAgentsTitle" aria-hidden="true">
<div class="quick-agents-header">
<h3 id="quickAgentsTitle">Quick Access to Other Agents</h3>
<button class="close-panel" onclick="toggleQuickAgents()" aria-label="Close quick agents panel">×</button>
</div>
<div class="quick-agents-grid">
<a href="/agents/data-analyzer/" class="quick-agent-card">
<div class="agent-icon">📊</div>
<div class="agent-info">
<h4>Data Analyzer</h4>
<p>AI-powered data analysis</p>
<span class="agent-price">5.0 AED</span>
</div>
</a>
<a href="/agents/weather-reporter/" class="quick-agent-card">
<div class="agent-icon">🌤️</div>
<div class="agent-info">
<h4>Weather Reporter</h4>
<p>Worldwide forecasts</p>
<span class="agent-price">2.0 AED</span>
</div>
</a>
<a href="/agents/job-posting-generator/" class="quick-agent-card">
<div class="agent-icon">💼</div>
<div class="agent-info">
<h4>Job Posting Generator</h4>
<p>Create job posts</p>
<span class="agent-price">3.0 AED</span>
</div>
</a>
</div>
<div class="quick-agents-footer">
<a href="{% url 'core:marketplace' %}" class="view-all-agents">View All Agents →</a>
</div>
</div>
```
---
## 📊 Step 5: Results Block — `components/results_block.html`
```html
<div class="agent-widget widget-wide results-container" id="resultsContainer" style="display: none;">
<div class="widget-header">
<h3 class="widget-title">
<span class="widget-icon">📊</span>
Analysis Results
</h3>
<span class="status-badge">Success</span>
</div>
<div class="widget-content">
<div class="results-content" id="resultsContent"></div>
<div class="results-actions">
<button onclick="copyResults()" class="btn btn-primary">📋 Copy</button>
<button onclick="downloadResults()" class="btn btn-secondary">💾 Download</button>
</div>
</div>
</div>
```
---
## ⏳ Step 6: Processing Status — `components/processing_status.html`
```html
<div id="processingStatus" class="agent-widget widget-wide processing-status">
<div class="widget-header">
<h3 class="widget-title">
<span class="widget-icon">⏳</span>
Processing
</h3>
</div>
<div class="widget-content" style="text-align: center;">
<div class="status-icon">⏳</div>
<div class="status-title">Working on your request...</div>
<div class="status-subtitle" id="statusText">Please wait...</div>
</div>
</div>
```
---
## Step 7: How It Works Widget — `components/how_it_works_widget.html`
```html
{% if steps == "data" %}
<ol class="info-list">
<li>Upload your data file</li>
<li>Choose analysis type</li>
<li>Get AI-powered insights</li>
<li>Copy or download results</li>
</ol>
{% elif steps == "weather" %}
<ol class="info-list">
<li>Enter any city name worldwide</li>
<li>Choose your preferred report type</li>
<li>Get real-time weather data</li>
<li>Copy or download detailed reports</li>
</ol>
{% endif %}
<button class="quick-agent-toggle btn btn-secondary btn-full" onclick="toggleQuickAgents()"
title="Quick access to other agents">
<span class="toggle-icon">🚀</span>
<span class="toggle-text">Explore Other Agents</span>
</button>
```
---
## 📁 Optional Folder Structure
```
templates/
├── base_agent.html
├── agents/
│ ├── data_analyzer.html
│ └── weather_reporter.html
├── components/
│ ├── agent_header.html
│ ├── wallet_card.html
│ ├── quick_agents_panel.html
│ ├── results_block.html
│ ├── processing_status.html
│ └── how_it_works_widget.html
```
---
Would you like me to now generate the unified `theme.css`? Or do you want to manually merge the CSS too?
Here is your **unified `theme.css`** for both the Data Analyzer and Weather Reporter agents. This file consolidates colors, typography, layout, buttons, forms, radio buttons, result boxes, and responsive utilities.
---
## ✅ `static/css/theme.css`
```css
/* ========== NetCop Unified Theme ========== */
/* Fonts */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
* {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
box-sizing: border-box;
}
/* ========== Root Variables ========== */
:root {
/* Colors */
--primary: #1a1a1a;
--primary-variant: #2d2d2d;
--background: #ffffff;
--surface: #ffffff;
--surface-variant: #f7fafc;
--outline: #e2e8f0;
--outline-variant: #cbd5e0;
--on-surface: #1a202c;
--on-surface-variant: #4a5568;
--success: #38a169;
--error: #e53e3e;
--warning: #d69e2e;
/* Spacing */
--spacing-xs: 4px;
--spacing-sm: 8px;
--spacing-md: 16px;
--spacing-lg: 24px;
--spacing-xl: 32px;
--spacing-2xl: 48px;
/* Radius */
--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 16px;
/* Shadows */
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 20px rgba(0, 0, 0, 0.15);
}
/* ========== Layout ========== */
body {
background: var(--background);
color: var(--on-surface);
margin: 0;
padding: 0;
line-height: 1.6;
}
/* Utility Classes */
.text-center {
text-align: center;
}
.flex {
display: flex;
}
.flex-col {
flex-direction: column;
}
.flex-row {
flex-direction: row;
}
.gap-md {
gap: var(--spacing-md);
}
/* ========== Agent Container ========== */
.agent-page {
min-height: 100vh;
padding: var(--spacing-lg) 0;
}
.agent-container {
max-width: 1600px;
margin: 0 auto;
padding: 0 var(--spacing-lg);
}
/* ========== Header ========== */
.agent-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--spacing-xl);
}
.agent-title {
font-size: 32px;
font-weight: 700;
letter-spacing: -0.5px;
}
.agent-subtitle {
font-size: 16px;
color: var(--on-surface-variant);
}
/* ========== Wallet Card ========== */
.wallet-card {
background: linear-gradient(135deg, #000000 0%, #333333 100%);
color: white;
border-radius: var(--radius-md);
padding: var(--spacing-md);
position: relative;
box-shadow: var(--shadow-md);
}
.wallet-card::before {
content: '';
position: absolute;
top: 0;
right: 0;
width: 100px;
height: 100px;
background: rgba(255, 255, 255, 0.1);
border-radius: 50%;
transform: translate(40px, -40px);
}
.wallet-header {
display: flex;
justify-content: space-between;
margin-bottom: var(--spacing-sm);
}
.wallet-title {
font-size: 14px;
font-weight: 600;
opacity: 0.9;
}
.wallet-icon {
font-size: 20px;
}
.balance-amount {
font-size: 24px;
font-weight: 700;
letter-spacing: -0.5px;
}
.balance-label {
font-size: 12px;
opacity: 0.8;
}
/* ========== Widget ========== */
.agent-widget {
background: var(--surface);
border: 1px solid var(--outline);
border-radius: var(--radius-lg);
padding: var(--spacing-xl);
box-shadow: var(--shadow-sm);
}
.widget-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--spacing-lg);
border-bottom: 1px solid var(--outline-variant);
padding-bottom: var(--spacing-md);
}
.widget-title {
font-size: 18px;
font-weight: 600;
display: flex;
align-items: center;
gap: var(--spacing-sm);
}
.widget-icon {
font-size: 20px;
padding: 6px;
border-radius: var(--radius-sm);
background: var(--surface-variant);
}
/* ========== Buttons ========== */
.btn {
padding: 12px 24px;
font-size: 14px;
font-weight: 600;
border-radius: var(--radius-md);
display: inline-flex;
align-items: center;
justify-content: center;
text-decoration: none;
cursor: pointer;
border: 1px solid transparent;
transition: all 0.2s ease;
min-height: 44px;
}
.btn-primary {
background: var(--primary);
color: white;
}
.btn-primary:hover {
background: var(--primary-variant);
}
.btn-secondary {
background: var(--surface);
color: var(--on-surface);
border: 2px solid var(--outline);
}
.btn-secondary:hover {
background: var(--surface-variant);
}
.btn-full {
width: 100%;
}
/* ========== Form Elements ========== */
.form-group {
margin-bottom: var(--spacing-lg);
}
.form-label {
font-size: 14px;
font-weight: 500;
margin-bottom: var(--spacing-sm);
display: block;
}
.form-input,
.form-textarea {
width: 100%;
padding: 12px 16px;
border: 2px solid var(--outline-variant);
border-radius: var(--radius-md);
background: var(--surface);
font-size: 14px;
transition: all 0.2s ease;
}
.form-input:focus,
.form-textarea:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.1);
}
.form-textarea {
resize: vertical;
min-height: 120px;
}
.form-help {
font-size: 12px;
color: var(--on-surface-variant);
}
.form-error {
font-size: 12px;
color: var(--error);
}
/* ========== Radio Buttons ========== */
.radio-grid {
display: grid;
gap: var(--spacing-md);
}
.radio-option,
.radio-card {
display: flex;
align-items: center;
gap: var(--spacing-md);
padding: var(--spacing-md);
border: 2px solid var(--outline-variant);
border-radius: var(--radius-md);
cursor: pointer;
background: var(--surface);
transition: all 0.2s ease;
}
.radio-option:hover,
.radio-card:hover {
border-color: var(--primary);
background: var(--surface-variant);
}
.radio-option.selected,
.radio-card.selected {
border-color: var(--primary);
}
/* ========== Results ========== */
.results-container {
background: var(--surface);
border: 1px solid var(--outline-variant);
border-radius: var(--radius-lg);
padding: var(--spacing-xl);
}
.results-content {
background: var(--surface-variant);
padding: var(--spacing-xl);
border-radius: var(--radius-md);
font-size: 15px;
line-height: 1.7;
}
.results-content h1,
.results-content h2,
.results-content h3 {
font-weight: 700;
color: var(--primary);
}
.results-content h1 {
font-size: 24px;
border-bottom: 3px solid var(--primary);
padding-bottom: var(--spacing-sm);
margin-bottom: var(--spacing-lg);
}
.results-content h2 {
font-size: 20px;
position: relative;
padding-left: var(--spacing-md);
}
.results-content h2::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
background: var(--primary);
}
.results-content h3 {
font-size: 18px;
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
border-left: 4px solid var(--primary);
padding: var(--spacing-md);
}
/* ========== Responsive ========== */
@media (max-width: 768px) {
.agent-header {
flex-direction: column;
align-items: flex-start;
gap: var(--spacing-sm);
}
.agent-title {
font-size: 24px;
}
.wallet-card {
padding: var(--spacing-lg);
}
.balance-amount {
font-size: 28px;
}
}
```
---
## ✅ What's Next?
* Place this file at: `static/css/theme.css`
* Link it in all agent templates using `{% static %}`
* Remove embedded `<style>` blocks from individual templates
---
Would you like me to now refactor either `data_analyzer.html` or `weather_reporter.html` to use this new theme and component system?