mirror of
https://github.com/thecyberlearn/quantum-ai-v3.git
synced 2026-08-18 09:52:58 +00:00
🎨 Add external service wrappers and social media previews
- External service wrapper system for JotForm, Zapier integrations - Simple template-based approach (iframe, landing, redirect) - Rich social media previews with Open Graph and Twitter Card tags - Professional social preview image for branded sharing - Updated documentation with usage examples 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
838927f316
commit
cae8d4a8fe
53
CLAUDE.md
53
CLAUDE.md
@ -212,6 +212,57 @@ The platform supports 2 agent types:
|
||||
- **Webhook Agents** - N8N integration with dynamic forms
|
||||
- **Direct Access Agents** - External forms (JotForm, etc.) with embedded interfaces
|
||||
|
||||
## External Service Wrappers
|
||||
|
||||
**Simple template-based system for event invitations, demos, and external forms:**
|
||||
|
||||
**Configuration:** Edit `EXTERNAL_PAGES` dict in `core/views.py`:
|
||||
```python
|
||||
EXTERNAL_PAGES = {
|
||||
'event-invitation': {
|
||||
'title': 'Event Registration',
|
||||
'external_url': 'https://form.jotform.com/252214924850455',
|
||||
'template': 'iframe', # iframe, landing, or redirect
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Templates Available:**
|
||||
- `templates/wrapper/iframe.html` - Full-screen iframe embed
|
||||
- `templates/wrapper/landing.html` - Branded landing page with embed
|
||||
- `templates/wrapper/redirect.html` - Auto-redirect with countdown
|
||||
|
||||
**Access:** `/{page-name}/` (e.g., `/event-invitation/`)
|
||||
|
||||
**Features:**
|
||||
- Rate limiting protection
|
||||
- Consistent branding
|
||||
- Mobile responsive
|
||||
- No database needed
|
||||
|
||||
## Social Media Integration
|
||||
|
||||
**Rich social media previews implemented in `templates/base.html`:**
|
||||
|
||||
**Open Graph Tags:**
|
||||
- `og:title` - Page title for social sharing
|
||||
- `og:description` - Page description
|
||||
- `og:image` - Preview image (`static/img/social-preview.png`)
|
||||
- `og:url` - Canonical page URL
|
||||
- `og:site_name` - "Quantum Tasks AI"
|
||||
|
||||
**Twitter Card Tags:**
|
||||
- `twitter:card` - Large image format
|
||||
- `twitter:title/description/image` - Twitter-specific metadata
|
||||
|
||||
**Custom Per-Page:** Override blocks in templates:
|
||||
```django
|
||||
{% block og_title %}Custom Page Title{% endblock %}
|
||||
{% block meta_description %}Custom description{% endblock %}
|
||||
```
|
||||
|
||||
**Result:** Rich previews on WhatsApp, Discord, Twitter, LinkedIn with branded image and professional descriptions.
|
||||
|
||||
## Production Deployment
|
||||
|
||||
**Railway Configuration:**
|
||||
@ -269,6 +320,8 @@ The platform supports 2 agent types:
|
||||
- **Direct Access Agents (4)**: CyberSec Career Navigator, AI Brand Strategist, Lean Six Sigma Expert, SWOT Analysis Expert
|
||||
|
||||
**Latest Changes:**
|
||||
- **External service wrapper system** - Simple template-based system for JotForm, Zapier, event invitations
|
||||
- **Social media meta tags** - Rich previews with Open Graph and Twitter Card tags
|
||||
- **View separation completed** - Split large views.py into focused modules (api, chat, web, direct access)
|
||||
- **Restored digital-branding.css** from git history with proper design system integration
|
||||
- **Added SWOT Analysis Expert** with proper category assignment (analysis)
|
||||
|
||||
16
agents/configs/agents/demo-assistant.json
Normal file
16
agents/configs/agents/demo-assistant.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"slug": "demo-assistant",
|
||||
"name": "Demo AI Assistant",
|
||||
"short_description": "Demo AI assistant for testing direct access agent functionality",
|
||||
"description": "A simple demo AI assistant that showcases the direct access agent system. Perfect for testing and demonstration purposes.",
|
||||
"category": "analysis",
|
||||
"price": 0.0,
|
||||
"agent_type": "form",
|
||||
"system_type": "direct_access",
|
||||
"form_schema": {
|
||||
"fields": []
|
||||
},
|
||||
"webhook_url": "https://agent.jotform.com/019865a942ab7fa5b5b743a5fd2abe09e345",
|
||||
"access_url_name": "agents:direct_access_handler",
|
||||
"display_url_name": "agents:direct_access_display"
|
||||
}
|
||||
@ -9,4 +9,7 @@ urlpatterns = [
|
||||
path('pricing/', views.pricing_view, name='pricing'),
|
||||
path('contact/', views.contact_form_view, name='contact_form'),
|
||||
path('health/', views.health_check_view, name='health_check'),
|
||||
|
||||
# External service wrapper pages
|
||||
path('<str:page_name>/', views.external_page_view, name='external_page'),
|
||||
]
|
||||
@ -1,7 +1,7 @@
|
||||
from django.shortcuts import render, redirect
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib import messages
|
||||
from django.http import JsonResponse
|
||||
from django.http import JsonResponse, Http404
|
||||
from django.core.mail import send_mail
|
||||
from django.conf import settings
|
||||
from django_ratelimit.decorators import ratelimit
|
||||
@ -282,3 +282,61 @@ def health_check_view(request):
|
||||
|
||||
# Always return 200 - we're healthy if Django is running
|
||||
return JsonResponse(health_data, status=200)
|
||||
|
||||
|
||||
# Simple external service wrapper configurations
|
||||
EXTERNAL_PAGES = {
|
||||
'event-invitation': {
|
||||
'title': 'Event Registration',
|
||||
'description': 'Register for our upcoming event',
|
||||
'external_url': 'https://form.jotform.com/252214924850455',
|
||||
'template': 'iframe', # iframe, landing, or redirect
|
||||
},
|
||||
'demo-form': {
|
||||
'title': 'Product Demo Request',
|
||||
'description': 'Schedule a personalized demo of our platform',
|
||||
'external_url': 'https://calendly.com/your-demo-link',
|
||||
'template': 'landing',
|
||||
},
|
||||
'consultation': {
|
||||
'title': 'Free Consultation',
|
||||
'external_url': 'https://www.jotform.com/consultation-form',
|
||||
'template': 'redirect',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ratelimit(key='ip', rate='30/m', method='GET', block=False)
|
||||
def external_page_view(request, page_name):
|
||||
"""
|
||||
Simple external service wrapper view.
|
||||
Just renders templates with external URLs - no database needed.
|
||||
"""
|
||||
# Check rate limiting
|
||||
if getattr(request, 'limited', False):
|
||||
logger.warning(f"External page rate limit exceeded for IP {request.META.get('REMOTE_ADDR')}")
|
||||
messages.warning(request, 'Too many requests. Please wait a moment.')
|
||||
return redirect('core:homepage')
|
||||
|
||||
# Get page config
|
||||
page_config = EXTERNAL_PAGES.get(page_name)
|
||||
if not page_config:
|
||||
raise Http404("Page not found")
|
||||
|
||||
# Choose template
|
||||
template_map = {
|
||||
'iframe': 'wrapper/iframe.html',
|
||||
'landing': 'wrapper/landing.html',
|
||||
'redirect': 'wrapper/redirect.html',
|
||||
}
|
||||
|
||||
template_name = template_map.get(page_config['template'], 'wrapper/iframe.html')
|
||||
|
||||
context = {
|
||||
'page_title': page_config['title'],
|
||||
'page_description': page_config.get('description', ''),
|
||||
'external_url': page_config['external_url'],
|
||||
'user_balance': request.user.wallet_balance if request.user.is_authenticated else 0,
|
||||
}
|
||||
|
||||
return render(request, template_name, context)
|
||||
|
||||
@ -156,25 +156,29 @@ git push
|
||||
- **Zero URL Configuration**: Dynamic routing based on JSON config properties
|
||||
- **Zero Templates**: Single generic template works for all direct access agents
|
||||
- **Zero Database Setup**: Pure file-based loading with intelligent caching
|
||||
- **Zero Error Handling**: Automatic webhook error detection and user feedback
|
||||
|
||||
### Agent Type Handling
|
||||
- **Webhook Agents**: Automatically generate dynamic forms from `form_schema`
|
||||
- **Direct Access Agents**: Automatically handle payment processing + external redirect
|
||||
- **Both Types**: Work with only JSON configuration, no additional code
|
||||
- **Error Handling**: Automatically detects webhook failures (OpenAI quota, N8N issues, timeouts) and shows user-friendly messages
|
||||
|
||||
### Development Workflow Comparison
|
||||
```bash
|
||||
# ❌ Old Complex Way (5+ steps)
|
||||
# ❌ Old Complex Way (6+ steps)
|
||||
1. Create JSON config
|
||||
2. Write Python view functions
|
||||
3. Add URL routes
|
||||
4. Create HTML templates
|
||||
5. Update view imports
|
||||
6. Test and debug
|
||||
6. Write error handling code
|
||||
7. Test and debug
|
||||
|
||||
# ✅ New Simple Way (1 step)
|
||||
1. Create JSON config
|
||||
# Done! Everything else is automatic 🎉
|
||||
# - Forms, routing, error handling, user feedback all automated
|
||||
```
|
||||
|
||||
### How Both Agent Types Work Now
|
||||
@ -182,6 +186,7 @@ git push
|
||||
**Webhook Agents:**
|
||||
- JSON config → Dynamic form via `agent_detail_view`
|
||||
- Form submission → N8N webhook → Results display
|
||||
- Automatic error detection and user feedback
|
||||
- No individual Python code needed
|
||||
|
||||
**Direct Access Agents:**
|
||||
@ -189,6 +194,45 @@ git push
|
||||
- Payment → Generic iframe display
|
||||
- No individual Python code needed
|
||||
|
||||
## Automatic Error Handling 🛡️
|
||||
|
||||
The platform now includes **completely automated error handling** for all agents:
|
||||
|
||||
### What's Automatically Handled
|
||||
- **N8N Webhook Failures**: Service down, timeouts, configuration errors
|
||||
- **AI Service Limits**: OpenAI quota exceeded, rate limits, API errors
|
||||
- **Network Issues**: Connection failures, DNS problems, timeouts
|
||||
- **Invalid Responses**: Malformed data, unexpected formats
|
||||
|
||||
### User Experience
|
||||
- **Clear Error Messages**: "Agent is temporarily unavailable. Please try again later."
|
||||
- **Persistent Display**: Error shown in results area (won't disappear like notifications)
|
||||
- **No Technical Jargon**: Simple, friendly language instead of HTTP status codes
|
||||
|
||||
### Developer Benefits
|
||||
- **Zero Configuration**: No error handling code needed in JSON configs
|
||||
- **Automatic Detection**: System distinguishes between success and various failure types
|
||||
- **Consistent UX**: All agents have identical error handling behavior
|
||||
- **Debug Friendly**: Technical errors still logged to console for troubleshooting
|
||||
|
||||
### Examples of Handled Errors
|
||||
```json
|
||||
// N8N Response (OpenAI quota exceeded)
|
||||
{
|
||||
"errorMessage": "You exceeded your current quota",
|
||||
"errorDetails": {"httpCode": "429"}
|
||||
}
|
||||
// → User sees: "Agent is temporarily unavailable. Please try again later."
|
||||
|
||||
// HTTP 500 from webhook
|
||||
// → User sees: "Agent is temporarily unavailable. Please try again later."
|
||||
|
||||
// Connection timeout
|
||||
// → User sees: "Agent is temporarily unavailable. Please try again later."
|
||||
```
|
||||
|
||||
All technical details are logged for debugging, but users always see the same friendly message.
|
||||
|
||||
## Custom Integration (Advanced)
|
||||
|
||||
For agents needing custom behavior, add views to appropriate modules:
|
||||
@ -229,4 +273,4 @@ Then add URL routes in `agents/urls.py` and update marketplace template if neede
|
||||
|
||||
---
|
||||
|
||||
*Last updated: 2025-01-14*
|
||||
*Last updated: 2025-01-15*
|
||||
1
static/img/social-preview.png
Normal file
1
static/img/social-preview.png
Normal file
File diff suppressed because one or more lines are too long
@ -6,6 +6,25 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}Quantum Tasks AI - AI Platform{% endblock %}</title>
|
||||
|
||||
<!-- SEO Meta Tags -->
|
||||
<meta name="description" content="{% block meta_description %}Quantum Tasks AI - Advanced AI platform for automation and intelligent task management. Access powerful AI agents for your business needs.{% endblock %}">
|
||||
<meta name="keywords" content="AI, artificial intelligence, automation, task management, AI agents, quantum computing">
|
||||
<meta name="author" content="Quantum Tasks AI">
|
||||
|
||||
<!-- Open Graph Meta Tags for Social Media -->
|
||||
<meta property="og:title" content="{% block og_title %}{% block title %}Quantum Tasks AI - AI Platform{% endblock %}{% endblock %}">
|
||||
<meta property="og:description" content="{% block og_description %}{% block meta_description %}Quantum Tasks AI - Advanced AI platform for automation and intelligent task management. Access powerful AI agents for your business needs.{% endblock %}{% endblock %}">
|
||||
<meta property="og:image" content="{% block og_image %}{% static 'img/social-preview.png' %}{% endblock %}">
|
||||
<meta property="og:url" content="{% block og_url %}{{ request.build_absolute_uri }}{% endblock %}">
|
||||
<meta property="og:type" content="{% block og_type %}website{% endblock %}">
|
||||
<meta property="og:site_name" content="Quantum Tasks AI">
|
||||
|
||||
<!-- Twitter Card Meta Tags -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="{% block twitter_title %}{% block og_title %}{% block title %}Quantum Tasks AI - AI Platform{% endblock %}{% endblock %}{% endblock %}">
|
||||
<meta name="twitter:description" content="{% block twitter_description %}{% block og_description %}{% block meta_description %}Quantum Tasks AI - Advanced AI platform for automation and intelligent task management. Access powerful AI agents for your business needs.{% endblock %}{% endblock %}{% endblock %}">
|
||||
<meta name="twitter:image" content="{% block twitter_image %}{% block og_image %}{% static 'img/social-preview.png' %}{% endblock %}{% endblock %}">
|
||||
|
||||
<!-- Unified Font Loading - Single Source of Truth -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
|
||||
42
templates/wrapper/iframe.html
Normal file
42
templates/wrapper/iframe.html
Normal file
@ -0,0 +1,42 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}{{ page_title|default:"External Service" }} - Quantum Tasks AI{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
/* Full-width iframe styling */
|
||||
.main-container {
|
||||
max-width: none;
|
||||
padding: 0;
|
||||
height: calc(100vh - 80px);
|
||||
}
|
||||
|
||||
.iframe-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.iframe-container iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Hide footer for full-screen */
|
||||
.footer {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="iframe-container">
|
||||
<iframe
|
||||
src="{{ external_url }}"
|
||||
frameborder="0"
|
||||
scrolling="auto"
|
||||
title="{{ page_title|default:'External Service' }}">
|
||||
</iframe>
|
||||
</div>
|
||||
{% endblock %}
|
||||
68
templates/wrapper/landing.html
Normal file
68
templates/wrapper/landing.html
Normal file
@ -0,0 +1,68 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}{{ page_title|default:"External Service" }} - Quantum Tasks AI{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
.landing-page {
|
||||
min-height: 80vh;
|
||||
padding: 2rem 0;
|
||||
}
|
||||
|
||||
.landing-header {
|
||||
text-align: center;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.landing-title {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
color: #1a1a1a;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.landing-description {
|
||||
font-size: 1.25rem;
|
||||
color: #666;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.external-service-embed {
|
||||
width: 100%;
|
||||
min-height: 600px;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.external-service-embed iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="landing-page">
|
||||
<div class="container">
|
||||
<div class="landing-header">
|
||||
<h1 class="landing-title">{{ page_title|default:"External Service" }}</h1>
|
||||
{% if page_description %}
|
||||
<p class="landing-description">{{ page_description }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="external-service-embed">
|
||||
<iframe
|
||||
src="{{ external_url }}"
|
||||
frameborder="0"
|
||||
scrolling="auto"
|
||||
title="{{ page_title|default:'External Service' }}">
|
||||
</iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
105
templates/wrapper/redirect.html
Normal file
105
templates/wrapper/redirect.html
Normal file
@ -0,0 +1,105 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Redirecting to {{ page_title|default:"External Service" }} - Quantum Tasks AI{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
.redirect-page {
|
||||
min-height: 80vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.redirect-content {
|
||||
max-width: 600px;
|
||||
padding: 3rem 2rem;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.redirect-title {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
color: #1a1a1a;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.redirect-timer {
|
||||
font-size: 1.25rem;
|
||||
color: #667eea;
|
||||
font-weight: 600;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.redirect-button {
|
||||
display: inline-block;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 1rem 2rem;
|
||||
border-radius: 8px;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
.redirect-button:hover {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
border: 3px solid #f3f3f3;
|
||||
border-top: 3px solid #667eea;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 0 auto 1rem;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="redirect-page">
|
||||
<div class="redirect-content">
|
||||
<div class="spinner"></div>
|
||||
<h1 class="redirect-title">Redirecting to {{ page_title|default:"External Service" }}</h1>
|
||||
|
||||
<div class="redirect-timer">
|
||||
Redirecting in <span id="countdown">3</span> seconds...
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<a href="{{ external_url }}" class="redirect-button">
|
||||
Continue Now
|
||||
</a>
|
||||
<a href="{% url 'core:homepage' %}" class="redirect-button" style="background: #6c757d;">
|
||||
Go Back Home
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let countdown = 3;
|
||||
const countdownElement = document.getElementById('countdown');
|
||||
|
||||
const timer = setInterval(() => {
|
||||
countdown--;
|
||||
countdownElement.textContent = countdown;
|
||||
|
||||
if (countdown <= 0) {
|
||||
clearInterval(timer);
|
||||
window.location.href = "{{ external_url }}";
|
||||
}
|
||||
}, 1000);
|
||||
</script>
|
||||
{% endblock %}
|
||||
Loading…
Reference in New Issue
Block a user