gb/CAMPAIGNS.md
Rodrigo Rodriguez (Pragmatismo) f3bad05e76
Some checks failed
BotServer CI / build (push) Failing after 13s
Fix LXD socket handling in container mode
2026-03-15 18:19:22 -03:00

511 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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

# Campaigns App Improvement Specification
## Current State Analysis
The Campaigns app (`botui/ui/suite/campaigns/campaigns.html`) provides multi-channel marketing with:
- Campaign grid view with status badges
- Channel filters (Email, WhatsApp, Social)
- Lists and Templates tabs
- Basic campaign creation modal
- Metrics display (sent, opened, clicked)
## Critical Issues to Fix
### 1. Missing Backend Integration
**Problem:** All data loading endpoints return empty or placeholder data
**Fix Required:**
```
GET /api/crm/campaigns - Returns campaign list
GET /api/crm/lists - Returns contact lists
GET /api/crm/templates - Returns message templates
POST /api/crm/campaigns - Creates new campaign
```
**Expected Response Format:**
```json
{
"campaigns": [
{
"id": "uuid",
"name": "Welcome Series",
"status": "running",
"channels": ["email", "whatsapp"],
"metrics": {
"sent": 1250,
"opened": 890,
"clicked": 234
},
"budget": 500.00,
"scheduled_at": "2026-03-20T10:00:00Z"
}
]
}
```
### 2. Campaign Card Template Missing
**Problem:** Backend returns JSON but frontend expects HTML
**Fix:** Backend must return HTML fragments:
```html
<div class="campaign-card" data-id="{{id}}">
<div class="campaign-card-header">
<h3 class="campaign-card-title">{{name}}</h3>
<span class="campaign-status {{status}}">{{status}}</span>
</div>
<div class="campaign-channels">
{{#each channels}}
<span class="campaign-channel-tag">
{{icon}} {{name}}
</span>
{{/each}}
</div>
<div class="campaign-metrics">
<div class="campaign-metric">
<span class="campaign-metric-value">{{metrics.sent}}</span>
<span class="campaign-metric-label">Sent</span>
</div>
<div class="campaign-metric">
<span class="campaign-metric-value">{{metrics.opened}}</span>
<span class="campaign-metric-label">Opened</span>
</div>
<div class="campaign-metric">
<span class="campaign-metric-value">{{metrics.clicked}}</span>
<span class="campaign-metric-label">Clicked</span>
</div>
</div>
<div class="campaign-actions">
<button class="campaign-action-btn" onclick="viewCampaign('{{id}}')">View</button>
<button class="campaign-action-btn" onclick="editCampaign('{{id}}')">Edit</button>
<button class="campaign-action-btn primary" onclick="launchCampaign('{{id}}')">Launch</button>
</div>
</div>
```
### 3. Form Submission Not Working
**Problem:** Modal form uses HTMX but doesn't refresh list after creation
**Fix:**
```javascript
document.getElementById('campaign-form').addEventListener('htmx:afterRequest', function(e) {
if (e.detail.successful) {
hideCampaignModal();
// Refresh campaigns list
htmx.ajax('GET', '/api/crm/campaigns', {
target: '#campaignsList',
swap: 'innerHTML'
});
} else {
// Show error message
const error = e.detail.xhr.responseText;
showError(error);
}
});
```
### 4. Channel Filter Not Working
**Problem:** Filter function checks text content instead of data attributes
**Fix:**
```html
<!-- Add data attribute to cards -->
<div class="campaign-card" data-channels="email,whatsapp">
<!-- Update filter function -->
<script>
function filterCampaigns(channel) {
const cards = document.querySelectorAll('.campaign-card');
cards.forEach(card => {
if (channel === 'all') {
card.style.display = '';
} else {
const channels = card.dataset.channels.split(',');
card.style.display = channels.includes(channel) ? '' : 'none';
}
});
}
</script>
```
## Major Improvements Needed
### 5. Campaign Builder Wizard
**What:** Multi-step campaign creation with preview
**Implementation:**
```html
<div id="campaign-wizard" class="wizard">
<!-- Step 1: Basic Info -->
<div class="wizard-step active" data-step="1">
<h3>Campaign Details</h3>
<input type="text" name="name" placeholder="Campaign Name" required>
<select name="channel" required>
<option value="email">Email</option>
<option value="whatsapp">WhatsApp</option>
<option value="multi">Multi-Channel</option>
</select>
<button onclick="nextStep()">Next</button>
</div>
<!-- Step 2: Audience -->
<div class="wizard-step" data-step="2">
<h3>Select Audience</h3>
<div class="list-selector">
<!-- Load contact lists -->
<div hx-get="/api/crm/lists" hx-trigger="load"></div>
</div>
<button onclick="prevStep()">Back</button>
<button onclick="nextStep()">Next</button>
</div>
<!-- Step 3: Content -->
<div class="wizard-step" data-step="3">
<h3>Create Message</h3>
<select name="template" onchange="loadTemplate(this.value)">
<option value="">Start from scratch</option>
<!-- Load templates -->
</select>
<textarea name="message" rows="10"></textarea>
<button onclick="prevStep()">Back</button>
<button onclick="nextStep()">Next</button>
</div>
<!-- Step 4: Schedule -->
<div class="wizard-step" data-step="4">
<h3>Schedule Campaign</h3>
<label>
<input type="radio" name="schedule_type" value="now" checked>
Send immediately
</label>
<label>
<input type="radio" name="schedule_type" value="later">
Schedule for later
</label>
<input type="datetime-local" name="scheduled_at" id="schedule-datetime">
<button onclick="prevStep()">Back</button>
<button onclick="nextStep()">Next</button>
</div>
<!-- Step 5: Review -->
<div class="wizard-step" data-step="5">
<h3>Review & Launch</h3>
<div class="campaign-preview">
<!-- Show summary of all settings -->
</div>
<button onclick="prevStep()">Back</button>
<button onclick="launchCampaign()" class="btn-primary">Launch Campaign</button>
</div>
</div>
<script>
let currentStep = 1;
function nextStep() {
if (validateStep(currentStep)) {
document.querySelector(`[data-step="${currentStep}"]`).classList.remove('active');
currentStep++;
document.querySelector(`[data-step="${currentStep}"]`).classList.add('active');
}
}
function prevStep() {
document.querySelector(`[data-step="${currentStep}"]`).classList.remove('active');
currentStep--;
document.querySelector(`[data-step="${currentStep}"]`).classList.add('active');
}
function validateStep(step) {
const stepEl = document.querySelector(`[data-step="${step}"]`);
const inputs = stepEl.querySelectorAll('[required]');
for (let input of inputs) {
if (!input.value) {
input.focus();
return false;
}
}
return true;
}
</script>
```
### 6. Contact List Management
**What:** Create and manage segmented contact lists
**Implementation:**
```html
<div id="list-modal" class="modal">
<form id="list-form">
<h2>Create Contact List</h2>
<div class="form-group">
<label>List Name</label>
<input type="text" name="name" required>
</div>
<div class="form-group">
<label>Description</label>
<textarea name="description" rows="3"></textarea>
</div>
<div class="form-group">
<label>Import Contacts</label>
<div class="import-options">
<button type="button" onclick="showCsvImport()">
📄 Upload CSV
</button>
<button type="button" onclick="showCrmImport()">
👥 Import from CRM
</button>
<button type="button" onclick="showManualAdd()">
Add Manually
</button>
</div>
</div>
<div id="import-area" class="import-area">
<!-- Dynamic import UI -->
</div>
<div class="form-actions">
<button type="button" onclick="hideListModal()">Cancel</button>
<button type="submit">Create List</button>
</div>
</form>
</div>
<!-- CSV Import -->
<div id="csv-import" style="display: none;">
<input type="file" accept=".csv" onchange="handleCsvUpload(this)">
<div class="csv-preview">
<table id="csv-preview-table"></table>
</div>
<div class="csv-mapping">
<label>Map columns:</label>
<select name="name_column"></select>
<select name="email_column"></select>
<select name="phone_column"></select>
</div>
</div>
```
### 7. Message Template Editor
**What:** Rich template editor with variables and preview
**Implementation:**
```html
<div id="template-editor" class="template-editor">
<div class="editor-toolbar">
<button onclick="insertVariable('{{name}}')">Name</button>
<button onclick="insertVariable('{{company}}')">Company</button>
<button onclick="insertVariable('{{custom_field}}')">Custom Field</button>
<button onclick="toggleBold()"><strong>B</strong></button>
<button onclick="toggleItalic()"><em>I</em></button>
<button onclick="insertLink()">🔗</button>
<button onclick="insertImage()">🖼️</button>
</div>
<div class="editor-content">
<textarea id="template-content" name="content"></textarea>
</div>
<div class="editor-preview">
<h4>Preview</h4>
<div id="template-preview"></div>
</div>
</div>
<script>
function insertVariable(variable) {
const textarea = document.getElementById('template-content');
const pos = textarea.selectionStart;
const text = textarea.value;
textarea.value = text.slice(0, pos) + variable + text.slice(pos);
textarea.focus();
textarea.selectionStart = textarea.selectionEnd = pos + variable.length;
updatePreview();
}
function updatePreview() {
const content = document.getElementById('template-content').value;
const preview = document.getElementById('template-preview');
// Replace variables with sample data
const sampleData = {
name: 'John Doe',
company: 'Acme Corp',
custom_field: 'Sample Value'
};
let rendered = content;
for (let [key, value] of Object.entries(sampleData)) {
rendered = rendered.replace(new RegExp(`{{${key}}}`, 'g'), value);
}
preview.innerHTML = rendered;
}
document.getElementById('template-content').addEventListener('input', updatePreview);
</script>
```
### 8. Campaign Analytics Dashboard
**What:** Detailed metrics and performance tracking
**Implementation:**
```html
<div id="campaign-analytics" class="analytics-dashboard">
<div class="analytics-header">
<h2>Campaign Performance</h2>
<select id="analytics-timeframe" onchange="loadAnalytics()">
<option value="7d">Last 7 days</option>
<option value="30d">Last 30 days</option>
<option value="90d">Last 90 days</option>
<option value="all">All time</option>
</select>
</div>
<div class="analytics-grid">
<!-- Key Metrics -->
<div class="metric-card">
<span class="metric-label">Total Sent</span>
<span class="metric-value" hx-get="/api/crm/analytics/sent" hx-trigger="load">0</span>
<span class="metric-change positive">+12%</span>
</div>
<div class="metric-card">
<span class="metric-label">Open Rate</span>
<span class="metric-value" hx-get="/api/crm/analytics/open-rate" hx-trigger="load">0%</span>
<span class="metric-change positive">+5%</span>
</div>
<div class="metric-card">
<span class="metric-label">Click Rate</span>
<span class="metric-value" hx-get="/api/crm/analytics/click-rate" hx-trigger="load">0%</span>
<span class="metric-change negative">-2%</span>
</div>
<div class="metric-card">
<span class="metric-label">Conversion Rate</span>
<span class="metric-value" hx-get="/api/crm/analytics/conversion-rate" hx-trigger="load">0%</span>
<span class="metric-change positive">+8%</span>
</div>
</div>
<!-- Charts -->
<div class="analytics-charts">
<div class="chart-container">
<h3>Campaign Performance Over Time</h3>
<canvas id="performance-chart"></canvas>
</div>
<div class="chart-container">
<h3>Channel Breakdown</h3>
<canvas id="channel-chart"></canvas>
</div>
</div>
<!-- Top Campaigns -->
<div class="top-campaigns">
<h3>Top Performing Campaigns</h3>
<table class="analytics-table">
<thead>
<tr>
<th>Campaign</th>
<th>Channel</th>
<th>Sent</th>
<th>Opened</th>
<th>Clicked</th>
<th>Converted</th>
</tr>
</thead>
<tbody hx-get="/api/crm/analytics/top-campaigns" hx-trigger="load">
</tbody>
</table>
</div>
</div>
```
### 9. A/B Testing
**What:** Test different message variants
**Implementation:**
```html
<div class="ab-test-setup">
<h3>A/B Test Setup</h3>
<div class="variant-container">
<div class="variant">
<h4>Variant A (50%)</h4>
<textarea name="variant_a_content"></textarea>
<input type="range" name="variant_a_percentage" min="0" max="100" value="50">
</div>
<div class="variant">
<h4>Variant B (50%)</h4>
<textarea name="variant_b_content"></textarea>
<input type="range" name="variant_b_percentage" min="0" max="100" value="50">
</div>
</div>
<div class="test-settings">
<label>Test Duration</label>
<input type="number" name="test_duration" value="24"> hours
<label>Success Metric</label>
<select name="success_metric">
<option value="open_rate">Open Rate</option>
<option value="click_rate">Click Rate</option>
<option value="conversion_rate">Conversion Rate</option>
</select>
<label>
<input type="checkbox" name="auto_select_winner">
Automatically send winning variant to remaining contacts
</label>
</div>
</div>
```
### 10. WhatsApp Integration
**What:** Send WhatsApp campaigns with media support
**Implementation:**
```html
<div class="whatsapp-composer">
<div class="message-preview">
<div class="whatsapp-bubble">
<div class="message-content" id="whatsapp-preview">
<!-- Preview of message -->
</div>
</div>
</div>
<div class="message-editor">
<textarea name="message" placeholder="Type your message..."
oninput="updateWhatsAppPreview()"></textarea>
<div class="media-attachments">
<button onclick="attachImage()">📷 Image</button>
<button onclick="attachVideo()">🎥 Video</button>
<button onclick="attachDocument()">📄 Document</button>
</div>
<div class="quick-replies">
<h4>Quick Reply Buttons</h4>
<div id="quick-replies-list">
<input type="text" placeholder="Button 1" name="quick_reply_1">
<input type="text" placeholder="Button 2" name="quick_reply_2">
<input type="text" placeholder="Button 3" name="quick_reply_3">
</div>
</div>
</div>
</div>
```
## Continued in CAMPAIGNS-PART2.md...