Compare commits

..

No commits in common. "main" and "v1.0-working" have entirely different histories.

62 changed files with 663 additions and 14988 deletions

View File

@ -4,10 +4,6 @@ DEBUG=True
ALLOWED_HOSTS=localhost,127.0.0.1,your-domain.com ALLOWED_HOSTS=localhost,127.0.0.1,your-domain.com
CSRF_TRUSTED_ORIGINS=http://localhost:8000,http://127.0.0.1:8000 CSRF_TRUSTED_ORIGINS=http://localhost:8000,http://127.0.0.1:8000
# Email Verification
# Set to False to bypass email verification for testing (until final domain is ready)
REQUIRE_EMAIL_VERIFICATION=True
# Database Configuration # Database Configuration
# Default: SQLite (simple, reliable, no setup required) # Default: SQLite (simple, reliable, no setup required)
# Railway: Automatically uses PostgreSQL via DATABASE_URL # Railway: Automatically uses PostgreSQL via DATABASE_URL

156
CLAUDE.md
View File

@ -4,15 +4,13 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Project Overview ## Project Overview
Quantum Tasks AI is a Django-based AI agent marketplace platform. Users can access AI agent services through a web interface, with execution handled via two distinct systems: N8N webhook integrations and direct form access integrations. Quantum Tasks AI is a Django-based AI agent marketplace platform. Users can purchase AI agent services through a web interface, with agent execution handled via N8N webhooks and payments processed through Stripe.
**Key Architecture:** **Key Architecture:**
- **Django Framework**: Main web application using Django 5.2.4 - **Django Framework**: Main web application using Django 5.2.4
- **Agent System**: Database-driven agents app with dual integration systems: - **Agent System**: Database-driven agents app with marketplace and N8N webhook execution
- **Webhook Agents**: N8N integrations for complex processing
- **Direct Access Agents**: Form-based integrations (JotForm, etc.)
- **Authentication**: Custom user model with email verification - **Authentication**: Custom user model with email verification
- **Payments**: Stripe integration with wallet system (supports free agents) - **Payments**: Stripe integration with wallet system
- **Database**: SQLite for development, PostgreSQL for production (Railway) - **Database**: SQLite for development, PostgreSQL for production (Railway)
- **Static Files**: WhiteNoise for production static file serving - **Static Files**: WhiteNoise for production static file serving
@ -103,27 +101,17 @@ gunicorn netcop_hub.wsgi:application
### Agent System (agents app) ### Agent System (agents app)
**Key Files:** **Key Files:**
- `agents/models.py`: Agent, AgentCategory, AgentExecution, ChatSession models - `agents/models.py`: Agent, AgentCategory, AgentExecution models
- `agents/views.py`: Dual integration systems and web interface views - `agents/views.py`: REST API and web interface views
- `agents/templates/agents/`: Dynamic agent templates and marketplace - `agents/templates/agents/`: Dynamic agent templates with form generation
- `agents/management/commands/`: Agent creation and management commands - `agents/management/commands/`: Agent creation and management commands
- `templates/career_navigator.html`: Direct access form template
**Dual Integration Systems:** **Agent Flow:**
**System 1: Webhook Agents (N8N Integration)**
1. User browses marketplace (`/agents/`) 1. User browses marketplace (`/agents/`)
2. Clicks "Try Now" → Agent detail page (`/agents/{slug}/`) 2. Selects agent and fills dynamic form (`/agents/{slug}/`)
3. Fills dynamic form → Form submission calls `/agents/api/execute/` 3. Form submission creates AgentExecution and calls N8N webhook
4. N8N webhook processes request and returns response 4. N8N processes request and returns response via webhook
5. Results displayed with file upload support 5. Results displayed with file upload support and real-time wallet updates
**System 2: Direct Access Agents (Form Integration)**
1. User browses marketplace (`/agents/`)
2. Clicks special "Try Now" button → Direct access (`/agents/{slug}/access/`)
3. Payment processed → Redirect to form page (`/agents/{slug}/`)
4. Form displays embedded interface (JotForm, etc.)
5. User interacts directly with external form system
### Database Models ### Database Models
**User Management:** **User Management:**
@ -148,21 +136,30 @@ gunicorn netcop_hub.wsgi:application
- `STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET`: Stripe API keys - `STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET`: Stripe API keys
- `DATABASE_URL`: PostgreSQL connection string (Railway) - `DATABASE_URL`: PostgreSQL connection string (Railway)
**Current System:** **N8N Webhook URLs:**
The platform supports **8 total agents** across **6 categories**: Agent-specific webhook URLs are stored in the database with each agent. Current working agents (all tested and confirmed working):
- **4 Webhook Agents** (N8N integration): Social Ads Generator, Job Posting Generator, PDF Summarizer, 5 Whys Analyzer
- **4 Direct Access Agents** (External forms): CyberSec Career Navigator, AI Brand Strategist, Lean Six Sigma Expert, SWOT Analysis Expert
For detailed agent information and creation instructions, see `docs/AGENT_CREATION.md`. 1. **Social Ads Generator** (social-ads-generator) - 6.00 AED
- Creates compelling social media advertisements
- Form fields: description, social_platform, include_emoji, language
- Webhook: N8N endpoint for social media ad generation
2. **Job Posting Generator** (job-posting-generator) - 10.00 AED
- Creates professional job postings
- Form fields: job_title, company_name, job_description, seniority_level, contract_type, location, language
- Webhook: N8N endpoint for job posting generation
3. **PDF Summarizer** (pdf-summarizer) - 8.00 AED
- Analyzes and summarizes PDF documents with file upload
- Form fields: pdf_file (file upload with drag-and-drop), summary_type
- Webhook: N8N endpoint for PDF processing with multipart file support
### URL Structure ### URL Structure
``` ```
/ # Homepage (core app) / # Homepage (core app)
/digital-branding/ # Digital branding services page
/auth/ # Authentication (login, register, etc.) /auth/ # Authentication (login, register, etc.)
/agents/ # Agent marketplace (agents app) /agents/ # Agent marketplace (agents app)
/agents/{slug}/ # Individual agent pages (webhook agents) /agents/{slug}/ # Individual agent pages
/agents/{slug}/access/ # Direct access agent payment processing
/wallet/ # Wallet management /wallet/ # Wallet management
/admin/ # Django admin /admin/ # Django admin
``` ```
@ -180,16 +177,53 @@ For detailed agent information and creation instructions, see `docs/AGENT_CREATI
## Adding New Agents ## Adding New Agents
For comprehensive agent creation instructions, see **`docs/AGENT_CREATION.md`**. 1. **Create management command** (recommended approach):
```python
# agents/management/commands/create_new_agent.py
from django.core.management.base import BaseCommand
from agents.models import AgentCategory, Agent
**Quick Summary:** class Command(BaseCommand):
1. Create JSON config in `agents/configs/agents/your-agent-name.json` def handle(self, *args, **options):
2. Run `python manage.py populate_agents` category, _ = AgentCategory.objects.get_or_create(
3. Agent appears in marketplace automatically slug='category-slug',
defaults={'name': 'Category Name', 'icon': '🤖'}
)
The platform supports 2 agent types: Agent.objects.get_or_create(
- **Webhook Agents** - N8N integration with dynamic forms slug='agent-slug',
- **Direct Access Agents** - External forms (JotForm, etc.) with embedded interfaces defaults={
'name': 'Agent Name',
'short_description': 'Brief description',
'description': 'Full description',
'category': category,
'price': 10.0,
'form_schema': {
'fields': [
{
'name': 'input_field',
'type': 'text',
'label': 'Input Field',
'required': True
}
]
},
'webhook_url': 'http://your-n8n-webhook-url'
}
)
```
2. **Run the command**: `python manage.py create_new_agent`
3. **Update N8N workflow** to handle the new agent
4. **Agent will automatically appear** in marketplace with dynamic form generation
**Supported Form Field Types:**
- `text`: Text input
- `textarea`: Multi-line text
- `select`: Dropdown with options
- `file`: File upload with drag-and-drop
- `url`: URL input with validation
- `checkbox`: Boolean checkbox
## Production Deployment ## Production Deployment
@ -236,39 +270,23 @@ The platform supports 2 agent types:
## System Status ## System Status
**Current Status: ✅ STABLE COMPREHENSIVE SYSTEM** **Current Status: ✅ STABLE WORKING SYSTEM**
- **8 agents** confirmed working and tested (4 webhook + 4 direct access) - All 3 agents confirmed working and tested
- **6 categories** with clean, logical organization - Clean agents-only architecture (workflows app completely removed)
- **Dual integration architecture** with clear separation and documentation - Emergency recovery completed from optimization failures
- **Streamlined agent creation** via JSON configs + `populate_agents` command - System restored to stable commit 657712f
- **Scalable architecture** ready for 100+ agents
**Current Agents:**
- **Webhook Agents (4)**: Social Ads Generator, Job Posting Generator, PDF Summarizer, 5 Whys Analyzer
- **Direct Access Agents (4)**: CyberSec Career Navigator, AI Brand Strategist, Lean Six Sigma Expert, SWOT Analysis Expert
**Latest Changes:** **Latest Changes:**
- **Added SWOT Analysis Expert** with proper category assignment (analysis) - Removed workflows app completely for simplified architecture
- **Streamlined agent creation process** to use only JSON + `populate_agents` - Enhanced agent marketplace with modern responsive design
- **Separated documentation** into focused files (`docs/AGENT_CREATION.md`) - Fixed all authentication-aware UI components
- **Removed 10+ redundant management commands** for cleaner codebase - Implemented file upload support for PDF Summarizer
- **Fixed marketplace consistency** and updated documentation - Real-time wallet balance updates after agent execution
**Architecture Status:**
- **Error-free agent creation** via JSON configuration approach
- **Railway-ready deployment** with automatic agent population
- **Consistent UI standards** across all marketplace components
- **Comprehensive documentation** prevents common development mistakes
**Future Development:** **Future Development:**
- **New agents** should follow patterns in `docs/AGENT_CREATION.md` - Optimization work available in feature/optimization-backup branch
- **Use existing categories first** to avoid unnecessary proliferation - Safe to add new agents via database-driven approach
- **JSON + populate_agents** is the only supported creation method - Performance optimizations should be applied incrementally with testing
--- ---
Last updated: 2025-01-08 Last updated: Last updated: 2025-08-01 01:37:09
## Documentation
- **Quick Agent Requests**: See `docs/AGENT_REQUEST_TEMPLATE.md` for simple agent request template
- **Agent Creation**: See `docs/AGENT_CREATION.md` for comprehensive agent creation guide
- **Project Overview**: This file (CLAUDE.md) for Django development and architecture

View File

@ -1,5 +1,5 @@
from django.contrib import admin from django.contrib import admin
from .models import AgentCategory, Agent, AgentExecution, ChatSession, ChatMessage from .models import AgentCategory, Agent, AgentExecution
@admin.register(AgentCategory) @admin.register(AgentCategory)
class AgentCategoryAdmin(admin.ModelAdmin): class AgentCategoryAdmin(admin.ModelAdmin):
@ -10,8 +10,8 @@ class AgentCategoryAdmin(admin.ModelAdmin):
@admin.register(Agent) @admin.register(Agent)
class AgentAdmin(admin.ModelAdmin): class AgentAdmin(admin.ModelAdmin):
list_display = ['name', 'category', 'agent_type', 'price', 'is_active', 'created_at'] list_display = ['name', 'category', 'price', 'is_active', 'created_at']
list_filter = ['category', 'agent_type', 'is_active', 'created_at'] list_filter = ['category', 'is_active', 'created_at']
search_fields = ['name', 'description', 'short_description'] search_fields = ['name', 'description', 'short_description']
prepopulated_fields = {'slug': ('name',)} prepopulated_fields = {'slug': ('name',)}
readonly_fields = ['created_at', 'updated_at'] readonly_fields = ['created_at', 'updated_at']
@ -22,21 +22,3 @@ class AgentExecutionAdmin(admin.ModelAdmin):
list_filter = ['status', 'created_at', 'agent__category'] list_filter = ['status', 'created_at', 'agent__category']
search_fields = ['agent__name', 'user__email'] search_fields = ['agent__name', 'user__email']
readonly_fields = ['created_at', 'completed_at'] readonly_fields = ['created_at', 'completed_at']
@admin.register(ChatSession)
class ChatSessionAdmin(admin.ModelAdmin):
list_display = ['session_id', 'agent', 'user', 'status', 'fee_charged', 'created_at']
list_filter = ['status', 'agent__category', 'created_at']
search_fields = ['session_id', 'agent__name', 'user__email']
readonly_fields = ['session_id', 'created_at', 'updated_at', 'completed_at']
@admin.register(ChatMessage)
class ChatMessageAdmin(admin.ModelAdmin):
list_display = ['session', 'message_type', 'content_preview', 'timestamp']
list_filter = ['message_type', 'timestamp']
search_fields = ['session__session_id', 'content']
readonly_fields = ['timestamp']
def content_preview(self, obj):
return obj.content[:50] + "..." if len(obj.content) > 50 else obj.content
content_preview.short_description = 'Content Preview'

View File

@ -1,113 +0,0 @@
# Agent Configuration System
This directory contains JSON configuration files for dynamically creating agents and categories in the Quantum Tasks AI platform.
## Directory Structure
```
agents/configs/
├── categories/
│ └── categories.json # All agent categories
├── agents/
│ ├── ai-brand-strategist.json # Direct access agent
│ ├── cybersec-career-navigator.json # Direct access agent
│ ├── five-whys-analysis.json # Chat webhook agent
│ ├── job-posting-generator.json # Form webhook agent
│ ├── pdf-summarizer.json # File upload webhook agent
│ └── social-ads-generator.json # Form webhook agent
└── README.md # This file
```
## How It Works
1. **Categories** are defined in `categories/categories.json`
2. **Agents** are defined in individual JSON files in `agents/`
3. Run `python manage.py populate_agents` to create all agents from configs
4. **Adding new agents** is as simple as creating a new JSON file
## Adding New Agents
### Step 1: Create JSON Configuration File
Create a new file in `agents/` directory, e.g., `email-writer.json`:
```json
{
"slug": "email-writer",
"name": "Email Writer",
"short_description": "AI-powered professional email writing assistant",
"description": "Generate professional emails for any purpose with AI assistance.",
"category": "marketing",
"price": 3.0,
"agent_type": "form",
"system_type": "webhook",
"form_schema": {
"fields": [
{
"name": "email_type",
"type": "select",
"label": "Email Type",
"required": true,
"options": [
{"value": "business", "label": "Business Email"},
{"value": "marketing", "label": "Marketing Email"}
]
}
]
},
"webhook_url": "http://localhost:5678/webhook/email-writer",
"access_url_name": "",
"display_url_name": ""
}
```
### Step 2: Run Population Command
```bash
python manage.py populate_agents
```
### Step 3: Agent Appears Automatically
The agent will now appear in the marketplace with the configured settings.
## Agent Types
### Webhook Agents (N8N Integration)
- Set `system_type`: `"webhook"`
- Include detailed `form_schema` with fields
- Set `webhook_url` to N8N endpoint
- Leave `access_url_name` and `display_url_name` empty
### Direct Access Agents (External Forms)
- Set `system_type`: `"direct_access"`
- Set `form_schema`: `{"fields": []}`
- Set `webhook_url` to external form URL (JotForm, etc.)
- Set `access_url_name`: `"agents:direct_access_handler"`
- Set `display_url_name`: `"agents:direct_access_display"`
## Field Types for Webhook Agents
- `text`: Single-line text input
- `textarea`: Multi-line text input
- `select`: Dropdown with options array
- `file`: File upload with drag-and-drop
- `url`: URL input with validation
- `checkbox`: Boolean checkbox
## Benefits
**Scalable**: Add 100+ agents without code changes
**Version Controlled**: All agent definitions in git
**Consistent**: Ensures local and Railway databases match
**Simple**: Just create JSON file and run command
**Validated**: Built-in validation and error handling
## Railway Deployment
On Railway, just run:
```bash
python manage.py populate_agents
```
All agents defined in JSON files will be created automatically, ensuring Railway marketplace shows all agents consistently.

View File

@ -1,16 +0,0 @@
{
"slug": "ai-brand-strategist",
"name": "AI Brand Strategist",
"short_description": "Get AI-powered brand strategy insights and recommendations for your business",
"description": "Transform your brand with AI-driven strategic insights. Get expert guidance on brand positioning, messaging, visual identity, and competitive differentiation.",
"category": "marketing",
"price": 0.0,
"agent_type": "form",
"system_type": "direct_access",
"form_schema": {
"fields": []
},
"webhook_url": "https://agent.jotform.com/01986502acd276b48e3d5f39337046c8d9b6",
"access_url_name": "agents:direct_access_handler",
"display_url_name": "agents:direct_access_display"
}

View File

@ -1,16 +0,0 @@
{
"slug": "cybersec-career-navigator",
"name": "CyberSec Career Navigator",
"short_description": "Get personalized cybersecurity career guidance from AI expert Jessica",
"description": "Navigate your cybersecurity career path with expert AI guidance. Get personalized advice on certifications, job roles, skills development, and career progression.",
"category": "career-education",
"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"
}

View File

@ -1,14 +0,0 @@
{
"slug": "five-whys-analysis",
"name": "5 Whys Analysis",
"short_description": "Interactive problem-solving using the proven 5 Whys methodology",
"description": "Systematically find root causes through guided 5 Whys methodology. Perfect for troubleshooting operational problems, understanding failures, and identifying systemic issues.",
"category": "analysis",
"price": 15.0,
"agent_type": "chat",
"system_type": "webhook",
"form_schema": null,
"webhook_url": "http://localhost:5678/webhook/5-whys-web",
"access_url_name": "",
"display_url_name": ""
}

View File

@ -1,55 +0,0 @@
{
"slug": "job-posting-generator",
"name": "Job Posting Generator",
"short_description": "Create professional job postings that attract top talent",
"description": "Generate comprehensive and attractive job postings with AI-powered content creation. Perfect for HR teams and recruiters.",
"category": "human-resources",
"price": 10.0,
"agent_type": "form",
"system_type": "webhook",
"form_schema": {
"fields": [
{
"name": "job_title",
"type": "text",
"label": "Job Title",
"required": true
},
{
"name": "company_name",
"type": "text",
"label": "Company Name",
"required": true
},
{
"name": "job_description",
"type": "textarea",
"label": "Job Description",
"required": true,
"rows": 5
},
{
"name": "seniority_level",
"type": "select",
"label": "Seniority Level",
"required": true,
"options": [
{"value": "", "label": "Select level..."},
{"value": "entry", "label": "Entry Level"},
{"value": "junior", "label": "Junior"},
{"value": "mid", "label": "Mid Level"},
{"value": "senior", "label": "Senior"}
]
},
{
"name": "location",
"type": "text",
"label": "Location",
"required": true
}
]
},
"webhook_url": "http://localhost:5678/webhook/43f84411-eaaa-488c-9b1f-856e90d0aaf6",
"access_url_name": "",
"display_url_name": ""
}

View File

@ -1,16 +0,0 @@
{
"slug": "lean-six-sigma-expert",
"name": "Lean Six Sigma Expert",
"short_description": "Get expert guidance on Lean Six Sigma methodologies and process improvement strategies",
"description": "Optimize your business processes with expert Lean Six Sigma consultation. Our AI-powered expert provides comprehensive guidance on process improvement, waste reduction, quality enhancement, and operational excellence. Get personalized recommendations for implementing Lean Six Sigma methodologies in your organization.",
"category": "consulting",
"price": 0.0,
"agent_type": "form",
"system_type": "direct_access",
"form_schema": {
"fields": []
},
"webhook_url": "https://agent.jotform.com/01987b8843ae71129342f62a93d2c605efad",
"access_url_name": "agents:direct_access_handler",
"display_url_name": "agents:direct_access_display"
}

View File

@ -1,38 +0,0 @@
{
"slug": "pdf-summarizer",
"name": "PDF Summarizer",
"short_description": "Extract and summarize content from PDF documents with AI analysis",
"description": "Upload PDF documents and get comprehensive AI-powered summaries, key insights, and analysis. Perfect for processing reports and research papers.",
"category": "document-processing",
"price": 8.0,
"agent_type": "form",
"system_type": "webhook",
"form_schema": {
"fields": [
{
"name": "pdf_file",
"type": "file",
"label": "Upload PDF Document",
"required": true,
"accept": ".pdf",
"max_size": "10MB"
},
{
"name": "analysis_type",
"type": "select",
"label": "Analysis Type",
"required": true,
"default": "summary",
"options": [
{"value": "", "label": "Select analysis type..."},
{"value": "summary", "label": "Document Summary"},
{"value": "key_points", "label": "Key Points Extraction"},
{"value": "detailed_analysis", "label": "Detailed Analysis"}
]
}
]
},
"webhook_url": "http://localhost:5678/webhook/simple-pdf-processor",
"access_url_name": "",
"display_url_name": ""
}

View File

@ -1,49 +0,0 @@
{
"slug": "social-ads-generator",
"name": "Social Ads Generator",
"short_description": "Create compelling social media advertisements optimized for different platforms",
"description": "Generate engaging social media advertisements with AI-powered content generation. Optimized for Facebook, Instagram, LinkedIn, Twitter, TikTok, and YouTube.",
"category": "marketing",
"price": 6.0,
"agent_type": "form",
"system_type": "webhook",
"form_schema": {
"fields": [
{
"name": "description",
"type": "textarea",
"label": "Describe what you'd like to generate",
"placeholder": "Describe the product, service, or campaign",
"required": true,
"rows": 4
},
{
"name": "social_platform",
"type": "select",
"label": "For Social Media Platform",
"required": true,
"options": [
{"value": "", "label": "Select a platform..."},
{"value": "facebook", "label": "Facebook"},
{"value": "instagram", "label": "Instagram"},
{"value": "linkedin", "label": "LinkedIn"},
{"value": "twitter", "label": "X (Twitter)"}
]
},
{
"name": "include_emoji",
"type": "select",
"label": "Include Emoji",
"required": true,
"options": [
{"value": "", "label": "Select an option..."},
{"value": "yes", "label": "Yes"},
{"value": "no", "label": "No"}
]
}
]
},
"webhook_url": "http://localhost:5678/webhook/2dc234d8-7217-454a-83e9-81afe5b4fe2d",
"access_url_name": "",
"display_url_name": ""
}

View File

@ -1,16 +0,0 @@
{
"slug": "swot-analysis-expert",
"name": "SWOT Analysis Expert",
"short_description": "Strategic business analysis consultation",
"description": "Get expert SWOT analysis to evaluate your business strengths, weaknesses, opportunities, and threats with professional strategic insights.",
"category": "analysis",
"price": 0.0,
"agent_type": "form",
"system_type": "direct_access",
"form_schema": {
"fields": []
},
"webhook_url": "https://agent.jotform.com/019880edcf997a41a2b4c50daa850a50a0b9",
"access_url_name": "agents:direct_access_handler",
"display_url_name": "agents:direct_access_display"
}

View File

@ -1,38 +0,0 @@
[
{
"slug": "analysis",
"name": "Analysis & Problem Solving",
"description": "AI-powered analysis tools for problem-solving and decision making",
"icon": "🧠"
},
{
"slug": "career-education",
"name": "Career & Education",
"description": "Professional career guidance and educational resources",
"icon": "🎓"
},
{
"slug": "document-processing",
"name": "Document Processing",
"description": "AI-powered document analysis and processing tools",
"icon": "📄"
},
{
"slug": "human-resources",
"name": "Human Resources",
"description": "HR automation and talent management solutions",
"icon": "💼"
},
{
"slug": "marketing",
"name": "Marketing & Advertising",
"description": "AI-powered marketing tools and advertising solutions",
"icon": "📢"
},
{
"slug": "consulting",
"name": "Business Consulting",
"description": "Professional business consultation and strategy services",
"icon": "💼"
}
]

View File

@ -1,30 +0,0 @@
from django.core.management.base import BaseCommand
from django.utils import timezone
from agents.models import ChatSession
class Command(BaseCommand):
help = 'Mark expired chat sessions as expired'
def handle(self, *args, **options):
now = timezone.now()
# Find active sessions that have expired
expired_sessions = ChatSession.objects.filter(
status='active',
expires_at__lt=now
)
count = expired_sessions.count()
if count > 0:
# Mark them as expired
expired_sessions.update(
status='expired',
completed_at=now
)
self.stdout.write(
self.style.SUCCESS(f'✅ Marked {count} expired sessions as expired')
)
else:
self.stdout.write('✅ No expired sessions found')

View File

@ -0,0 +1,130 @@
from django.core.management.base import BaseCommand
from agents.models import AgentCategory, Agent
class Command(BaseCommand):
help = 'Create job posting generator agent'
def handle(self, *args, **options):
# Get or use existing HR category
hr_category, created = AgentCategory.objects.get_or_create(
slug='human-resources',
defaults={
'name': 'Human Resources',
'description': 'AI-powered HR and recruitment tools',
'icon': '💼'
}
)
if created:
self.stdout.write(self.style.SUCCESS(f'Created category: {hr_category.name}'))
else:
self.stdout.write(f'Category already exists: {hr_category.name}')
# Create Job Posting Generator agent
job_posting_agent, created = Agent.objects.get_or_create(
slug='job-posting-generator',
defaults={
'name': 'Job Posting Generator',
'short_description': 'Create professional job postings that attract top talent',
'description': 'Generate comprehensive and attractive job postings with AI-powered content creation. Perfect for HR teams and recruiters looking to create compelling job descriptions that attract qualified candidates. Supports multiple languages, locations, and contract types.',
'category': hr_category,
'price': 10.0,
'form_schema': {
'fields': [
{
'name': 'job_title',
'type': 'text',
'label': 'Job Title',
'placeholder': 'e.g., Senior Full Stack Developer',
'required': True,
'help_text': 'The position title for the job posting'
},
{
'name': 'company_name',
'type': 'text',
'label': 'Company Name',
'placeholder': 'e.g., Quantum Technologies Inc.',
'required': True,
'help_text': 'Name of the hiring company'
},
{
'name': 'job_description',
'type': 'textarea',
'label': 'Job Description',
'placeholder': 'Describe the role, responsibilities, and what makes this opportunity exciting...',
'required': True,
'rows': 5,
'help_text': 'Detailed description of the role, responsibilities, and company culture'
},
{
'name': 'seniority_level',
'type': 'select',
'label': 'Seniority Level',
'required': True,
'options': [
{'value': '', 'label': 'Select seniority level...'},
{'value': 'entry', 'label': 'Entry Level'},
{'value': 'junior', 'label': 'Junior'},
{'value': 'mid', 'label': 'Mid Level'},
{'value': 'senior', 'label': 'Senior'},
{'value': 'lead', 'label': 'Lead'},
{'value': 'principal', 'label': 'Principal'},
{'value': 'executive', 'label': 'Executive'}
],
'help_text': 'Experience level required for this position'
},
{
'name': 'contract_type',
'type': 'select',
'label': 'Contract Type',
'required': True,
'options': [
{'value': '', 'label': 'Select contract type...'},
{'value': 'full-time', 'label': 'Full-time'},
{'value': 'part-time', 'label': 'Part-time'},
{'value': 'contract', 'label': 'Contract'},
{'value': 'freelance', 'label': 'Freelance'},
{'value': 'internship', 'label': 'Internship'},
{'value': 'temporary', 'label': 'Temporary'}
],
'help_text': 'Type of employment contract'
},
{
'name': 'location',
'type': 'text',
'label': 'Location',
'placeholder': 'e.g., Dubai, UAE (Remote)',
'required': True,
'help_text': 'Job location, include if remote work is available'
},
{
'name': 'language',
'type': 'select',
'label': 'Language',
'required': False,
'default': 'English',
'options': [
{'value': 'English', 'label': 'English'},
{'value': 'Arabic', 'label': 'Arabic (العربية)'},
{'value': 'Spanish', 'label': 'Spanish (Español)'},
{'value': 'French', 'label': 'French (Français)'},
{'value': 'German', 'label': 'German (Deutsch)'},
{'value': 'Chinese', 'label': 'Chinese (中文)'}
],
'help_text': 'Primary language for the job posting'
}
]
},
'webhook_url': 'http://localhost:5678/webhook/43f84411-eaaa-488c-9b1f-856e90d0aaf6'
}
)
if created:
self.stdout.write(self.style.SUCCESS(f'Created agent: {job_posting_agent.name}'))
else:
self.stdout.write(f'Agent already exists: {job_posting_agent.name}')
self.stdout.write(self.style.SUCCESS('Job Posting Generator setup completed successfully'))
self.stdout.write(f'Agent ID: {job_posting_agent.id}')
self.stdout.write(f'Agent Slug: {job_posting_agent.slug}')
self.stdout.write(f'Price: {job_posting_agent.price} AED')

View File

@ -0,0 +1,104 @@
from django.core.management.base import BaseCommand
from agents.models import AgentCategory, Agent
class Command(BaseCommand):
help = 'Create PDF summarizer agent'
def handle(self, *args, **options):
# Get or create Document Processing category
doc_category, created = AgentCategory.objects.get_or_create(
slug='document-processing',
defaults={
'name': 'Document Processing',
'description': 'AI-powered document analysis and processing tools',
'icon': '📄'
}
)
if created:
self.stdout.write(self.style.SUCCESS(f'Created category: {doc_category.name}'))
else:
self.stdout.write(f'Category already exists: {doc_category.name}')
# Create PDF Summarizer agent
pdf_summarizer_agent, created = Agent.objects.get_or_create(
slug='pdf-summarizer',
defaults={
'name': 'PDF Summarizer',
'short_description': 'Extract and summarize content from PDF documents with AI analysis',
'description': 'Upload PDF documents and get comprehensive AI-powered summaries, key insights, and analysis. Perfect for processing reports, research papers, contracts, and other documents. Supports multiple analysis types including summary, key points extraction, and sentiment analysis.',
'category': doc_category,
'price': 8.0,
'form_schema': {
'fields': [
{
'name': 'pdf_file',
'type': 'file',
'label': 'Upload PDF Document',
'required': True,
'accept': '.pdf',
'max_size': '10MB',
'help_text': 'Select a PDF file to analyze (max 10MB)'
},
{
'name': 'analysis_type',
'type': 'select',
'label': 'Analysis Type',
'required': True,
'default': 'summary',
'options': [
{'value': '', 'label': 'Select analysis type...'},
{'value': 'summary', 'label': 'Document Summary'},
{'value': 'key_points', 'label': 'Key Points Extraction'},
{'value': 'detailed_analysis', 'label': 'Detailed Analysis'},
{'value': 'sentiment', 'label': 'Sentiment Analysis'},
{'value': 'questions', 'label': 'Generate Questions'},
{'value': 'action_items', 'label': 'Extract Action Items'}
],
'help_text': 'Choose the type of analysis to perform on the document'
},
{
'name': 'language',
'type': 'select',
'label': 'Document Language',
'required': False,
'default': 'auto',
'options': [
{'value': 'auto', 'label': 'Auto-detect'},
{'value': 'English', 'label': 'English'},
{'value': 'Arabic', 'label': 'Arabic (العربية)'},
{'value': 'Spanish', 'label': 'Spanish (Español)'},
{'value': 'French', 'label': 'French (Français)'},
{'value': 'German', 'label': 'German (Deutsch)'},
{'value': 'Chinese', 'label': 'Chinese (中文)'}
],
'help_text': 'Specify document language for better analysis accuracy'
},
{
'name': 'output_length',
'type': 'select',
'label': 'Summary Length',
'required': False,
'default': 'medium',
'options': [
{'value': 'short', 'label': 'Short (1-2 paragraphs)'},
{'value': 'medium', 'label': 'Medium (3-5 paragraphs)'},
{'value': 'long', 'label': 'Long (detailed summary)'}
],
'help_text': 'Choose the desired length of the analysis output'
}
]
},
'webhook_url': 'http://localhost:5678/webhook/simple-pdf-processor'
}
)
if created:
self.stdout.write(self.style.SUCCESS(f'Created agent: {pdf_summarizer_agent.name}'))
else:
self.stdout.write(f'Agent already exists: {pdf_summarizer_agent.name}')
self.stdout.write(self.style.SUCCESS('PDF Summarizer setup completed successfully'))
self.stdout.write(f'Agent ID: {pdf_summarizer_agent.id}')
self.stdout.write(f'Agent Slug: {pdf_summarizer_agent.slug}')
self.stdout.write(f'Price: {pdf_summarizer_agent.price} AED')

View File

@ -0,0 +1,151 @@
from django.core.management.base import BaseCommand
from agents.models import AgentCategory, Agent
class Command(BaseCommand):
help = 'Create sample agents for testing'
def handle(self, *args, **options):
# Create categories
ai_category, _ = AgentCategory.objects.get_or_create(
slug='ai-tools',
defaults={
'name': 'AI Tools',
'description': 'AI-powered automation tools',
'icon': '🤖'
}
)
data_category, _ = AgentCategory.objects.get_or_create(
slug='data-analysis',
defaults={
'name': 'Data Analysis',
'description': 'Data processing and analysis tools',
'icon': '📊'
}
)
web_category, _ = AgentCategory.objects.get_or_create(
slug='web-scraping',
defaults={
'name': 'Web Scraping',
'description': 'Web data extraction tools',
'icon': '🕷️'
}
)
# Create sample agents
Agent.objects.get_or_create(
slug='pdf-analyzer',
defaults={
'name': 'PDF Content Analyzer',
'short_description': 'Extract and analyze content from PDF documents',
'description': 'This agent processes PDF files and extracts meaningful insights including summaries, keywords, and sentiment analysis. Perfect for document processing workflows.',
'category': ai_category,
'price': 5.00,
'form_schema': {
'fields': [
{
'name': 'pdf_url',
'type': 'url',
'label': 'PDF URL',
'placeholder': 'https://example.com/document.pdf',
'required': True
},
{
'name': 'analysis_type',
'type': 'select',
'label': 'Analysis Type',
'options': [
{'value': 'summary', 'label': 'Summary'},
{'value': 'keywords', 'label': 'Keywords'},
{'value': 'sentiment', 'label': 'Sentiment Analysis'}
],
'required': True
}
]
},
'webhook_url': 'https://your-n8n-instance.com/webhook/pdf-analyzer'
}
)
Agent.objects.get_or_create(
slug='website-scraper',
defaults={
'name': 'Website Data Scraper',
'short_description': 'Extract structured data from any website',
'description': 'Advanced web scraping agent that can extract specific data from websites using CSS selectors or XPath. Handles JavaScript-rendered content and returns clean, structured data.',
'category': web_category,
'price': 3.00,
'form_schema': {
'fields': [
{
'name': 'website_url',
'type': 'url',
'label': 'Website URL',
'placeholder': 'https://example.com',
'required': True
},
{
'name': 'selectors',
'type': 'textarea',
'label': 'CSS Selectors (one per line)',
'placeholder': 'h1.title\n.price\n.description',
'required': True
},
{
'name': 'wait_for_js',
'type': 'checkbox',
'label': 'Wait for JavaScript to load',
'required': False
}
]
},
'webhook_url': 'https://your-n8n-instance.com/webhook/website-scraper'
}
)
Agent.objects.get_or_create(
slug='data-analyzer',
defaults={
'name': 'CSV Data Analyzer',
'short_description': 'Analyze and visualize CSV data with insights',
'description': 'Upload CSV files and get comprehensive data analysis including statistics, trends, and visualizations. Perfect for business intelligence and data exploration.',
'category': data_category,
'price': 4.50,
'form_schema': {
'fields': [
{
'name': 'csv_url',
'type': 'url',
'label': 'CSV File URL',
'placeholder': 'https://example.com/data.csv',
'required': True
},
{
'name': 'analysis_columns',
'type': 'text',
'label': 'Columns to Analyze (comma-separated)',
'placeholder': 'sales,revenue,date',
'required': False
},
{
'name': 'chart_type',
'type': 'select',
'label': 'Chart Type',
'options': [
{'value': 'line', 'label': 'Line Chart'},
{'value': 'bar', 'label': 'Bar Chart'},
{'value': 'pie', 'label': 'Pie Chart'},
{'value': 'scatter', 'label': 'Scatter Plot'}
],
'required': False
}
]
},
'webhook_url': 'https://your-n8n-instance.com/webhook/data-analyzer'
}
)
self.stdout.write(self.style.SUCCESS('Sample agents created successfully'))
self.stdout.write(f'Created categories: {AgentCategory.objects.count()}')
self.stdout.write(f'Created agents: {Agent.objects.count()}')

View File

@ -0,0 +1,101 @@
from django.core.management.base import BaseCommand
from agents.models import AgentCategory, Agent
class Command(BaseCommand):
help = 'Create social ads agent for testing'
def handle(self, *args, **options):
# Create Marketing category
marketing_category, created = AgentCategory.objects.get_or_create(
slug='marketing',
defaults={
'name': 'Marketing & Advertising',
'description': 'AI-powered marketing and advertising tools',
'icon': '📢'
}
)
if created:
self.stdout.write(self.style.SUCCESS(f'Created category: {marketing_category.name}'))
else:
self.stdout.write(f'Category already exists: {marketing_category.name}')
# Create Social Ads Generator agent
social_ads_agent, created = Agent.objects.get_or_create(
slug='social-ads-generator',
defaults={
'name': 'Social Ads Generator',
'short_description': 'Create compelling social media advertisements optimized for different platforms',
'description': 'Generate engaging social media advertisements with AI-powered content generation. Optimized for Facebook, Instagram, LinkedIn, Twitter, TikTok, and YouTube. Includes platform-specific formatting, emoji support, and multi-language capabilities.',
'category': marketing_category,
'price': 6.0,
'form_schema': {
'fields': [
{
'name': 'description',
'type': 'textarea',
'label': 'Describe what you\'d like to generate',
'placeholder': 'Describe the product, service, or campaign you want to create an ad for. Include key features, target audience, and any specific messaging you want to emphasize.',
'required': True,
'rows': 4,
'help_text': 'Provide clear, specific information about your product or service for better ad copy'
},
{
'name': 'social_platform',
'type': 'select',
'label': 'For Social Media Platform',
'required': True,
'options': [
{'value': '', 'label': 'Select a platform...'},
{'value': 'facebook', 'label': 'Facebook'},
{'value': 'instagram', 'label': 'Instagram'},
{'value': 'linkedin', 'label': 'LinkedIn'},
{'value': 'twitter', 'label': 'X (Twitter)'},
{'value': 'tiktok', 'label': 'TikTok'},
{'value': 'youtube', 'label': 'YouTube'}
],
'help_text': 'Choose the social media platform for optimization'
},
{
'name': 'include_emoji',
'type': 'select',
'label': 'Include Emoji',
'required': True,
'options': [
{'value': '', 'label': 'Select an option...'},
{'value': 'yes', 'label': 'Yes'},
{'value': 'no', 'label': 'No'}
],
'help_text': 'Whether to include emojis in the ad copy'
},
{
'name': 'language',
'type': 'select',
'label': 'Language',
'required': False,
'default': 'English',
'options': [
{'value': 'English', 'label': 'English'},
{'value': 'Arabic', 'label': 'Arabic (العربية)'},
{'value': 'Spanish', 'label': 'Spanish (Español)'},
{'value': 'French', 'label': 'French (Français)'},
{'value': 'German', 'label': 'German (Deutsch)'},
{'value': 'Chinese', 'label': 'Chinese (中文)'}
],
'help_text': 'Select the primary language for the ad copy'
}
]
},
'webhook_url': 'http://localhost:5678/webhook/2dc234d8-7217-454a-83e9-81afe5b4fe2d'
}
)
if created:
self.stdout.write(self.style.SUCCESS(f'Created agent: {social_ads_agent.name}'))
else:
self.stdout.write(f'Agent already exists: {social_ads_agent.name}')
self.stdout.write(self.style.SUCCESS('Social Ads Agent setup completed successfully'))
self.stdout.write(f'Agent ID: {social_ads_agent.id}')
self.stdout.write(f'Agent Slug: {social_ads_agent.slug}')
self.stdout.write(f'Price: {social_ads_agent.price} AED')

View File

@ -1,142 +0,0 @@
import json
import os
from pathlib import Path
from django.core.management.base import BaseCommand
from agents.models import AgentCategory, Agent
class Command(BaseCommand):
help = 'Dynamically populate all agents and categories from JSON configuration files'
def handle(self, *args, **options):
self.stdout.write(self.style.SUCCESS('🚀 Dynamically populating agents from configuration files...'))
self.stdout.write('')
# Track creation statistics
categories_created = 0
agents_created = 0
config_base_path = Path(__file__).parent.parent.parent / 'configs'
# Load and create categories
categories_file = config_base_path / 'categories' / 'categories.json'
if not categories_file.exists():
self.stdout.write(self.style.ERROR(f'❌ Categories file not found: {categories_file}'))
return
with open(categories_file, 'r', encoding='utf-8') as f:
categories_data = json.load(f)
categories = {}
for category_data in categories_data:
category, created = AgentCategory.objects.get_or_create(
slug=category_data['slug'],
defaults={
'name': category_data['name'],
'description': category_data['description'],
'icon': category_data['icon']
}
)
categories[category_data['slug']] = category
if created:
categories_created += 1
self.stdout.write(f'✅ Created category: {category.name}')
else:
self.stdout.write(f' Category exists: {category.name}')
self.stdout.write('')
# Load and create agents from JSON files
agents_dir = config_base_path / 'agents'
if not agents_dir.exists():
self.stdout.write(self.style.ERROR(f'❌ Agents directory not found: {agents_dir}'))
return
# Get all JSON files in agents directory
agent_files = list(agents_dir.glob('*.json'))
if not agent_files:
self.stdout.write(self.style.WARNING('⚠️ No agent configuration files found'))
return
self.stdout.write(f'📁 Found {len(agent_files)} agent configuration files')
self.stdout.write('')
# Process each agent configuration file
for agent_file in sorted(agent_files):
try:
with open(agent_file, 'r', encoding='utf-8') as f:
agent_data = json.load(f)
# Validate required fields
required_fields = ['slug', 'name', 'category', 'price', 'agent_type']
missing_fields = [field for field in required_fields if field not in agent_data]
if missing_fields:
self.stdout.write(self.style.ERROR(f'❌ Missing fields in {agent_file.name}: {missing_fields}'))
continue
# Get category
category_slug = agent_data['category']
if category_slug not in categories:
self.stdout.write(self.style.ERROR(f'❌ Unknown category "{category_slug}" in {agent_file.name}'))
continue
category = categories[category_slug]
# Create agent
agent, created = Agent.objects.get_or_create(
slug=agent_data['slug'],
defaults={
'name': agent_data['name'],
'short_description': agent_data.get('short_description', ''),
'description': agent_data.get('description', ''),
'category': category,
'price': agent_data['price'],
'agent_type': agent_data['agent_type'],
'form_schema': agent_data.get('form_schema'),
'webhook_url': agent_data.get('webhook_url', ''),
'access_url_name': agent_data.get('access_url_name', ''),
'display_url_name': agent_data.get('display_url_name', '')
}
)
if created:
agents_created += 1
system_type = agent_data.get('system_type', 'webhook')
self.stdout.write(f'✅ Created agent: {agent.name} ({system_type.title()})')
self.stdout.write(f' 💰 Price: {agent.price} AED')
self.stdout.write(f' 📁 Config: {agent_file.name}')
else:
self.stdout.write(f' Agent exists: {agent.name} (from {agent_file.name})')
except json.JSONDecodeError as e:
self.stdout.write(self.style.ERROR(f'❌ Invalid JSON in {agent_file.name}: {e}'))
continue
except Exception as e:
self.stdout.write(self.style.ERROR(f'❌ Error processing {agent_file.name}: {e}'))
continue
self.stdout.write('')
self.stdout.write(self.style.SUCCESS('🎉 Dynamic population completed successfully!'))
self.stdout.write('')
self.stdout.write(f'📊 Summary:')
self.stdout.write(f' Categories created: {categories_created}')
self.stdout.write(f' Agents created: {agents_created}')
self.stdout.write(f' Configuration files processed: {len(agent_files)}')
self.stdout.write('')
# Final verification
total_categories = AgentCategory.objects.filter(is_active=True).count()
total_agents = Agent.objects.filter(is_active=True).count()
webhook_agents = Agent.objects.filter(is_active=True, access_url_name='').count()
direct_agents = Agent.objects.filter(is_active=True).exclude(access_url_name='').count()
self.stdout.write(f'🔍 Final verification:')
self.stdout.write(f' Total categories: {total_categories}')
self.stdout.write(f' Total agents: {total_agents}')
self.stdout.write(f' Webhook agents: {webhook_agents}')
self.stdout.write(f' Direct access agents: {direct_agents}')
self.stdout.write('')
self.stdout.write(self.style.SUCCESS('✅ Database is now consistent and ready!'))
self.stdout.write('')
self.stdout.write('🚀 To add new agents:')
self.stdout.write(' 1. Create new JSON file in agents/configs/agents/')
self.stdout.write(' 2. Run this command again')
self.stdout.write(' 3. Agent will automatically appear in marketplace!')

View File

@ -1,160 +0,0 @@
# Generated by Django 5.2.4 on 2025-08-01 04:01
import django.db.models.deletion
import uuid
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("agents", "0001_initial"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name="agent",
name="agent_type",
field=models.CharField(
choices=[("form", "Form-based"), ("chat", "Chat-based")],
default="form",
help_text="Agent interaction type",
max_length=10,
),
),
migrations.AlterField(
model_name="agent",
name="form_schema",
field=models.JSONField(
blank=True, help_text="JSON schema for agent input form", null=True
),
),
migrations.CreateModel(
name="ChatSession",
fields=[
(
"id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
),
),
(
"session_id",
models.CharField(
help_text="Unique session identifier",
max_length=100,
unique=True,
),
),
(
"status",
models.CharField(
choices=[
("active", "Active"),
("completed", "Completed"),
("abandoned", "Abandoned"),
("failed", "Failed"),
],
default="active",
max_length=20,
),
),
(
"context_data",
models.JSONField(
default=dict, help_text="Session context and progress tracking"
),
),
("fee_charged", models.DecimalField(decimal_places=2, max_digits=10)),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("completed_at", models.DateTimeField(blank=True, null=True)),
(
"agent",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="chat_sessions",
to="agents.agent",
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"ordering": ["-created_at"],
},
),
migrations.CreateModel(
name="ChatMessage",
fields=[
(
"id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
),
),
(
"message_type",
models.CharField(
choices=[
("user", "User Message"),
("agent", "Agent Response"),
("system", "System Message"),
],
max_length=10,
),
),
("content", models.TextField()),
(
"metadata",
models.JSONField(
default=dict,
help_text="Additional message data like webhook responses",
),
),
("timestamp", models.DateTimeField(auto_now_add=True)),
(
"session",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="messages",
to="agents.chatsession",
),
),
],
options={
"ordering": ["timestamp"],
},
),
migrations.AddIndex(
model_name="chatsession",
index=models.Index(
fields=["session_id"], name="agents_chat_session_0d9cb4_idx"
),
),
migrations.AddIndex(
model_name="chatsession",
index=models.Index(
fields=["user", "-created_at"], name="agents_chat_user_id_f8983d_idx"
),
),
migrations.AddIndex(
model_name="chatmessage",
index=models.Index(
fields=["session", "timestamp"], name="agents_chat_session_e8eaed_idx"
),
),
]

View File

@ -1,40 +0,0 @@
# Generated by Django 5.2.4 on 2025-08-01 10:08
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
(
"agents",
"0002_agent_agent_type_alter_agent_form_schema_chatsession_and_more",
),
]
operations = [
migrations.AddField(
model_name="chatsession",
name="expires_at",
field=models.DateTimeField(
blank=True,
help_text="Session expiration time (2 hours from last activity)",
null=True,
),
),
migrations.AlterField(
model_name="chatsession",
name="status",
field=models.CharField(
choices=[
("active", "Active"),
("completed", "Completed"),
("expired", "Expired"),
("abandoned", "Abandoned"),
("failed", "Failed"),
],
default="active",
max_length=20,
),
),
]

View File

@ -1,21 +0,0 @@
# Generated by Django 5.2.4 on 2025-08-03 04:57
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("agents", "0003_chatsession_expires_at_alter_chatsession_status"),
]
operations = [
migrations.AddField(
model_name="agent",
name="message_limit",
field=models.IntegerField(
default=50,
help_text="Maximum messages per chat session (for chat agents)",
),
),
]

View File

@ -1,23 +0,0 @@
# Generated by Django 5.2.4 on 2025-08-04 11:05
from django.db import migrations
def fake_migration(apps, schema_editor):
"""
Fake migration - Railway database already has access_url_name and display_url_name columns
but Django model didn't have them defined. Now model has fields, so we just need to
mark this migration as applied without doing anything.
"""
pass
class Migration(migrations.Migration):
dependencies = [
("agents", "0004_add_message_limit"),
]
operations = [
migrations.RunPython(fake_migration, fake_migration),
]

View File

@ -1,42 +0,0 @@
# Generated by Django 5.2.4 on 2025-08-04 17:04
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("agents", "0005_auto_20250804_1105"),
]
operations = [
migrations.AddField(
model_name="agent",
name="access_url_name",
field=models.CharField(
blank=True,
default="",
help_text="URL name for direct access agents",
max_length=100,
),
),
migrations.AddField(
model_name="agent",
name="display_url_name",
field=models.CharField(
blank=True,
default="",
help_text="URL name for agent display page",
max_length=100,
),
),
migrations.AlterField(
model_name="chatsession",
name="expires_at",
field=models.DateTimeField(
blank=True,
help_text="Session expiration time (30 minutes from last activity)",
null=True,
),
),
]

View File

@ -17,11 +17,6 @@ class AgentCategory(models.Model):
return self.name return self.name
class Agent(models.Model): class Agent(models.Model):
AGENT_TYPE_CHOICES = [
('form', 'Form-based'),
('chat', 'Chat-based'),
]
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=200) name = models.CharField(max_length=200)
slug = models.SlugField(unique=True) slug = models.SlugField(unique=True)
@ -29,12 +24,8 @@ class Agent(models.Model):
description = models.TextField() description = models.TextField()
category = models.ForeignKey(AgentCategory, on_delete=models.CASCADE, related_name='agents') category = models.ForeignKey(AgentCategory, on_delete=models.CASCADE, related_name='agents')
price = models.DecimalField(max_digits=10, decimal_places=2) price = models.DecimalField(max_digits=10, decimal_places=2)
agent_type = models.CharField(max_length=10, choices=AGENT_TYPE_CHOICES, default='form', help_text="Agent interaction type") form_schema = models.JSONField(help_text="JSON schema for agent input form")
form_schema = models.JSONField(help_text="JSON schema for agent input form", null=True, blank=True)
webhook_url = models.URLField(help_text="n8n webhook URL for execution") webhook_url = models.URLField(help_text="n8n webhook URL for execution")
message_limit = models.IntegerField(default=50, help_text="Maximum messages per chat session (for chat agents)")
access_url_name = models.CharField(max_length=100, blank=True, default='', help_text="URL name for direct access agents")
display_url_name = models.CharField(max_length=100, blank=True, default='', help_text="URL name for agent display page")
is_active = models.BooleanField(default=True) is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True) updated_at = models.DateTimeField(auto_now=True)
@ -71,79 +62,3 @@ class AgentExecution(models.Model):
def __str__(self): def __str__(self):
return f"{self.agent.name} - {self.user.email} - {self.status}" return f"{self.agent.name} - {self.user.email} - {self.status}"
class ChatSession(models.Model):
STATUS_CHOICES = [
('active', 'Active'),
('completed', 'Completed'),
('expired', 'Expired'),
('abandoned', 'Abandoned'),
('failed', 'Failed'),
]
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
session_id = models.CharField(max_length=100, unique=True, help_text="Unique session identifier")
agent = models.ForeignKey(Agent, on_delete=models.CASCADE, related_name='chat_sessions')
user = models.ForeignKey('authentication.User', on_delete=models.CASCADE)
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='active')
context_data = models.JSONField(default=dict, help_text="Session context and progress tracking")
fee_charged = models.DecimalField(max_digits=10, decimal_places=2)
expires_at = models.DateTimeField(null=True, blank=True, help_text="Session expiration time (30 minutes from last activity)")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
completed_at = models.DateTimeField(null=True, blank=True)
def save(self, *args, **kwargs):
# Set expires_at to 30 minutes from now if not set
if not self.expires_at:
from django.utils import timezone
from datetime import timedelta
self.expires_at = timezone.now() + timedelta(minutes=30)
super().save(*args, **kwargs)
class Meta:
ordering = ['-created_at']
indexes = [
models.Index(fields=['session_id']),
models.Index(fields=['user', '-created_at']),
]
def __str__(self):
return f"{self.agent.name} - {self.user.email} - {self.session_id}"
def is_expired(self):
from django.utils import timezone
if not self.expires_at:
return False # Sessions without expiration date are considered active
return timezone.now() > self.expires_at
def extend_session(self):
"""Extend session by 30 minutes from now"""
from django.utils import timezone
from datetime import timedelta
self.expires_at = timezone.now() + timedelta(minutes=30)
self.updated_at = timezone.now()
self.save()
class ChatMessage(models.Model):
MESSAGE_TYPE_CHOICES = [
('user', 'User Message'),
('agent', 'Agent Response'),
('system', 'System Message'),
]
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
session = models.ForeignKey(ChatSession, on_delete=models.CASCADE, related_name='messages')
message_type = models.CharField(max_length=10, choices=MESSAGE_TYPE_CHOICES)
content = models.TextField()
metadata = models.JSONField(default=dict, help_text="Additional message data like webhook responses")
timestamp = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['timestamp']
indexes = [
models.Index(fields=['session', 'timestamp']),
]
def __str__(self):
return f"{self.session.session_id} - {self.message_type} - {self.timestamp}"

File diff suppressed because it is too large Load Diff

View File

@ -39,42 +39,7 @@ document.body.setAttribute('data-user-balance', '{{ user.wallet_balance }}');
</h3> </h3>
</div> </div>
<div class="widget-content"> <div class="widget-content">
{% if agent.access_url_name and agent.display_url_name %} <form id="agentForm" method="POST" enctype="multipart/form-data" data-agent-id="{{ agent.id }}">
<!-- Direct Access Agent - External Form -->
<div class="direct-access-info">
<div class="section-container">
<h4 class="section-subtitle">{{ agent.category.icon }} {{ agent.name }} - External Consultation</h4>
<p style="color: #6b7280; margin-bottom: var(--spacing-lg);">
This consultation will redirect you to our specialized external form for personalized guidance.
</p>
{% if user.is_authenticated %}
{% if agent.price == 0 %}
<a href="{% url 'agents:direct_access_handler' agent.slug %}" class="btn btn-primary btn-full">
{{ agent.category.icon }} Start {{ agent.name }} Consultation (FREE)
</a>
{% elif user.wallet_balance >= agent.price %}
<a href="{% url 'agents:direct_access_handler' agent.slug %}" class="btn btn-primary btn-full">
{{ agent.category.icon }} Start {{ agent.name }} Consultation ({{ agent.price }} AED)
</a>
{% else %}
<div style="background: #fef2f2; color: #dc2626; padding: var(--spacing-md); border-radius: var(--radius-md); margin-bottom: var(--spacing-md); font-size: 14px; font-weight: 500; text-align: center;">
Insufficient balance! You need {{ agent.price }} AED.
</div>
<a href="{% url 'wallet:wallet' %}" class="btn btn-primary btn-full" style="text-decoration: none;">
💰 Top Up Wallet
</a>
{% endif %}
{% else %}
<a href="{% url 'authentication:login' %}" class="btn btn-primary btn-full">
🔐 Login to Continue
</a>
{% endif %}
</div>
</div>
{% else %}
<!-- Webhook Agent - Dynamic Form -->
<form id="agentForm" method="POST" enctype="multipart/form-data" data-agent-id="{{ agent.id }}">
{% csrf_token %} {% csrf_token %}
<!-- Dynamic Form Fields --> <!-- Dynamic Form Fields -->
@ -202,7 +167,6 @@ document.body.setAttribute('data-user-balance', '{{ user.wallet_balance }}');
{% endif %} {% endif %}
</div> </div>
</form> </form>
{% endif %}
</div> </div>
</div> </div>

View File

@ -6,9 +6,6 @@
{% block extra_css %} {% block extra_css %}
<link rel="stylesheet" href="{% static 'css/agent-base.css' %}"> <link rel="stylesheet" href="{% static 'css/agent-base.css' %}">
<link rel="stylesheet" href="{% static 'css/marketplace.css' %}"> <link rel="stylesheet" href="{% static 'css/marketplace.css' %}">
<style>
/* All Try Now buttons now use consistent styling from marketplace.css */
</style>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@ -52,11 +49,12 @@
<!-- Stats --> <!-- Stats -->
<div class="marketplace-stats"> <div class="marketplace-stats">
{% if search_query %} {% if search_query %}
Search results for "{{ search_query }}" Search results for "{{ search_query }}" •
{% if selected_category %} • {{ selected_category|capfirst }} category{% endif %}
{% elif selected_category %}
{{ selected_category|capfirst }} category
{% endif %} {% endif %}
{% if selected_category %}
{{ selected_category|capfirst }} category •
{% endif %}
{{ agents.count }} agent{{ agents.count|pluralize }} available
</div> </div>
<!-- Agents Grid --> <!-- Agents Grid -->
@ -74,47 +72,12 @@
<p class="agent-description">{{ agent.short_description }}</p> <p class="agent-description">{{ agent.short_description }}</p>
<div class="agent-footer"> <div class="agent-footer">
{% if user.is_authenticated %} {% if user.is_authenticated %}
{% if agent.slug == 'cybersec-career-navigator' %} <span class="agent-category">{{ agent.category.name }}</span>
<a href="{% url 'agents:career_navigator_access' %}" class="try-btn"> <a href="{% url 'agents:detail' agent.slug %}" class="try-btn">Try Now →</a>
Try Now →
</a>
{% elif agent.slug == 'ai-brand-strategist' %}
<a href="{% url 'agents:ai_brand_strategist_access' %}" class="try-btn">
Try Now →
</a>
{% elif agent.slug == 'lean-six-sigma-expert' %}
<a href="{% url 'agents:lean_six_sigma_expert_access' %}" class="try-btn">
Try Now →
</a>
{% elif agent.slug == 'swot-analysis-expert' %}
<a href="{% url 'agents:swot_analysis_expert_access' %}" class="try-btn">
Try Now →
</a>
{% else %}
<a href="{% url 'agents:detail' agent.slug %}" class="try-btn">Try Now →</a>
{% endif %}
{% else %} {% else %}
{% if agent.slug == 'cybersec-career-navigator' %} <a href="{% url 'authentication:login' %}?next={% url 'agents:detail' agent.slug %}" class="try-btn login-required" style="width: 100%;">
<a href="{% url 'authentication:login' %}?next={% url 'agents:career_navigator_access' %}" class="try-btn login-required" style="width: 100%;"> 🔐 Login to Try
🔐 Login to Try </a>
</a>
{% elif agent.slug == 'ai-brand-strategist' %}
<a href="{% url 'authentication:login' %}?next={% url 'agents:ai_brand_strategist_access' %}" class="try-btn login-required" style="width: 100%;">
🔐 Login to Try
</a>
{% elif agent.slug == 'lean-six-sigma-expert' %}
<a href="{% url 'authentication:login' %}?next={% url 'agents:lean_six_sigma_expert_access' %}" class="try-btn login-required" style="width: 100%;">
🔐 Login to Try
</a>
{% elif agent.slug == 'swot-analysis-expert' %}
<a href="{% url 'authentication:login' %}?next={% url 'agents:swot_analysis_expert_access' %}" class="try-btn login-required" style="width: 100%;">
🔐 Login to Try
</a>
{% else %}
<a href="{% url 'authentication:login' %}?next={% url 'agents:detail' agent.slug %}" class="try-btn login-required" style="width: 100%;">
🔐 Login to Try
</a>
{% endif %}
{% endif %} {% endif %}
</div> </div>
</div> </div>

View File

@ -7,36 +7,13 @@ urlpatterns = [
# Web interface # Web interface
path('', views.agents_marketplace, name='marketplace'), path('', views.agents_marketplace, name='marketplace'),
# Direct access routes
path('career-navigator/', views.career_navigator_view, name='career_navigator'),
path('career-navigator/access/', views.career_navigator_access, name='career_navigator_access'),
path('ai-brand-strategist/', views.ai_brand_strategist_view, name='ai_brand_strategist'),
path('ai-brand-strategist/access/', views.ai_brand_strategist_access, name='ai_brand_strategist_access'),
path('lean-six-sigma-expert/', views.lean_six_sigma_expert_view, name='lean_six_sigma_expert'),
path('lean-six-sigma-expert/access/', views.lean_six_sigma_expert_access, name='lean_six_sigma_expert_access'),
path('swot-analysis-expert/', views.swot_analysis_expert_view, name='swot_analysis_expert'),
path('swot-analysis-expert/access/', views.swot_analysis_expert_access, name='swot_analysis_expert_access'),
# API endpoints - specific URLs first to avoid slug conflicts # API endpoints - specific URLs first to avoid slug conflicts
path('api/execute/', views.execute_agent, name='execute_agent'), path('api/execute/', views.execute_agent, name='execute_agent'),
path('api/executions/', views.execution_list, name='execution_list'), path('api/executions/', views.execution_list, name='execution_list'),
path('api/executions/<uuid:execution_id>/', views.execution_detail, name='execution_detail'), path('api/executions/<uuid:execution_id>/', views.execution_detail, name='execution_detail'),
# Chat API endpoints
path('api/chat/start/', views.start_chat_session, name='start_chat_session'),
path('api/chat/send/', views.send_chat_message, name='send_chat_message'),
path('api/chat/history/<str:session_id>/', views.get_chat_history, name='get_chat_history'),
path('api/chat/session/<str:session_id>/status/', views.get_session_status, name='get_session_status'),
path('api/chat/end/', views.end_chat_session, name='end_chat_session'),
path('api/chat/export/<str:session_id>/', views.export_chat, name='export_chat'),
path('api/', views.agent_list, name='agent_list'), path('api/', views.agent_list, name='agent_list'),
path('api/<slug:slug>/', views.agent_detail, name='agent_detail_api'), path('api/<slug:slug>/', views.agent_detail, name='agent_detail_api'),
# Generic direct access routes (must be before agent detail)
path('<slug:slug>/access/', views.direct_access_handler, name='direct_access_handler'),
path('<slug:slug>/display/', views.direct_access_display, name='direct_access_display'),
# Agent detail page (must be last to avoid conflicts) # Agent detail page (must be last to avoid conflicts)
path('<slug:slug>/', views.agent_detail_view, name='detail'), path('<slug:slug>/', views.agent_detail_view, name='detail'),
] ]

File diff suppressed because it is too large Load Diff

View File

@ -30,7 +30,7 @@ def validate_password_strength(password):
is_common = password.lower() in common_passwords is_common = password.lower() in common_passwords
if not (has_length and has_lower and has_upper and has_digit and has_special) or is_common: if not (has_length and has_lower and has_upper and has_digit and has_special) or is_common:
return ["Password must have 8+ characters, uppercase, lowercase, number, and special character"] return ["Password must be at least 8 characters with uppercase, lowercase, number, and special character"]
return [] return []
@ -89,17 +89,10 @@ def handle_ratelimited(request, exception):
@ratelimit(key='ip', rate='5/m', method=UNSAFE, block=False) @ratelimit(key='ip', rate='5/m', method=UNSAFE, block=False)
def login_view(request): def login_view(request):
"""User login view with rate limiting (5 attempts per minute per IP)""" """User login view with rate limiting (5 attempts per minute per IP)"""
# Handle post-login session messages
if 'post_login_message' in request.session:
messages.info(request, request.session.pop('post_login_message'))
# Check if rate limited # Check if rate limited
if getattr(request, 'limited', False): if getattr(request, 'limited', False):
# Use session to avoid repeated rate limit messages logger.warning(f"Login rate limit exceeded for IP {request.META.get('REMOTE_ADDR')}")
if not request.session.get('rate_limit_shown'): messages.error(request, 'Too many login attempts. Please try again in a few minutes.')
logger.warning(f"Login rate limit exceeded for IP {request.META.get('REMOTE_ADDR')}")
messages.error(request, 'Too many login attempts. Please wait before trying again.')
request.session['rate_limit_shown'] = True
return render(request, 'authentication/login.html') return render(request, 'authentication/login.html')
if request.method == 'POST': if request.method == 'POST':
@ -108,16 +101,12 @@ def login_view(request):
user = authenticate(request, username=email, password=password) user = authenticate(request, username=email, password=password)
if user is not None: if user is not None:
# Check if email is verified (only if email verification is required) # Check if email is verified
if settings.REQUIRE_EMAIL_VERIFICATION and not user.email_verified: if not user.email_verified:
messages.warning(request, 'Please verify your email address before logging in. Check your inbox for the verification link.') messages.warning(request, 'Please verify your email address before logging in. Check your inbox for the verification link.')
# Store resend URL in context for template return render(request, 'authentication/login.html')
context = {'show_resend_verification': True}
return render(request, 'authentication/login.html', context)
login(request, user) login(request, user)
# Clear rate limit flag on successful login
request.session.pop('rate_limit_shown', None)
# Redirect to 'next' parameter if provided, otherwise homepage # Redirect to 'next' parameter if provided, otherwise homepage
next_url = request.GET.get('next') or request.POST.get('next') next_url = request.GET.get('next') or request.POST.get('next')
if next_url: if next_url:
@ -125,8 +114,6 @@ def login_view(request):
return redirect('core:homepage') return redirect('core:homepage')
else: else:
messages.error(request, 'Invalid email or password') messages.error(request, 'Invalid email or password')
# Clear rate limit flag on any POST attempt (failed login)
request.session.pop('rate_limit_shown', None)
return render(request, 'authentication/login.html') return render(request, 'authentication/login.html')
@ -167,25 +154,15 @@ def register_view(request):
email=email, email=email,
password=password1 password=password1
) )
# Don't automatically login - require email verification first
# Handle email verification based on settings # Send verification email
if settings.REQUIRE_EMAIL_VERIFICATION: if send_verification_email(user):
# Don't automatically login - require email verification first messages.success(request, 'Account created successfully! Please check your email to verify your account.')
# Send verification email
if send_verification_email(user):
messages.success(request, 'Account created. Check your email to verify.')
else:
messages.warning(request, 'Account created. Email verification failed - try again later.')
return redirect('authentication:login')
else: else:
# Skip email verification - auto-verify and login messages.warning(request, 'Account created but verification email could not be sent. You can request a new one after logging in.')
user.email_verified = True
user.save()
login(request, user) return redirect('authentication:login')
messages.success(request, f'Welcome {user.username}!')
return redirect('core:homepage')
except Exception as e: except Exception as e:
logger.error(f"Error creating account for {email}: {str(e)}") logger.error(f"Error creating account for {email}: {str(e)}")
messages.error(request, 'Error creating account') messages.error(request, 'Error creating account')
@ -196,6 +173,7 @@ def register_view(request):
def logout_view(request): def logout_view(request):
"""User logout view""" """User logout view"""
logout(request) logout(request)
messages.success(request, 'You have been logged out successfully')
return redirect('core:homepage') return redirect('core:homepage')
@ -347,7 +325,7 @@ def reset_password_view(request, token):
# Mark token as used # Mark token as used
reset_token.mark_as_used() reset_token.mark_as_used()
messages.success(request, 'Password reset. You can now log in.') messages.success(request, 'Your password has been reset successfully. You can now log in.')
return redirect('authentication:login') return redirect('authentication:login')
return render(request, 'authentication/reset_password.html', {'token': token}) return render(request, 'authentication/reset_password.html', {'token': token})
@ -369,7 +347,7 @@ def verify_email_view(request, token):
# Mark token as used # Mark token as used
verification_token.mark_as_used() verification_token.mark_as_used()
messages.success(request, 'Email verified. You can now log in.') messages.success(request, 'Email verified successfully! You can now log in.')
return redirect('authentication:login') return redirect('authentication:login')
@ -394,7 +372,7 @@ def resend_verification_view(request):
# Send new verification email # Send new verification email
if send_verification_email(user): if send_verification_email(user):
messages.success(request, 'Verification email sent.') messages.success(request, 'Verification email sent. Please check your inbox.')
else: else:
messages.error(request, 'Unable to send verification email at this time.') messages.error(request, 'Unable to send verification email at this time.')

View File

@ -5,9 +5,7 @@ app_name = 'core'
urlpatterns = [ urlpatterns = [
path('', views.homepage_view, name='homepage'), path('', views.homepage_view, name='homepage'),
path('digital-branding/', views.digital_branding_view, name='digital_branding'),
path('pricing/', views.pricing_view, name='pricing'), path('pricing/', views.pricing_view, name='pricing'),
path('contact/', views.contact_form_view, name='contact_form'), path('contact/', views.contact_form_view, name='contact_form'),
path('event/', views.event_view, name='event'),
path('health/', views.health_check_view, name='health_check'), path('health/', views.health_check_view, name='health_check'),
] ]

View File

@ -39,30 +39,30 @@ def homepage_view(request):
return render(request, 'core/homepage.html', {'featured_agents': [], 'user_balance': 0}) return render(request, 'core/homepage.html', {'featured_agents': [], 'user_balance': 0})
@ratelimit(key='ip', rate='60/m', method='GET', block=False) @ratelimit(key='ip', rate='60/m', method='GET', block=False)
def pricing_view(request): def pricing_view(request):
"""Pricing page - redirect to marketplace""" """Pricing page for non-logged-in users with rate limiting"""
# Redirect all pricing page access to the AI marketplace
return redirect('agents:marketplace')
@ratelimit(key='ip', rate='60/m', method='GET', block=False)
def digital_branding_view(request):
"""Digital branding services page with rate limiting"""
# Check if rate limited # Check if rate limited
if getattr(request, 'limited', False): if getattr(request, 'limited', False):
logger.warning(f"Digital branding page rate limit exceeded for IP {request.META.get('REMOTE_ADDR')}") logger.warning(f"Pricing page rate limit exceeded for IP {request.META.get('REMOTE_ADDR')}")
messages.warning(request, 'Too many requests. Please wait a moment before refreshing.') messages.warning(request, 'Too many requests. Please wait a moment before refreshing.')
# If user is already logged in, redirect to wallet top-up
if request.user.is_authenticated:
return redirect('wallet:wallet_topup')
try: try:
# Get sample agents to show pricing context from database
sample_agents = Agent.objects.filter(is_active=True).select_related('category')[:4]
context = { context = {
'user_balance': request.user.wallet_balance if request.user.is_authenticated else 0, 'sample_agents': sample_agents,
} }
return render(request, 'core/digital_branding.html', context) return render(request, 'core/pricing.html', context)
except Exception as e: except Exception as e:
logger.error(f"Digital branding view error: {e}") logger.error(f"Pricing view error: {e}")
messages.error(request, 'Unable to load digital branding page. Please try again.') messages.error(request, 'Unable to load pricing page. Please try again.')
return render(request, 'core/digital_branding.html', {}) return render(request, 'core/pricing.html', {'sample_agents': []})
def validate_contact_input(name, email, message, company=""): def validate_contact_input(name, email, message, company=""):
@ -214,22 +214,6 @@ def contact_form_view(request):
}, status=500) }, status=500)
@ratelimit(key='ip', rate='60/m', method='GET', block=False)
def event_view(request):
"""Event page view"""
# Check if rate limited
if getattr(request, 'limited', False):
logger.warning(f"Event page rate limit exceeded for IP {request.META.get('REMOTE_ADDR')}")
messages.warning(request, 'Too many requests. Please wait a moment before refreshing.')
context = {
'form_url': 'https://form.jotform.com/252214924850455',
'user_balance': request.user.wallet_balance if request.user.is_authenticated else 0,
}
return render(request, 'core/event.html', context)
@ratelimit(key='ip', rate='60/m', method='GET', block=False) @ratelimit(key='ip', rate='60/m', method='GET', block=False)
def health_check_view(request): def health_check_view(request):
"""Simplified health check endpoint - no database dependency for startup""" """Simplified health check endpoint - no database dependency for startup"""

View File

@ -1,302 +0,0 @@
# Agent Creation Guide
This guide provides comprehensive instructions for adding new agents to the Quantum Tasks AI platform.
## Overview
Quantum Tasks AI supports **TWO DISTINCT AGENT SYSTEMS**:
- **Webhook Agents** - N8N integrations for complex processing with dynamic forms
- **Direct Access Agents** - External form services (JotForm, Google Forms) with embedded interfaces
**⚡ RECOMMENDED APPROACH:** Use JSON configuration + `populate_agents` command for error-free, Railway-ready agent creation.
---
## Current Agent Status
**Total Agents: 8** (4 webhook + 4 direct access)
**Total Categories: 6**
### Webhook Agents (N8N Integration)
1. **Social Ads Generator** - 6.00 AED - Creates social media advertisements
2. **Job Posting Generator** - 10.00 AED - Creates professional job postings
3. **PDF Summarizer** - 8.00 AED - Analyzes and summarizes PDF documents
4. **5 Whys Analyzer** - 15.00 AED - Interactive chat-based root cause analysis
### Direct Access Agents (External Forms)
1. **CyberSec Career Navigator** - FREE - Career guidance consultation
2. **AI Brand Strategist** - FREE - Brand strategy consultation
3. **Lean Six Sigma Expert** - FREE - Process improvement consultation
4. **SWOT Analysis Expert** - FREE - Strategic business analysis
---
## 🏷️ Choose Existing Category First
**IMPORTANT:** Always use existing categories before creating new ones to avoid category proliferation.
### Available Categories:
- 🧠 **`analysis`** - Problem-solving, SWOT analysis, strategic analysis tools
- 🎓 **`career-education`** - Career guidance, educational resources, professional development
- 📄 **`document-processing`** - PDF analysis, file processing, document tools
- 💼 **`human-resources`** - Job postings, HR automation, talent management
- 📢 **`marketing`** - Social ads, branding, content marketing, advertising
- 💼 **`consulting`** - Business consultation, strategy services, expert advice
**Only create new categories when absolutely necessary and logically distinct.**
---
## 🚀 Agent Creation Workflow
### System 1: Webhook Agents (N8N Integration)
- **Use for:** Dynamic forms, server-side processing, file uploads, complex workflows
- **Examples:** Social Ads Generator, PDF Summarizer, Job Posting Generator
- **Flow:** Marketplace → Agent detail page → Dynamic form → N8N webhook → Results
### System 2: Direct Access Agents (External Forms)
- **Use for:** External form services (JotForm, Google Forms), consultation interfaces
- **Examples:** SWOT Analysis Expert, CyberSec Career Navigator, AI Brand Strategist
- **Flow:** Marketplace → Payment processing → Quantum Tasks header + embedded external form
---
## 📝 Implementation Steps
### Step 1: Create JSON Configuration
Create a new file in `agents/configs/agents/your-agent-name.json`:
#### Webhook Agent Example:
```json
{
"slug": "content-optimizer",
"name": "Content Optimizer",
"short_description": "AI-powered content optimization and enhancement",
"description": "Enhance your content for better engagement with AI-powered optimization suggestions, tone analysis, and improvement recommendations.",
"category": "marketing",
"price": 5.0,
"agent_type": "form",
"system_type": "webhook",
"form_schema": {
"fields": [
{
"name": "content",
"type": "textarea",
"label": "Content to Optimize",
"required": true
},
{
"name": "content_type",
"type": "select",
"label": "Content Type",
"required": true,
"options": [
{"value": "blog", "label": "Blog Post"},
{"value": "social", "label": "Social Media"},
{"value": "email", "label": "Email Marketing"}
]
}
]
},
"webhook_url": "http://localhost:5678/webhook/content-optimizer",
"access_url_name": "",
"display_url_name": ""
}
```
#### Direct Access Agent Example:
```json
{
"slug": "business-strategist",
"name": "Business Strategist",
"short_description": "Expert business strategy consultation",
"description": "Get professional business strategy insights and recommendations from experienced consultants to grow your business effectively.",
"category": "consulting",
"price": 0.0,
"agent_type": "form",
"system_type": "direct_access",
"form_schema": {
"fields": []
},
"webhook_url": "https://agent.jotform.com/your-form-id",
"access_url_name": "agents:direct_access_handler",
"display_url_name": "agents:direct_access_display"
}
```
### Step 2: Run populate_agents Command
```bash
# Development
source venv/bin/activate
python manage.py populate_agents
# Production (Railway)
python manage.py populate_agents # Runs automatically on deployment
```
### Step 3: Additional Setup (Direct Access Agents Only)
For direct access agents that need custom templates or marketplace integration:
#### 3a. Create Custom Template (optional):
```html
<!-- templates/your_agent_name.html -->
{% extends 'base.html' %}
{% load static %}
{% block title %}Your Agent Name - Quantum Tasks AI{% endblock %}
{% block extra_css %}
<style>
.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; }
.footer { display: none !important; }
</style>
{% endblock %}
{% block content %}
<div class="iframe-container">
<iframe src="{{ form_url }}" frameborder="0" scrolling="auto" title="Your Agent Name"></iframe>
</div>
{% endblock %}
```
#### 3b. Add Custom Views (if needed):
Add view functions to `agents/views.py` following the pattern of existing direct access agents.
#### 3c. Add URL Routes (if needed):
Add routes to `agents/urls.py` following the pattern of existing direct access agents.
#### 3d. Update Marketplace Template (if needed):
Add button logic to `agents/templates/agents/marketplace.html` for custom marketplace behavior.
**⚠️ Important**: Keep all "Try Now" buttons consistent with the format `Try Now →` (no icons or emojis).
### Step 4: Setup External Services
#### For Webhook Agents:
- Create N8N workflow at the webhook URL
- Configure webhook to accept JSON payload with `sessionId`, `message`, etc.
#### For Direct Access Agents:
- Create external form (JotForm, Google Forms, etc.)
- Ensure form URL is accessible and properly configured
---
## ✅ Benefits of This Approach
- ✅ **Single source of truth** - JSON configs define everything
- ✅ **Railway-ready immediately** - No manual database setup needed
- ✅ **Error-free** - No category creation mistakes or typos
- ✅ **Consistent** - All agents use same reliable creation process
- ✅ **Scalable** - Easy to add 100+ agents
- ✅ **Version controlled** - Configs are tracked in git
---
## 🔧 Supported Form Field Types (Webhook Agents)
- `text` - Single-line text input
- `textarea` - Multi-line text input
- `select` - Dropdown with options array
- `file` - File upload with drag-and-drop
- `url` - URL input with validation
- `checkbox` - Boolean checkbox
---
## ⚠️ Common Mistakes to Avoid
1. **Creating unnecessary categories** - Use existing ones first
2. **Missing system_type** - Include "webhook" or "direct_access"
3. **Wrong access_url_name** - Empty for webhook agents, populated for direct access
4. **Forgetting populate_agents** - Run after creating JSON config
5. **Complex custom commands** - Use JSON + populate_agents instead
6. **Adding icons to Try Now buttons** - Keep all marketplace buttons consistent with "Try Now →" format
---
## 🔄 Agent Management Commands
### Essential Commands:
```bash
# Populate all agents from JSON configs (main command)
python manage.py populate_agents
# Clean up expired chat sessions
python manage.py cleanup_expired_sessions
```
### Development Workflow:
1. Create JSON config file
2. Run `populate_agents`
3. Test agent functionality
4. Commit changes to git
5. Deploy to Railway (auto-runs populate_agents)
---
## 📊 Agent Configuration Reference
### Required JSON Fields:
- `slug` - URL-friendly identifier (kebab-case)
- `name` - Display name
- `short_description` - Brief description for marketplace
- `description` - Full description with details
- `category` - Must match existing category slug
- `price` - Price in AED (0.0 for free agents)
- `agent_type` - Always "form"
- `system_type` - "webhook" or "direct_access"
### System-Specific Fields:
#### Webhook Agents:
- `form_schema` - JSON schema defining form fields
- `webhook_url` - N8N webhook endpoint
- `access_url_name` - Empty string ""
- `display_url_name` - Empty string ""
#### Direct Access Agents:
- `form_schema` - Usually `{"fields": []}`
- `webhook_url` - External form URL (JotForm, etc.)
- `access_url_name` - "agents:direct_access_handler"
- `display_url_name` - "agents:direct_access_display"
---
## 🚀 Railway Deployment
When you commit changes to the repository:
1. ✅ **Railway auto-deploys** new code
2. ✅ **populate_agents runs automatically** on deployment
3. ✅ **New agents appear** in production marketplace
4. ✅ **Categories are created** if needed (but use existing ones first!)
5. ✅ **No manual database work** required
---
## 📞 Support & Documentation
- **Main Documentation**: See `CLAUDE.md` for project overview
- **Agent Issues**: Check Railway logs and database for agent status
- **Form Problems**: Verify external form URLs are accessible
- **Category Issues**: Use existing categories from the list above
---
## 🚀 Quick Agent Request
**For fast agent creation**, use the **Agent Request Template**:
👉 **See `docs/AGENT_REQUEST_TEMPLATE.md`** for a simple template to request new agents
Simply fill out the template and provide it to Claude Code for instant agent creation!
---
*Last updated: 2025-01-08*

View File

@ -1,135 +0,0 @@
# Agent Request Template
Use this template when requesting new agents to ensure all necessary information is provided for quick, error-free agent creation.
## How to Use This Template
1. **Copy the template below**
2. **Fill in all required fields**
3. **Provide to Claude Code** with the request "Create agent using this template"
4. **Claude will handle** JSON config creation, populate_agents execution, and deployment
---
## Agent Request Template
```markdown
## New Agent Request
**Agent Name**: [Enter the display name for the agent]
**Type**: [Webhook OR Direct Access]
**Category**: [Choose from: analysis, career-education, document-processing, human-resources, marketing, consulting]
**Price**: [X.XX AED or 0.0 for FREE]
**Short Description**: [Brief 1-line description for marketplace]
**Full Description**: [Detailed description of what the agent does and its benefits]
### For Webhook Agents Only:
**Form Fields**:
- Field 1: [name: field_name, type: text/textarea/select/file/url/checkbox, label: "Display Label", required: true/false]
- Field 2: [name: field_name, type: text/textarea/select/file/url/checkbox, label: "Display Label", required: true/false]
- [Add more fields as needed]
**N8N Webhook URL**: [Your N8N webhook endpoint URL]
### For Direct Access Agents Only:
**External Form URL**: [JotForm, Google Forms, or other external form URL]
**Custom Template Needed**: [Yes/No - specify if you need custom styling/layout]
**Custom Views Needed**: [Yes/No - specify if you need special marketplace behavior]
### Optional Information:
**Special Requirements**: [Any unique features or customizations needed]
**Integration Notes**: [Any special setup or configuration details]
```
---
## Available Categories
**Choose from these existing categories** (avoid creating new ones):
- 🧠 **`analysis`** - Problem-solving, SWOT analysis, strategic analysis tools
- 🎓 **`career-education`** - Career guidance, educational resources, professional development
- 📄 **`document-processing`** - PDF analysis, file processing, document tools
- 💼 **`human-resources`** - Job postings, HR automation, talent management
- 📢 **`marketing`** - Social ads, branding, content marketing, advertising
- 💼 **`consulting`** - Business consultation, strategy services, expert advice
---
## Form Field Types (Webhook Agents)
- **`text`** - Single-line text input
- **`textarea`** - Multi-line text input
- **`select`** - Dropdown (requires options array)
- **`file`** - File upload with drag-and-drop
- **`url`** - URL input with validation
- **`checkbox`** - Boolean true/false
---
## Example Requests
### Example 1: Webhook Agent
```markdown
## New Agent Request
**Agent Name**: Email Campaign Optimizer
**Type**: Webhook
**Category**: marketing
**Price**: 4.0 AED
**Short Description**: AI-powered email campaign optimization and A/B testing
**Full Description**: Optimize your email campaigns with AI analysis of subject lines, content, and send times. Get recommendations for better open rates and conversions.
### Form Fields:
- Field 1: [name: email_subject, type: text, label: "Email Subject Line", required: true]
- Field 2: [name: email_content, type: textarea, label: "Email Content", required: true]
- Field 3: [name: target_audience, type: select, label: "Target Audience", required: true, options: [{"value": "b2b", "label": "Business"}, {"value": "b2c", "label": "Consumer"}]]
**N8N Webhook URL**: http://localhost:5678/webhook/email-optimizer
```
### Example 2: Direct Access Agent
```markdown
## New Agent Request
**Agent Name**: Financial Planning Consultant
**Type**: Direct Access
**Category**: consulting
**Price**: 0.0 AED
**Short Description**: Professional financial planning and investment consultation
**Full Description**: Get expert financial advice tailored to your goals. Our certified financial planners provide personalized investment strategies and retirement planning.
### For Direct Access Agents:
**External Form URL**: https://agent.jotform.com/financial-planning-form-id
**Custom Template Needed**: No
**Custom Views Needed**: No
```
---
## What Happens Next
After you provide the completed template:
1. ✅ **Claude creates JSON config** in `agents/configs/agents/`
2. ✅ **Runs populate_agents command** to add agent to database
3. ✅ **Agent appears in marketplace** automatically
4. ✅ **Creates any needed templates/views** (for Direct Access agents)
5. ✅ **Updates marketplace integration** if needed
6. ✅ **Commits changes** and makes them Railway-ready
**No additional work needed on your part!** 🚀
---
## Tips for Better Requests
- ✅ **Use existing categories** - Avoid creating new ones unless absolutely necessary
- ✅ **Be specific** - Clear descriptions help users understand the agent's value
- ✅ **Test external forms** - Ensure JotForm/external URLs are working before requesting
- ✅ **Consider pricing** - Free agents get more usage, paid agents need clear value proposition
- ✅ **Think about fields** - For webhook agents, plan your form fields carefully
---
*For comprehensive agent creation details, see `docs/AGENT_CREATION.md`*

View File

@ -1,10 +1,10 @@
=== Documentation Auto-Update Summary === === Documentation Auto-Update Summary ===
Update Date: 2025-08-01 09:23:11 Update Date: 2025-08-01 01:37:22
Recent Commits: Recent Commits:
- 11d28a1 📚 Update documentation after GitHub push
- f6970b6 🎨 Complete Phase 1 UI optimization with button hover fixes
- 277e7ec 📄 Auto-update documentation timestamp after security fixes - 277e7ec 📄 Auto-update documentation timestamp after security fixes
- c8ad34f 🔒 Implement critical security fixes for production readiness
- 657712f 🗑️ Remove workflows app completely and streamline to agents-only
Documentation Changes: Documentation Changes:
- CLAUDE.md - CLAUDE.md

File diff suppressed because it is too large Load Diff

View File

@ -61,10 +61,6 @@ if config('RAILWAY_ENVIRONMENT', default=''):
else: else:
SITE_URL = config('SITE_URL', default='http://localhost:8000') SITE_URL = config('SITE_URL', default='http://localhost:8000')
# Email verification requirement
# Set to False for testing environments until final domain is ready
REQUIRE_EMAIL_VERIFICATION = config('REQUIRE_EMAIL_VERIFICATION', default=True, cast=bool)
# Application definition # Application definition

View File

@ -4,7 +4,7 @@
"builder": "NIXPACKS" "builder": "NIXPACKS"
}, },
"deploy": { "deploy": {
"startCommand": "python manage.py migrate; python manage.py populate_agents; python manage.py reset_admin; python manage.py verify_email admin@quantumtaskai.com --force || true; python manage.py collectstatic --noinput && gunicorn netcop_hub.wsgi:application --bind 0.0.0.0:$PORT --workers 1 --timeout 60", "startCommand": "python manage.py migrate --run-syncdb; python manage.py populate_agents; python manage.py reset_admin; python manage.py verify_email admin@quantumtaskai.com --force || true; python manage.py collectstatic --noinput && gunicorn netcop_hub.wsgi:application --bind 0.0.0.0:$PORT --workers 1 --timeout 60",
"restartPolicyType": "ON_FAILURE", "restartPolicyType": "ON_FAILURE",
"restartPolicyMaxRetries": 3 "restartPolicyMaxRetries": 3
}, },

View File

@ -8,7 +8,6 @@ gunicorn==21.2.0
psycopg2-binary==2.9.9 psycopg2-binary==2.9.9
dj-database-url==2.1.0 dj-database-url==2.1.0
whitenoise==6.8.2 whitenoise==6.8.2
reportlab==4.2.5
# Optional performance dependencies # Optional performance dependencies
redis==5.2.0 redis==5.2.0

View File

@ -89,7 +89,7 @@ html {
/* Layout - Data Analyzer Exact Copy */ /* Layout - Data Analyzer Exact Copy */
.agent-container { .agent-container {
margin: 0 auto; margin: 0 auto;
padding: var(--spacing-sm); padding: var(--spacing-lg);
max-width: 1600px; max-width: 1600px;
} }
@ -99,7 +99,7 @@ html {
border-radius: var(--radius); border-radius: var(--radius);
padding: var(--space-lg); padding: var(--space-lg);
border: var(--agent-card-border); border: var(--agent-card-border);
margin-bottom: var(--space-md); margin-bottom: var(--space-lg);
backdrop-filter: var(--agent-backdrop-filter); backdrop-filter: var(--agent-backdrop-filter);
box-shadow: var(--agent-card-shadow); box-shadow: var(--agent-card-shadow);
} }
@ -1207,7 +1207,7 @@ html {
/* Agent Grid and Layout System */ /* Agent Grid and Layout System */
.agent-grid { .agent-grid {
display: flex; display: flex;
gap: var(--spacing-sm); gap: var(--spacing-lg);
align-items: flex-start; align-items: flex-start;
flex-wrap: wrap; flex-wrap: wrap;
} }
@ -1216,7 +1216,7 @@ html {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 20px; margin-bottom: var(--spacing-xl);
} }
/* Typography - Data Analyzer Exact Copy */ /* Typography - Data Analyzer Exact Copy */

View File

@ -1,652 +0,0 @@
/* Digital Branding Services Page Styles */
/* Following homepage.css patterns and using unified color system from base.css */
/* Hero Section - matches homepage hero */
.digital-branding-hero {
position: relative;
background: var(--gradient-hero);
overflow: hidden;
min-height: 85vh;
display: flex;
align-items: center;
padding: clamp(40px, 10vw, 80px) clamp(16px, 4vw, 24px) clamp(60px, 15vw, 100px);
}
.hero-container {
max-width: 1280px;
margin: 0 auto;
text-align: center;
position: relative;
z-index: 10;
width: 100%;
}
.trust-badge {
display: inline-flex;
align-items: center;
gap: 0.5rem;
background: rgba(59, 130, 246, 0.08);
padding: 0.5rem 1.25rem;
border-radius: 50px;
margin-bottom: 2.5rem;
font-size: 0.875rem;
font-weight: 600;
color: var(--primary-blue);
border: 1px solid rgba(59, 130, 246, 0.15);
backdrop-filter: blur(10px);
}
.trust-badge-emoji {
font-size: 0.75rem;
}
.hero-title {
font-weight: 800;
margin-bottom: 2.5rem;
line-height: 1.1;
letter-spacing: -0.02em;
font-size: clamp(36px, 8vw, 110px);
margin-bottom: clamp(24px, 6vw, 40px);
}
.hero-title-gradient {
background: var(--gradient-primary);
background-size: 300% 300%;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.hero-title-normal {
color: var(--text-primary);
}
.hero-description {
color: var(--text-secondary);
margin-bottom: 3.5rem;
max-width: 720px;
margin-left: auto;
margin-right: auto;
line-height: 1.7;
font-weight: 400;
font-size: clamp(1.1rem, 3vw, 1.4rem);
margin-bottom: clamp(32px, 8vw, 56px);
padding: 0 clamp(8px, 2vw, 16px);
}
.hero-buttons {
display: flex;
justify-content: center;
flex-wrap: wrap;
margin-bottom: 5rem;
gap: clamp(12px, 3vw, 20px);
margin-bottom: clamp(40px, 10vw, 80px);
padding: 0 clamp(8px, 2vw, 16px);
}
.btn-primary {
background: var(--gradient-primary);
color: white;
border-radius: 1rem;
font-weight: bold;
border: none;
cursor: pointer;
box-shadow: 0 8px 24px rgba(59, 130, 246, 0.25), 0 4px 12px rgba(0, 0, 0, 0.05);
letter-spacing: 0.01em;
text-align: center;
text-decoration: none;
display: inline-block;
padding: clamp(14px, 4vw, 18px) clamp(24px, 6vw, 36px);
font-size: clamp(14px, 3.5vw, 18px);
min-height: 48px;
min-width: clamp(140px, 40vw, 180px);
transition: all 0.2s ease;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(59, 130, 246, 0.4);
filter: brightness(1.1);
}
.btn-primary:active {
transform: translateY(0);
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.2);
}
.btn-secondary {
background: var(--background-card);
color: var(--primary-blue);
border-radius: 1rem;
font-weight: 600;
border: 2px solid var(--border-light);
text-decoration: none;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.04), 0 4px 12px rgba(59, 130, 246, 0.08);
letter-spacing: 0.01em;
display: inline-block;
text-align: center;
padding: clamp(14px, 4vw, 18px) clamp(24px, 6vw, 36px);
font-size: clamp(14px, 3.5vw, 18px);
min-height: 48px;
min-width: clamp(140px, 40vw, 180px);
transition: all 0.2s ease;
}
.btn-secondary:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(59, 130, 246, 0.15);
border-color: var(--primary-blue);
background: rgba(59, 130, 246, 0.05);
}
.btn-secondary:active {
transform: translateY(0);
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.12);
}
.trust-indicators {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr));
gap: clamp(16px, 4vw, 48px);
max-width: 600px;
margin: 0 auto;
padding: 0 clamp(8px, 2vw, 16px);
}
.trust-card {
text-align: center;
background: rgba(255, 255, 255, 0.5);
border: 1px solid rgba(255, 255, 255, 0.8);
backdrop-filter: blur(10px);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.03);
border-radius: 1rem;
padding: clamp(16px, 4vw, 24px) clamp(12px, 3vw, 16px);
}
.trust-number {
background: var(--text-gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
font-weight: 800;
margin-bottom: 0.5rem;
font-size: clamp(1rem, 3vw, 1.2rem);
}
.trust-text {
color: var(--text-secondary);
font-weight: 500;
letter-spacing: 0.01em;
font-size: clamp(12px, 3vw, 15px);
}
/* Why Choose Us Section - matches company profile */
.why-choose-us {
position: relative;
background: var(--company-gradient);
overflow: hidden;
padding: clamp(60px, 15vw, 120px) clamp(16px, 4vw, 24px);
}
.section-container {
max-width: 1200px;
margin: 0 auto;
position: relative;
z-index: 10;
}
.section-header {
text-align: center;
margin-bottom: clamp(40px, 10vw, 80px);
}
.section-badge {
display: inline-flex;
align-items: center;
gap: 0.5rem;
background: rgba(30, 64, 175, 0.1);
padding: 0.5rem 1.25rem;
border-radius: 50px;
margin-bottom: 1.5rem;
border: 1px solid rgba(30, 64, 175, 0.2);
}
.section-badge-icon {
font-size: 1rem;
}
.section-badge-text {
font-size: 0.875rem;
font-weight: 600;
color: var(--primary-blue);
}
.section-title {
font-weight: 800;
color: var(--primary-blue);
margin-bottom: 1rem;
text-align: center;
font-size: clamp(2rem, 5vw, 3.5rem);
}
.section-subtitle {
font-size: 1.25rem;
color: var(--text-light);
max-width: 600px;
margin: 0 auto;
line-height: 1.6;
}
.why-choose-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
gap: clamp(24px, 6vw, 40px);
}
.choice-card {
background: white;
border-radius: clamp(16px, 4vw, 24px);
box-shadow: 0 8px 32px rgba(30, 64, 175, 0.08);
text-align: center;
padding: clamp(24px, 6vw, 40px);
transition: all 0.2s ease;
}
.choice-icon {
margin-bottom: 1.25rem;
font-size: clamp(2.5rem, 6vw, 3.5rem);
margin-bottom: clamp(16px, 4vw, 20px);
}
.choice-title {
font-weight: bold;
color: var(--primary-blue);
margin-bottom: 1rem;
font-size: clamp(1.2rem, 4vw, 1.5rem);
margin-bottom: clamp(12px, 3vw, 16px);
}
.choice-description {
color: var(--text-light);
line-height: 1.6;
font-size: clamp(14px, 3.5vw, 18px);
}
/* Our Process Section - matches services */
.our-process {
background: var(--services-gradient);
padding: clamp(60px, 15vw, 120px) clamp(16px, 4vw, 24px);
}
.process-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr));
gap: clamp(20px, 5vw, 32px);
margin-bottom: clamp(40px, 10vw, 60px);
}
.process-step-card {
background: white;
border-radius: clamp(16px, 4vw, 20px);
box-shadow: 0 8px 32px rgba(30, 64, 175, 0.08);
padding: clamp(24px, 6vw, 32px);
display: flex;
align-items: flex-start;
gap: clamp(16px, 4vw, 20px);
transition: all 0.2s ease;
}
.step-number {
background: var(--gradient-primary);
color: white;
width: clamp(40px, 10vw, 50px);
height: clamp(40px, 10vw, 50px);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: clamp(16px, 4vw, 20px);
flex-shrink: 0;
}
.step-content {
flex: 1;
}
.step-title {
font-weight: bold;
color: var(--primary-blue);
margin-bottom: 0.75rem;
font-size: clamp(1.1rem, 3.5vw, 1.3rem);
}
.step-description {
color: var(--text-light);
line-height: 1.6;
font-size: clamp(14px, 3.5vw, 16px);
}
/* RACE Framework */
.race-framework {
background: white;
border-radius: clamp(16px, 4vw, 24px);
box-shadow: 0 12px 40px rgba(30, 64, 175, 0.08);
padding: clamp(24px, 6vw, 40px);
}
.race-title {
text-align: center;
font-weight: bold;
color: var(--primary-blue);
margin-bottom: clamp(20px, 5vw, 32px);
font-size: clamp(1.3rem, 4vw, 1.8rem);
}
.race-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
gap: clamp(16px, 4vw, 24px);
}
.race-card {
text-align: center;
background: var(--background-light);
border-radius: clamp(12px, 3vw, 16px);
padding: clamp(16px, 4vw, 24px);
transition: all 0.2s ease;
}
.race-phase {
margin-bottom: 0.75rem;
}
.race-icon {
font-size: clamp(1.5rem, 4vw, 2rem);
margin-bottom: 0.5rem;
display: block;
}
.race-phase h4 {
font-weight: bold;
color: var(--primary-blue);
margin: 0;
font-size: clamp(1.1rem, 3.5vw, 1.3rem);
}
.race-focus {
font-weight: 600;
color: var(--text-primary);
margin-bottom: 0.5rem;
font-size: clamp(14px, 3.5vw, 16px);
}
.race-actions {
color: var(--text-light);
font-size: clamp(12px, 3vw, 14px);
line-height: 1.5;
}
/* Branding Services Section - matches services */
.branding-services {
background: var(--clients-gradient);
padding: clamp(60px, 15vw, 120px) clamp(16px, 4vw, 24px);
}
.services-title {
font-weight: bold;
text-align: center;
margin-bottom: 1rem;
color: var(--primary-blue);
font-size: clamp(1.8rem, 5vw, 2.5rem);
}
.services-subtitle {
text-align: center;
color: var(--text-light);
margin-bottom: 3rem;
font-size: clamp(16px, 4vw, 20px);
margin-bottom: clamp(24px, 6vw, 48px);
}
.branding-services-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
gap: clamp(20px, 5vw, 32px);
}
.service-card {
background: white;
border-radius: clamp(16px, 4vw, 20px);
box-shadow: 0 8px 32px rgba(30, 64, 175, 0.08);
text-align: center;
padding: clamp(24px, 6vw, 32px);
transition: all 0.2s ease;
}
.service-icon {
margin-bottom: 1.25rem;
font-size: clamp(2.5rem, 6vw, 3rem);
margin-bottom: clamp(16px, 4vw, 20px);
}
.service-title {
font-weight: bold;
color: var(--primary-blue);
margin-bottom: 1rem;
font-size: clamp(1.2rem, 4vw, 1.4rem);
margin-bottom: clamp(12px, 3vw, 16px);
}
.service-description {
color: var(--text-light);
line-height: 1.6;
font-size: clamp(14px, 3.5vw, 16px);
}
/* CTA Section - matches contact */
.branding-cta {
background: var(--contact-gradient);
padding: clamp(60px, 15vw, 120px) clamp(16px, 4vw, 24px);
}
.cta-container {
max-width: 1200px;
margin: 0 auto;
}
.cta-title {
font-weight: bold;
text-align: center;
color: var(--primary-blue);
font-size: clamp(1.8rem, 5vw, 2.5rem);
margin-bottom: clamp(24px, 6vw, 48px);
}
.cta-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(350px, 100%), 1fr));
gap: clamp(30px, 8vw, 60px);
align-items: start;
}
.cta-content {
background: white;
border-radius: clamp(16px, 4vw, 20px);
box-shadow: 0 8px 32px rgba(30, 64, 175, 0.08);
padding: clamp(24px, 6vw, 40px);
}
.cta-content-title {
font-weight: bold;
color: var(--primary-blue);
margin-bottom: 1rem;
font-size: clamp(1.3rem, 4vw, 1.6rem);
}
.cta-description {
color: var(--text-light);
line-height: 1.6;
margin-bottom: 1.5rem;
font-size: clamp(14px, 3.5vw, 18px);
}
.cta-features {
margin-bottom: 2rem;
}
.cta-feature {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 0.75rem;
font-size: clamp(14px, 3.5vw, 16px);
}
.feature-icon {
color: var(--success-green);
font-weight: bold;
}
.cta-buttons {
display: flex;
flex-direction: column;
gap: clamp(12px, 3vw, 16px);
}
.cta-btn {
padding: clamp(14px, 4vw, 16px) clamp(20px, 5vw, 24px);
border-radius: clamp(8px, 2vw, 12px);
font-weight: 600;
text-decoration: none;
text-align: center;
font-size: clamp(14px, 3.5vw, 16px);
transition: all 0.2s ease;
min-height: 48px;
display: flex;
align-items: center;
justify-content: center;
}
.cta-btn.primary {
background: var(--gradient-primary);
color: white;
box-shadow: 0 4px 16px rgba(59, 130, 246, 0.25);
}
.cta-btn.primary:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(59, 130, 246, 0.35);
}
.cta-btn.secondary {
background: var(--background-card);
color: var(--primary-blue);
border: 2px solid var(--border-light);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.05);
}
.cta-btn.secondary:hover {
transform: translateY(-2px);
border-color: var(--primary-blue);
background: rgba(59, 130, 246, 0.05);
box-shadow: 0 6px 20px rgba(59, 130, 246, 0.15);
}
.cta-info {
background: white;
border-radius: clamp(16px, 4vw, 20px);
box-shadow: 0 8px 32px rgba(30, 64, 175, 0.08);
padding: clamp(24px, 6vw, 40px);
}
.cta-info-title {
font-weight: bold;
color: var(--primary-blue);
margin-bottom: 1.5rem;
font-size: clamp(1.2rem, 4vw, 1.5rem);
margin-bottom: clamp(16px, 4vw, 24px);
}
.contact-item {
display: flex;
align-items: flex-start;
gap: clamp(12px, 3vw, 16px);
margin-bottom: clamp(20px, 5vw, 32px);
}
.contact-icon {
background: var(--primary-gradient);
border-radius: clamp(8px, 2vw, 12px);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
width: clamp(40px, 10vw, 50px);
height: clamp(40px, 10vw, 50px);
font-size: clamp(16px, 4vw, 20px);
}
.contact-details h4 {
font-weight: 600;
color: var(--primary-blue);
margin-bottom: 0.5rem;
font-size: clamp(16px, 4vw, 19px);
}
.contact-details p {
color: var(--text-light);
line-height: 1.6;
font-size: clamp(14px, 3.5vw, 16px);
}
.contact-email {
color: var(--primary-blue);
text-decoration: none;
}
.contact-email:hover {
text-decoration: underline;
}
.commitment-statement {
background: var(--services-gradient);
border-left: 4px solid var(--primary-blue);
border-radius: clamp(12px, 3vw, 16px);
padding: clamp(16px, 4vw, 24px);
margin-top: clamp(20px, 5vw, 32px);
}
.commitment-text {
color: var(--primary-blue);
font-weight: 600;
text-align: center;
font-size: clamp(14px, 3.5vw, 18px);
line-height: 1.6;
}
/* Loading state for buttons */
.cta-btn.loading {
opacity: 0.7;
pointer-events: none;
}
/* Mobile responsive adjustments */
@media (max-width: 767px) {
.cta-buttons {
flex-direction: column;
}
.process-step-card {
flex-direction: column;
text-align: center;
}
.step-number {
margin: 0 auto 1rem auto;
}
.race-grid {
grid-template-columns: 1fr;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 513 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

View File

@ -478,109 +478,6 @@ class AgentsCore extends WorkflowsCore {
return isValid; return isValid;
} }
/**
* Handle JotForm agent execution (CyberSec Career Navigator)
*/
async handleJotFormAgent() {
// Check authentication and balance
if (!this.constructor.checkAuthentication()) return;
if (!this.constructor.checkBalance(this.price)) return;
const submitBtn = document.getElementById('generateBtn');
if (submitBtn) {
submitBtn.disabled = true;
submitBtn.textContent = '⏳ Processing Payment...';
}
try {
// Create execution record and charge wallet via form API
const response = await fetch('/agents/api/form/access/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
},
body: JSON.stringify({
agent_slug: this.agentSlug
})
});
if (response.ok) {
const result = await response.json();
// Update wallet balance
this.constructor.updateWalletBalance(result.new_balance);
// Show white-label interface
this.showWhiteLabelInterface(result.interface_url);
this.constructor.showToast('✅ Payment processed! Access granted to Quantum AI Career Navigator', 'success');
} else {
const error = await response.json();
throw new Error(error.error || 'Failed to process payment');
}
} catch (error) {
console.error('Form agent error:', error);
this.constructor.showToast(`${error.message}`, 'error');
this.resetSubmitButton();
}
}
/**
* Show white-label interface in results container
*/
showWhiteLabelInterface(interfaceUrl) {
const resultsContainer = document.getElementById('resultsContainer');
const resultsContent = document.getElementById('resultsContent');
if (resultsContainer && resultsContent) {
// Update header
const widgetTitle = resultsContainer.querySelector('.widget-title');
if (widgetTitle) {
widgetTitle.innerHTML = '<span class="widget-icon">🎓</span>Quantum AI Career Navigator';
}
// Create white-label interface
resultsContent.innerHTML = `
<div class="career-nav-container" style="text-align: center; margin-bottom: 20px;">
<h3 style="color: #0369a1; margin-bottom: 10px;">🎓 Quantum AI Career Navigator</h3>
<p style="color: #6b7280; margin-bottom: 20px;">Meet Jessica, your personal AI cybersecurity career advisor. Share your goals and get expert guidance tailored to your journey.</p>
</div>
<div class="career-interface-container" style="width: 100%; min-height: 600px; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 20px rgba(0,0,0,0.1);">
<iframe
src="${interfaceUrl}"
style="width: 100%; min-height: 600px; border: none; background: white;"
frameborder="0"
scrolling="auto"
title="Quantum AI Career Navigator - Your Personal Career Advisor">
</iframe>
</div>
<div class="career-footer" style="margin-top: 20px; padding: 15px; background: #f8fafc; border-radius: 8px; text-align: center;">
<p style="color: #6b7280; font-size: 14px; margin: 0;">
💡 <strong>Pro Tip:</strong> Be specific about your experience level and career goals for the most personalized advice from your AI advisor!
</p>
</div>
`;
// Hide action buttons since this is an interactive interface
const actionButtons = resultsContainer.querySelector('.results-actions');
if (actionButtons) {
actionButtons.style.display = 'none';
}
// Show results container
resultsContainer.style.display = 'block';
// Scroll to results
resultsContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
// Reset submit button
this.resetSubmitButton();
}
}
/** /**
* Reset submit button to original state * Reset submit button to original state
*/ */

View File

@ -1,43 +0,0 @@
{% extends 'base.html' %}
{% load static %}
{% block title %}AI Brand Strategist - Quantum Tasks AI{% endblock %}
{% block extra_css %}
<style>
/* Override main-container for full-width iframe */
.main-container {
max-width: none;
padding: 0;
height: calc(100vh - 80px); /* Account for header height */
}
.iframe-container {
width: 100%;
height: 100%;
}
.iframe-container iframe {
width: 100%;
height: 100%;
border: none;
display: block;
}
/* Hide footer for this page */
.footer {
display: none !important;
}
</style>
{% endblock %}
{% block content %}
<div class="iframe-container">
<iframe
src="{{ form_url }}"
frameborder="0"
scrolling="auto"
title="AI Brand Strategist">
</iframe>
</div>
{% endblock %}

View File

@ -104,37 +104,6 @@
text-decoration: underline; text-decoration: underline;
} }
.verification-actions {
text-align: center;
margin-bottom: var(--spacing-lg);
padding: var(--spacing-md);
background: var(--surface-variant);
border-radius: var(--radius-sm);
border: 1px solid var(--outline);
}
.verification-text {
margin: 0 0 var(--spacing-sm) 0;
color: var(--on-surface-variant);
font-size: 14px;
}
.btn-outline {
background: transparent;
border: 1px solid var(--primary);
color: var(--primary);
}
.btn-outline:hover {
background: var(--primary);
color: var(--on-primary);
}
.btn-sm {
padding: var(--spacing-xs) var(--spacing-md);
font-size: 14px;
}
/* Responsive */ /* Responsive */
@media (max-width: 480px) { @media (max-width: 480px) {
.login-page { .login-page {
@ -168,13 +137,6 @@
</div> </div>
{% endif %} {% endif %}
{% if show_resend_verification %}
<div class="verification-actions">
<p class="verification-text">Need to resend verification email?</p>
<a href="{% url 'authentication:resend_verification' %}" class="btn btn-outline btn-sm">Resend Verification</a>
</div>
{% endif %}
<form method="post" class="login-form"> <form method="post" class="login-form">
{% csrf_token %} {% csrf_token %}
{% if request.GET.next %} {% if request.GET.next %}
@ -214,6 +176,7 @@
<div class="login-auth-links"> <div class="login-auth-links">
<p><a href="{% url 'authentication:forgot_password' %}">Forgot your password?</a></p> <p><a href="{% url 'authentication:forgot_password' %}">Forgot your password?</a></p>
<p><a href="{% url 'authentication:resend_verification' %}">Resend email verification</a></p>
<p>Don't have an account? <a href="{% url 'authentication:register' %}">Create account</a></p> <p>Don't have an account? <a href="{% url 'authentication:register' %}">Create account</a></p>
</div> </div>
</div> </div>

View File

@ -6,35 +6,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Quantum Tasks AI - AI Platform{% endblock %}</title> <title>{% block title %}Quantum Tasks AI - AI Platform{% endblock %}</title>
<!-- SEO Meta Tags -->
<meta name="description" content="{% block description %}Quantum Tasks AI - Advanced AI agent marketplace for automation, analysis, and productivity. Access powerful AI tools for business and personal use.{% endblock %}">
<meta name="keywords" content="AI, artificial intelligence, automation, AI agents, machine learning, productivity, business tools">
<meta name="author" content="Quantum Tasks AI">
<!-- Open Graph Meta Tags for Rich Link Previews -->
<meta property="og:type" content="website">
<meta property="og:site_name" content="Quantum Tasks AI">
<meta property="og:title" content="{% block og_title %}Quantum Tasks AI - AI Platform{% endblock %}">
<meta property="og:description" content="{% block og_description %}Advanced AI agent marketplace for automation, analysis, and productivity. Access powerful AI tools for business and personal use.{% endblock %}">
<meta property="og:url" content="{% block og_url %}https://quantumtaskai.com{{ request.get_full_path }}{% endblock %}">
<meta property="og:image" content="{% block og_image %}https://quantumtaskai.com{% static 'img/og-image.png' %}{% endblock %}">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="Quantum Tasks AI - AI Agent Marketplace">
<!-- Twitter Card Meta Tags -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@quantumtaskai">
<meta name="twitter:title" content="{% block twitter_title %}Quantum Tasks AI - AI Platform{% endblock %}">
<meta name="twitter:description" content="{% block twitter_description %}Advanced AI agent marketplace for automation, analysis, and productivity. Access powerful AI tools for business and personal use.{% endblock %}">
<meta name="twitter:image" content="{% block twitter_image %}https://quantumtaskai.com{% static 'img/og-image.png' %}{% endblock %}">
<!-- Favicon -->
<link rel="icon" type="image/x-icon" href="{% static 'img/favicon.ico' %}">
<link rel="apple-touch-icon" sizes="180x180" href="{% static 'img/apple-touch-icon.png' %}">
<link rel="icon" type="image/png" sizes="32x32" href="{% static 'img/favicon-32x32.png' %}">
<link rel="icon" type="image/png" sizes="16x16" href="{% static 'img/favicon-16x16.png' %}">
<!-- Unified Font Loading - Single Source of Truth --> <!-- Unified Font Loading - Single Source of Truth -->
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
@ -59,8 +30,8 @@
</a> </a>
<nav class="header-nav" id="header-nav"> <nav class="header-nav" id="header-nav">
<a href="{% url 'core:homepage' %}" class="nav-link {% if request.resolver_match.url_name == 'homepage' %}active{% endif %}">Home</a> <a href="{% url 'core:homepage' %}" class="nav-link {% if request.resolver_match.url_name == 'homepage' %}active{% endif %}">Home</a>
<a href="{% url 'core:digital_branding' %}" class="nav-link {% if request.resolver_match.url_name == 'digital_branding' %}active{% endif %}">AI Digital Branding</a>
<a href="{% url 'agents:marketplace' %}" class="nav-link {% if request.resolver_match.url_name == 'marketplace' %}active{% endif %}">AI Marketplace</a> <a href="{% url 'agents:marketplace' %}" class="nav-link {% if request.resolver_match.url_name == 'marketplace' %}active{% endif %}">AI Marketplace</a>
<a href="{% url 'core:pricing' %}" class="nav-link {% if request.resolver_match.url_name == 'pricing' %}active{% endif %}">Pricing</a>
</nav> </nav>
<button class="mobile-nav-toggle" onclick="toggleMobileNav()" aria-label="Toggle navigation"> <button class="mobile-nav-toggle" onclick="toggleMobileNav()" aria-label="Toggle navigation">

View File

@ -1,43 +0,0 @@
{% extends 'base.html' %}
{% load static %}
{% block title %}Career Navigator - Quantum Tasks AI{% endblock %}
{% block extra_css %}
<style>
/* Override main-container for full-width iframe */
.main-container {
max-width: none;
padding: 0;
height: calc(100vh - 80px); /* Account for header height */
}
.iframe-container {
width: 100%;
height: 100%;
}
.iframe-container iframe {
width: 100%;
height: 100%;
border: none;
display: block;
}
/* Hide footer for this page */
.footer {
display: none !important;
}
</style>
{% endblock %}
{% block content %}
<div class="iframe-container">
<iframe
src="{{ form_url }}"
frameborder="0"
scrolling="auto"
title="Career Navigator">
</iframe>
</div>
{% endblock %}

View File

@ -1,119 +0,0 @@
{% if chat_session %}
<div class="agent-widget widget-small" style="min-width: min(280px, 100%); max-width: min(280px, 100%); margin-left: auto;">
<div class="widget-header">
<h3 class="widget-title">
<span class="widget-icon">💬</span>
Current Session
</h3>
</div>
<div class="widget-content">
<div class="current-session-info">
<div class="session-status-row">
<span class="status-label">Status:</span>
<span class="session-status status-{{ chat_session.status }}">
{% if chat_session.status == 'active' %}
✅ {{ chat_session.get_status_display }}
{% elif chat_session.status == 'expired' %}
⏰ {{ chat_session.get_status_display }}
{% elif chat_session.status == 'completed' %}
🏁 {{ chat_session.get_status_display }}
{% else %}
{{ chat_session.get_status_display }}
{% endif %}
</span>
</div>
<div class="session-id-row">
<span class="id-label">Session ID:</span>
<span class="session-id">{{ chat_session.session_id }}</span>
</div>
<div class="session-time-row">
<span class="time-label">Started:</span>
<span class="session-time">{{ chat_session.created_at|date:"M d, H:i" }}</span>
</div>
</div>
</div>
</div>
<style>
.current-session-info {
display: flex;
flex-direction: column;
gap: var(--spacing-sm);
}
.session-status-row,
.session-id-row,
.session-time-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--spacing-xs) 0;
border-bottom: 1px solid var(--outline-variant);
}
.session-status-row:last-child,
.session-id-row:last-child,
.session-time-row:last-child {
border-bottom: none;
}
.status-label,
.id-label,
.time-label {
font-size: 13px;
font-weight: 500;
color: var(--on-surface-variant);
}
.session-status {
font-size: 13px;
font-weight: 600;
display: flex;
align-items: center;
gap: 4px;
}
.session-status.status-active {
color: #10b981;
}
.session-status.status-expired {
color: #f59e0b;
}
.session-status.status-completed {
color: #6366f1;
}
.session-id {
font-family: var(--font-mono);
font-size: 11px;
color: var(--on-surface-variant);
background: var(--surface-variant);
padding: 2px 6px;
border-radius: 3px;
word-break: break-all;
}
.session-time {
font-size: 12px;
color: var(--on-surface);
}
@media (max-width: 768px) {
.session-id-row {
flex-direction: column;
align-items: flex-start;
gap: 4px;
}
.session-id {
font-size: 10px;
align-self: stretch;
text-align: center;
}
}
</style>
{% endif %}

View File

@ -1,206 +0,0 @@
{% if previous_sessions %}
<div class="agent-widget widget-small" style="min-width: min(280px, 100%); max-width: min(280px, 100%); margin-left: auto;">
<div class="widget-header">
<h3 class="widget-title">
<span class="widget-icon">📋</span>
Previous Sessions
</h3>
</div>
<div class="widget-content">
{% for session in previous_sessions %}
<div class="session-item">
<div class="session-header">
<div class="session-info">
<span class="session-status status-{{ session.status }}">
{% if session.status == 'completed' %}✅{% elif session.status == 'expired' %}⏰{% elif session.status == 'abandoned' %}❌{% endif %}
{{ session.get_status_display }}
</span>
<span class="session-date">{{ session.created_at|date:"M d" }}</span>
</div>
<div class="session-stats">
<span class="message-count">{{ session.user_message_count }}/{{ session.agent.message_limit }} msgs</span>
</div>
</div>
{% if session.user_message_count > 0 and session.status != 'active' %}
<div class="session-actions">
<div class="download-actions">
<a href="{% url 'agents:export_chat' session.session_id %}?format=pdf"
class="download-link pdf-link"
title="Download PDF">
📄 PDF
</a>
<a href="{% url 'agents:export_chat' session.session_id %}?format=txt"
class="download-link txt-link"
title="Download TXT">
📝 TXT
</a>
</div>
</div>
{% endif %}
</div>
{% endfor %}
{% if previous_sessions|length >= 5 %}
<div class="view-all-sessions">
<a href="#" class="view-all-link" onclick="alert('Full session history coming soon!')">
View All Sessions →
</a>
</div>
{% endif %}
</div>
</div>
<style>
.session-item {
padding: var(--spacing-sm);
border: 1px solid var(--outline-variant);
border-radius: var(--radius-sm);
margin-bottom: var(--spacing-sm);
background: var(--surface);
transition: all 0.2s ease;
}
.session-item:hover {
background: var(--surface-variant);
border-color: var(--primary);
}
.session-item:last-child {
margin-bottom: 0;
}
.session-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: var(--spacing-xs);
}
.session-info {
display: flex;
flex-direction: column;
gap: 2px;
}
.session-status {
font-size: 12px;
font-weight: 600;
display: flex;
align-items: center;
gap: 4px;
}
.session-status.status-completed {
color: var(--success);
}
.session-status.status-expired {
color: var(--warning);
}
.session-status.status-abandoned {
color: var(--error);
}
.session-date {
font-size: 11px;
color: var(--on-surface-variant);
}
.session-stats {
text-align: right;
}
.message-count {
font-size: 11px;
color: var(--on-surface-variant);
background: var(--surface-variant);
padding: 2px 6px;
border-radius: 3px;
}
.session-actions {
border-top: 1px solid var(--outline-variant);
padding-top: var(--spacing-xs);
}
.download-actions {
display: flex;
gap: var(--spacing-sm);
justify-content: center;
}
.download-link {
display: flex;
align-items: center;
gap: 4px;
padding: 4px 8px;
background: var(--primary);
color: white;
text-decoration: none;
border-radius: 4px;
font-size: 11px;
font-weight: 500;
transition: all 0.2s ease;
flex: 1;
justify-content: center;
}
.download-link:hover {
background: var(--primary-dark);
transform: translateY(-1px);
color: white;
text-decoration: none;
}
.download-link.pdf-link {
background: #dc2626;
}
.download-link.pdf-link:hover {
background: #b91c1c;
}
.download-link.txt-link {
background: var(--primary);
}
.download-link.txt-link:hover {
background: var(--primary-dark);
}
.view-all-sessions {
margin-top: var(--spacing-sm);
text-align: center;
border-top: 1px solid var(--outline-variant);
padding-top: var(--spacing-sm);
}
.view-all-link {
font-size: 12px;
color: var(--primary);
text-decoration: none;
font-weight: 500;
}
.view-all-link:hover {
text-decoration: underline;
}
@media (max-width: 768px) {
.session-header {
flex-direction: column;
gap: var(--spacing-xs);
}
.session-stats {
text-align: left;
}
.download-actions {
flex-direction: column;
}
}
</style>
{% endif %}

View File

@ -7,23 +7,29 @@
</div> </div>
<div class="quick-agents-grid"> <div class="quick-agents-grid">
{% for agent in all_agents %} <a href="/agents/social-ads-generator/" class="quick-agent-card">
{% if agent.slug == 'cybersec-career-navigator' %} <div class="agent-icon">📢</div>
<a href="{% url 'agents:career_navigator_access' %}" class="quick-agent-card"> <div class="agent-info">
{% else %} <h4>Social Ads Generator</h4>
<a href="{% url 'agents:detail' agent.slug %}" class="quick-agent-card"> <p>Create social media ads</p>
{% endif %} </div>
<div class="agent-icon">{{ agent.category.icon }}</div> </a>
<div class="agent-info">
<h4>{{ agent.name }}</h4> <a href="/agents/job-posting-generator/" class="quick-agent-card">
<div class="agent-price">{{ agent.price }} AED</div> <div class="agent-icon">💼</div>
</div> <div class="agent-info">
</a> <h4>Job Posting Generator</h4>
{% empty %} <p>Create professional job posts</p>
<p style="text-align: center; color: var(--on-surface-variant); padding: 20px;"> </div>
No other agents available </a>
</p>
{% endfor %} <a href="/agents/pdf-summarizer/" class="quick-agent-card">
<div class="agent-icon">📄</div>
<div class="agent-info">
<h4>PDF Summarizer</h4>
<p>Analyze and summarize PDFs</p>
</div>
</a>
</div> </div>
<div class="quick-agents-footer"> <div class="quick-agents-footer">

View File

@ -1,200 +0,0 @@
{% if chat_session and messages %}
<div class="session-indicators">
<h4 class="session-indicators-title">Session Status</h4>
<!-- Time Remaining Indicator -->
<div class="indicator-item">
<div class="indicator-header">
<span class="indicator-icon"></span>
<span class="indicator-label">Time Remaining</span>
<span class="indicator-value" id="timeRemaining">
{% if time_remaining %}
{{ time_remaining }}
{% else %}
Calculating...
{% endif %}
</span>
</div>
<div class="progress-bar">
<div class="progress-fill time-progress" id="timeProgress" style="width: {{ time_percentage|default:100 }}%"></div>
</div>
</div>
<!-- Messages Remaining Indicator -->
<div class="indicator-item">
<div class="indicator-header">
<span class="indicator-icon">💬</span>
<span class="indicator-label">Messages</span>
<span class="indicator-value" id="messageCount">
{{ message_count|default:0 }}/{{ message_limit }} used
</span>
</div>
<div class="progress-bar">
<div class="progress-fill message-progress" id="messageProgress" style="width: {{ message_percentage|default:0 }}%"></div>
</div>
</div>
<!-- Warning Messages -->
<div class="session-warnings" id="sessionWarnings">
{% if time_percentage <= 20 %}
<div class="warning-item time-warning">
⚠️ Session expires soon!
</div>
{% endif %}
{% if message_percentage >= 80 %}
<div class="warning-item message-warning">
⚠️ Approaching message limit!
</div>
{% endif %}
</div>
</div>
<style>
.session-indicators {
background: var(--surface);
border: 1px solid var(--outline-variant);
border-radius: var(--radius-lg);
padding: var(--spacing-lg);
margin-bottom: var(--spacing-md);
box-shadow: var(--shadow-sm);
min-width: min(280px, 100%);
max-width: min(280px, 100%);
margin-left: auto;
}
.session-indicators-title {
font-size: 16px;
font-weight: 600;
color: var(--on-surface);
margin: 0 0 var(--spacing-md) 0;
display: flex;
align-items: center;
gap: var(--spacing-sm);
}
.indicator-item {
margin-bottom: var(--spacing-md);
}
.indicator-item:last-of-type {
margin-bottom: 0;
}
.indicator-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: var(--spacing-xs);
}
.indicator-icon {
font-size: 16px;
}
.indicator-label {
font-size: 14px;
font-weight: 500;
color: var(--on-surface);
flex: 1;
margin-left: var(--spacing-sm);
}
.indicator-value {
font-size: 13px;
font-weight: 600;
color: var(--on-surface-variant);
}
.progress-bar {
width: 100%;
height: 6px;
background: var(--surface-variant);
border-radius: 3px;
overflow: hidden;
}
.progress-fill {
height: 100%;
border-radius: 3px;
transition: width 0.3s ease, background-color 0.3s ease;
}
.time-progress {
background: linear-gradient(90deg, #10b981, #059669);
}
.time-progress[style*="width: 0"],
.time-progress[style*="width: 1"],
.time-progress[style*="width: 2"] {
background: linear-gradient(90deg, #ef4444, #dc2626);
}
.time-progress[style*="width: 3"],
.time-progress[style*="width: 4"],
.time-progress[style*="width: 5"],
.time-progress[style*="width: 10"],
.time-progress[style*="width: 15"],
.time-progress[style*="width: 20"] {
background: linear-gradient(90deg, #f59e0b, #d97706);
}
.message-progress {
background: linear-gradient(90deg, #3b82f6, #2563eb);
}
.message-progress[style*="width: 8"],
.message-progress[style*="width: 9"] {
background: linear-gradient(90deg, #f59e0b, #d97706);
}
.message-progress[style*="width: 95"],
.message-progress[style*="width: 96"],
.message-progress[style*="width: 97"],
.message-progress[style*="width: 98"],
.message-progress[style*="width: 99"],
.message-progress[style*="width: 100"] {
background: linear-gradient(90deg, #ef4444, #dc2626);
}
.session-warnings {
margin-top: var(--spacing-md);
}
.warning-item {
background: #fef3c7;
color: #92400e;
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--radius-sm);
font-size: 13px;
font-weight: 500;
margin-bottom: var(--spacing-xs);
border-left: 3px solid #f59e0b;
}
.warning-item:last-child {
margin-bottom: 0;
}
.time-warning {
background: #fee2e2;
color: #991b1b;
border-left-color: #ef4444;
}
@media (max-width: 768px) {
.session-indicators {
padding: var(--spacing-md);
}
.indicator-header {
flex-wrap: wrap;
gap: var(--spacing-xs);
}
.indicator-value {
flex-basis: 100%;
text-align: right;
}
}
</style>
{% endif %}

View File

@ -1,523 +0,0 @@
{% extends 'base.html' %}
{% load static %}
{% block title %}AI Digital Branding Services - Quantum Tasks AI{% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="{% static 'css/digital-branding.css' %}">
{% endblock %}
{% block content %}
<style>
/* Override main-container for full-width sections */
.main-container {
max-width: none;
padding: 0;
}
</style>
<!-- Hero Section -->
<section id="digital-branding-hero" class="digital-branding-hero">
<div class="hero-container">
<!-- Trust Badge -->
<div class="trust-badge">
<span class="trust-badge-emoji"></span>
<span>Trusted Digital Brand Partner</span>
</div>
<h1 class="hero-title">
<span class="hero-title-gradient">
AI-Powered Digital Branding
</span>
<br />
<span class="hero-title-normal">
Intelligence
</span>
</h1>
<p class="hero-description">
Elevate Your Brand with Intelligent Digital Strategies. Combine proven marketing expertise with AI automation to accelerate growth, optimize performance, and deliver measurable results that matter.
</p>
<!-- CTA Buttons -->
<div class="hero-buttons">
{% if user.is_authenticated %}
<a href="https://form.jotform.com/252121444918050" target="_blank" class="btn-primary">
Start Discovery
</a>
<a href="#our-process" class="btn-secondary">
View Process
</a>
{% else %}
<a href="https://form.jotform.com/252121444918050" target="_blank" class="btn-primary">
Get Started
</a>
<a href="#our-process" class="btn-secondary">
Learn More
</a>
{% endif %}
</div>
<!-- Trust Indicators -->
<div class="trust-indicators">
<div class="trust-card">
<div class="trust-number">
250%
</div>
<div class="trust-text">
Average ROI Increase
</div>
</div>
<div class="trust-card">
<div class="trust-number">
3x
</div>
<div class="trust-text">
Faster Content Creation
</div>
</div>
<div class="trust-card">
<div class="trust-number">
24/7
</div>
<div class="trust-text">
Performance Monitoring
</div>
</div>
</div>
</div>
</section>
<!-- Why Choose Us Section -->
<section id="why-choose-us" class="why-choose-us">
<div class="section-container">
<!-- Section Header -->
<div class="section-header">
<div class="section-badge">
<span class="section-badge-icon">🎯</span>
<span class="section-badge-text">Why Choose Us</span>
</div>
<h2 class="section-title">
Intelligent Digital Excellence
</h2>
<p class="section-subtitle">
Combine proven digital strategies with smart automation and data insights to accelerate your brand's growth and market impact
</p>
</div>
<div class="why-choose-grid">
<div class="choice-card">
<div class="choice-icon">🤖</div>
<h3 class="choice-title">Data-Driven Brand Intelligence</h3>
<p class="choice-description">
Advanced analytics and automation for real-time audience insights, trend forecasting, and competitive intelligence. Our smart systems continuously optimize your strategy based on performance data.
</p>
</div>
<div class="choice-card">
<div class="choice-icon">🌐</div>
<h3 class="choice-title">Comprehensive Digital Automation</h3>
<p class="choice-description">
End-to-end digital presence management with smart automation, content optimization, intelligent scheduling, SEO enhancement, and reputation management for maximum efficiency and impact.
</p>
</div>
<div class="choice-card">
<div class="choice-icon"></div>
<h3 class="choice-title">Smart Personalization & Optimization</h3>
<p class="choice-description">
Intelligent systems that learn your brand identity, automatically adapt strategies for your market, and continuously optimize campaigns based on performance data and market insights.
</p>
</div>
</div>
</div>
</section>
<!-- Our Process Section -->
<section id="our-process" class="our-process">
<div class="section-container">
<div class="section-header">
<div class="section-badge">
<span class="section-badge-icon">📋</span>
<span class="section-badge-text">Our Process</span>
</div>
<h2 class="section-title">
AI-Enhanced SOSTAC+RACE Framework
</h2>
<p class="section-subtitle">
Our proven methodology supercharged with artificial intelligence, machine learning insights, and automated optimization at every stage
</p>
</div>
<div class="process-grid">
<div class="process-step-card">
<div class="step-number">1</div>
<div class="step-content">
<h3 class="step-title">AI-Powered Situation Analysis</h3>
<p class="step-description">
Deploy machine learning algorithms to analyze your brand's digital DNA, competitive intelligence automation, and predictive market positioning with real-time data insights.
</p>
</div>
</div>
<div class="process-step-card">
<div class="step-number">2</div>
<div class="step-content">
<h3 class="step-title">AI-Optimized Objectives</h3>
<p class="step-description">
Generate data-driven SMART goals using predictive analytics, automated KPI tracking, and AI-powered performance forecasting with continuous goal optimization.
</p>
</div>
</div>
<div class="process-step-card">
<div class="step-number">3</div>
<div class="step-content">
<h3 class="step-title">Intelligent Strategy Design</h3>
<p class="step-description">
AI-driven customer journey mapping, automated audience segmentation, dynamic value proposition testing, and intelligent channel selection with performance optimization.
</p>
</div>
</div>
<div class="process-step-card">
<div class="step-number">4</div>
<div class="step-content">
<h3 class="step-title">AI-Automated Tactics (RACE Framework)</h3>
<p class="step-description">
Deploy intelligent automation for Reach (AI-powered visibility), Act (automated engagement), Convert (smart sales funnels), and Engage (AI-driven loyalty programs).
</p>
</div>
</div>
<div class="process-step-card">
<div class="step-number">5</div>
<div class="step-content">
<h3 class="step-title">Automated Action Execution</h3>
<p class="step-description">
AI-powered task automation, intelligent timeline management, and automated brand asset generation with smart content creation and deployment systems.
</p>
</div>
</div>
<div class="process-step-card">
<div class="step-number">6</div>
<div class="step-content">
<h3 class="step-title">Intelligent Control & Optimization</h3>
<p class="step-description">
Real-time AI monitoring, predictive KPI analysis, automated strategy refinement, and continuous machine learning optimization with performance forecasting.
</p>
</div>
</div>
</div>
<!-- RACE Framework Details -->
<div class="race-framework">
<h3 class="race-title">RACE Framework Breakdown</h3>
<div class="race-grid">
<div class="race-card">
<div class="race-phase">
<span class="race-icon">📢</span>
<h4>Reach</h4>
</div>
<div class="race-focus">AI-Powered Visibility</div>
<div class="race-actions">Automated LLM optimization, AI-driven SEO, smart ad targeting, AI influencer matching, automated PR distribution</div>
</div>
<div class="race-card">
<div class="race-phase">
<span class="race-icon">💬</span>
<h4>Act</h4>
</div>
<div class="race-focus">Intelligent Engagement</div>
<div class="race-actions">AI content generation, dynamic landing pages, chatbot interactions, automated social responses</div>
</div>
<div class="race-card">
<div class="race-phase">
<span class="race-icon">🎯</span>
<h4>Convert</h4>
</div>
<div class="race-focus">Smart Conversions</div>
<div class="race-actions">AI-optimized CTAs, predictive retargeting, automated lead scoring, intelligent funnel optimization</div>
</div>
<div class="race-card">
<div class="race-phase">
<span class="race-icon">❤️</span>
<h4>Engage</h4>
</div>
<div class="race-focus">AI-Driven Loyalty</div>
<div class="race-actions">Automated email personalization, AI community management, predictive advocacy, smart retention campaigns</div>
</div>
</div>
</div>
</div>
</section>
<!-- Services Section -->
<section id="services" class="branding-services">
<div class="section-container">
<h2 class="services-title">
AI-Powered Service Portfolio
</h2>
<p class="services-subtitle">
Advanced artificial intelligence solutions that automate, optimize, and accelerate your digital brand transformation
</p>
<div class="branding-services-grid">
<div class="service-card">
<div class="service-icon">🎨</div>
<h3 class="service-title">Brand Discovery & Smart Identity Design</h3>
<p class="service-description">
Complete brand audit enhanced with AI insights, strategic identity creation, and adaptive design systems that evolve with market trends and audience data.
</p>
</div>
<div class="service-card">
<div class="service-icon">📱</div>
<h3 class="service-title">Strategic Platform Management & Automation</h3>
<p class="service-description">
Smart platform selection and optimization, automated content distribution, intelligent scheduling, and performance optimization across social media, websites, and digital touchpoints.
</p>
</div>
<div class="service-card">
<div class="service-icon">🔍</div>
<h3 class="service-title">Advanced Content & Marketing Automation</h3>
<p class="service-description">
AI-enhanced content creation, automated SEO optimization, intelligent ad targeting and social media management with real-time performance adjustments for maximum impact.
</p>
</div>
<div class="service-card">
<div class="service-icon">📢</div>
<h3 class="service-title">Influencer Partnerships & Digital PR</h3>
<p class="service-description">
Strategic influencer discovery and matching, automated PR campaign management, smart media outreach, and comprehensive relationship management for maximum brand amplification.
</p>
</div>
<div class="service-card">
<div class="service-icon">🎯</div>
<h3 class="service-title">Brand Consistency & Messaging Systems</h3>
<p class="service-description">
Unified brand guidelines with automated enforcement, intelligent messaging frameworks, smart visual asset management, and consistent brand voice across all platforms.
</p>
</div>
<div class="service-card">
<div class="service-icon">📊</div>
<h3 class="service-title">Predictive AI Analytics & Automated Insights</h3>
<p class="service-description">
Real-time predictive analytics, automated performance optimization, AI-generated insights and recommendations, with intelligent forecasting and proactive strategy adjustments.
</p>
</div>
</div>
</div>
</section>
<!-- CTA Section -->
<section id="discovery-form" class="branding-cta">
<div class="cta-container">
<h2 class="cta-title">
Accelerate Your Digital Brand Growth
</h2>
<div class="cta-grid">
<!-- CTA Content -->
<div class="cta-content">
<h3 class="cta-content-title">Ready to transform your digital brand presence?</h3>
<p class="cta-description">
Complete our Digital Brand Strategy Assessment and receive a customized growth roadmap with smart automation recommendations within 24 hours.
</p>
<!-- Features List -->
<div class="cta-features">
<div class="cta-feature">
<span class="feature-icon"></span>
<span>Free comprehensive brand analysis</span>
</div>
<div class="cta-feature">
<span class="feature-icon"></span>
<span>Personalized growth automation roadmap</span>
</div>
<div class="cta-feature">
<span class="feature-icon"></span>
<span>Custom strategy proposal in 24 hours</span>
</div>
</div>
<!-- CTA Buttons -->
<div class="cta-buttons">
<!-- <a href="https://form.jotform.com/252121444918050" target="_blank" class="cta-btn primary">
🚀 Get Discovery Questionnaire
</a> -->
<a href="https://form.jotform.com/252121444918050" target="_blank" class="cta-btn secondary">
💬 Contact Us Directly
</a>
</div>
</div>
<!-- Contact Information -->
<div class="cta-info">
<h3 class="cta-info-title">Get in Touch</h3>
<!-- Contact Item -->
<div class="contact-item">
<div class="contact-icon">
✉️
</div>
<div class="contact-details">
<h4>Email Address</h4>
<p>
<a href="mailto:abhay@quantumtaskai.com" class="contact-email">
abhay@quantumtaskai.com
</a>
</p>
</div>
</div>
<!-- Commitment Statement -->
<div class="commitment-statement">
<p class="commitment-text">
Leading the AI Revolution in Digital Branding - Where Artificial Intelligence Meets Brand Excellence for Unprecedented Growth and Automation.
</p>
</div>
</div>
</div>
</div>
</section>
{% endblock %}
{% block extra_js %}
<script>
class DigitalBrandingManager {
constructor() {
this.processCards = document.querySelectorAll('.process-step-card');
this.raceCards = document.querySelectorAll('.race-card');
this.serviceCards = document.querySelectorAll('.service-card');
this.ctaButtons = document.querySelectorAll('.cta-btn');
this.init();
}
init() {
this.setupCardInteractions();
this.setupButtonHovers();
this.addScrollAnimations();
this.setupSmoothScrolling();
}
setupCardInteractions() {
// Process step cards
this.processCards.forEach(card => {
card.addEventListener('mouseenter', () => {
this.highlightCard(card);
});
card.addEventListener('mouseleave', () => {
this.removeHighlight(card);
});
});
// RACE framework cards
this.raceCards.forEach(card => {
card.addEventListener('mouseenter', () => {
this.highlightCard(card);
});
card.addEventListener('mouseleave', () => {
this.removeHighlight(card);
});
});
// Service cards
this.serviceCards.forEach(card => {
card.addEventListener('mouseenter', () => {
this.highlightCard(card);
});
card.addEventListener('mouseleave', () => {
this.removeHighlight(card);
});
});
}
highlightCard(card) {
card.style.transform = 'translateY(-4px)';
card.style.boxShadow = '0 20px 40px rgba(30, 64, 175, 0.15)';
}
removeHighlight(card) {
card.style.transform = 'translateY(0)';
card.style.boxShadow = '';
}
setupButtonHovers() {
this.ctaButtons.forEach(button => {
button.addEventListener('mouseenter', () => {
button.style.transform = 'translateY(-2px)';
});
button.addEventListener('mouseleave', () => {
button.style.transform = '';
});
button.addEventListener('click', () => {
if (!button.classList.contains('loading')) {
button.classList.add('loading');
const originalText = button.textContent;
button.textContent = '⏳ Loading...';
setTimeout(() => {
if (button.classList.contains('loading')) {
button.classList.remove('loading');
button.textContent = originalText;
}
}, 2000);
}
});
});
}
addScrollAnimations() {
if ('IntersectionObserver' in window) {
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}
});
}, observerOptions);
// Observe cards for scroll animations
[...this.processCards, ...this.raceCards, ...this.serviceCards].forEach((card, index) => {
card.style.opacity = '0';
card.style.transform = 'translateY(20px)';
card.style.transition = `opacity 0.6s ease ${index * 0.1}s, transform 0.6s ease ${index * 0.1}s`;
observer.observe(card);
});
// Observe sections
document.querySelectorAll('.section-header, .cta-content').forEach(section => {
section.style.opacity = '0';
section.style.transform = 'translateY(20px)';
section.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
observer.observe(section);
});
}
}
setupSmoothScrolling() {
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
}
}
// Initialize when DOM is loaded
document.addEventListener('DOMContentLoaded', function() {
new DigitalBrandingManager();
});
</script>
{% endblock %}

View File

@ -1,43 +0,0 @@
{% extends 'base.html' %}
{% load static %}
{% block title %}Event - Quantum Tasks AI{% endblock %}
{% block extra_css %}
<style>
/* Override main-container for full-width iframe */
.main-container {
max-width: none;
padding: 0;
height: calc(100vh - 80px); /* Account for header height */
}
.iframe-container {
width: 100%;
height: 100%;
}
.iframe-container iframe {
width: 100%;
height: 100%;
border: none;
display: block;
}
/* Hide footer for this page */
.footer {
display: none !important;
}
</style>
{% endblock %}
{% block content %}
<div class="iframe-container">
<iframe
src="{{ form_url }}"
frameborder="0"
scrolling="auto"
title="Event">
</iframe>
</div>
{% endblock %}

View File

@ -3,11 +3,6 @@
{% block title %}Quantum Tasks AI - AI & Task Automation Solutions{% endblock %} {% block title %}Quantum Tasks AI - AI & Task Automation Solutions{% endblock %}
{% block description %}Discover powerful AI agents for automation, analysis, and productivity. Access premium AI tools for social media, business analysis, document processing, and more.{% endblock %}
{% block og_title %}Quantum Tasks AI - AI & Task Automation Solutions{% endblock %}
{% block og_description %}Discover powerful AI agents for automation, analysis, and productivity. Access premium AI tools for social media, business analysis, document processing, and more.{% endblock %}
{% block extra_css %} {% block extra_css %}
<link rel="stylesheet" href="{% static 'css/homepage.css' %}"> <link rel="stylesheet" href="{% static 'css/homepage.css' %}">
{% endblock %} {% endblock %}

View File

@ -1,43 +0,0 @@
{% extends 'base.html' %}
{% load static %}
{% block title %}Lean Six Sigma Expert - Quantum Tasks AI{% endblock %}
{% block extra_css %}
<style>
/* Override main-container for full-width iframe */
.main-container {
max-width: none;
padding: 0;
height: calc(100vh - 80px); /* Account for header height */
}
.iframe-container {
width: 100%;
height: 100%;
}
.iframe-container iframe {
width: 100%;
height: 100%;
border: none;
display: block;
}
/* Hide footer for this page */
.footer {
display: none !important;
}
</style>
{% endblock %}
{% block content %}
<div class="iframe-container">
<iframe
src="{{ form_url }}"
frameborder="0"
scrolling="auto"
title="Lean Six Sigma Expert">
</iframe>
</div>
{% endblock %}

View File

@ -1,38 +0,0 @@
{% extends 'base.html' %}
{% load static %}
{% block title %}SWOT Analysis Expert - Quantum Tasks AI{% endblock %}
{% block extra_css %}
<style>
.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;
}
.footer {
display: none !important;
}
</style>
{% endblock %}
{% block content %}
<div class="iframe-container">
<iframe
src="{{ form_url }}"
frameborder="0"
scrolling="auto"
title="SWOT Analysis Expert">
</iframe>
</div>
{% endblock %}

View File

@ -79,22 +79,16 @@ def wallet_topup_view(request):
return redirect('wallet:wallet_topup') return redirect('wallet:wallet_topup')
except Exception as e: except Exception as e:
logger.error(f"Checkout session creation failed for user {request.user.id}: {e}") logger.error(f"Checkout session creation failed for user {request.user.id}: {e}")
messages.error(request, 'Payment failed. Try again.') messages.error(request, 'Unable to process payment at this time. Please try again.')
return redirect('wallet:wallet_topup') return redirect('wallet:wallet_topup')
return render(request, 'wallet/wallet_topup.html') return render(request, 'wallet/wallet_topup.html')
@login_required
@ratelimit(key='user', rate='10/m', method='GET', block=False) @ratelimit(key='user', rate='10/m', method='GET', block=False)
def wallet_topup_success_view(request): def wallet_topup_success_view(request):
"""Payment success page with automatic payment verification""" """Payment success page with automatic payment verification"""
# Check authentication first and clear any messages if redirecting to login
if not request.user.is_authenticated:
# Clear any existing messages to prevent them from showing on login page
storage = messages.get_messages(request)
storage.used = True
return redirect('authentication:login')
# Check if rate limited # Check if rate limited
if getattr(request, 'limited', False): if getattr(request, 'limited', False):
logger.warning(f"Payment success page rate limit exceeded for user {request.user.id}") logger.warning(f"Payment success page rate limit exceeded for user {request.user.id}")
@ -105,7 +99,7 @@ def wallet_topup_success_view(request):
if not session_id: if not session_id:
logger.warning(f"No session ID provided for user {request.user.id}") logger.warning(f"No session ID provided for user {request.user.id}")
messages.error(request, 'Payment session not found.') messages.error(request, 'No payment session found. Please contact support if you completed a payment.')
return redirect('wallet:wallet') return redirect('wallet:wallet')
# Verify payment directly with Stripe API # Verify payment directly with Stripe API
@ -117,10 +111,10 @@ def wallet_topup_success_view(request):
if result['success']: if result['success']:
if result['processed']: if result['processed']:
messages.success(request, f'{result["amount"]} AED added to wallet.') messages.success(request, f'Payment successful! {result["amount"]} AED has been added to your wallet.')
logger.info(f"Payment verified and wallet updated for user {request.user.id}") logger.info(f"Payment verified and wallet updated for user {request.user.id}")
else: else:
messages.info(request, 'Payment already processed.') messages.info(request, 'Payment already processed. Your wallet balance is up to date.')
logger.info(f"Payment already processed for session {session_id}") logger.info(f"Payment already processed for session {session_id}")
else: else:
messages.warning(request, 'Payment verification failed. Please contact support.') messages.warning(request, 'Payment verification failed. Please contact support.')
@ -128,21 +122,15 @@ def wallet_topup_success_view(request):
except Exception as e: except Exception as e:
logger.error(f"Error verifying payment for user {request.user.id}: {e}") logger.error(f"Error verifying payment for user {request.user.id}: {e}")
messages.error(request, 'Payment verification failed.') messages.error(request, 'Unable to verify payment. Please contact support if you completed a payment.')
return redirect('wallet:wallet') return redirect('wallet:wallet')
@login_required
def wallet_topup_cancel_view(request): def wallet_topup_cancel_view(request):
"""Payment cancel page""" """Payment cancel page"""
# Check authentication first and clear any messages if redirecting to login messages.info(request, 'Payment was cancelled. No charges were made.')
if not request.user.is_authenticated:
# Clear any existing messages to prevent them from showing on login page
storage = messages.get_messages(request)
storage.used = True
return redirect('authentication:login')
messages.info(request, 'Payment cancelled.')
return redirect('wallet:wallet_topup') return redirect('wallet:wallet_topup')