gb/CAMPAIGNS-PART2.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

13 KiB

Campaigns App Improvements - Part 2

Additional Features (Continued)

11. Drip Campaign Automation

What: Multi-step automated email sequences

Implementation:

<div class="drip-campaign-builder">
    <div class="drip-timeline">
        <div class="drip-step" data-step="1">
            <div class="step-header">
                <span class="step-number">1</span>
                <input type="text" placeholder="Step name" value="Welcome Email">
            </div>
            <div class="step-content">
                <select name="trigger">
                    <option value="immediate">Send immediately</option>
                    <option value="delay">Wait X days</option>
                    <option value="condition">Wait for condition</option>
                </select>
                <input type="number" name="delay_days" placeholder="Days">
                <textarea name="content" placeholder="Email content"></textarea>
            </div>
            <button onclick="addDripStep()">+ Add Step</button>
        </div>
    </div>
</div>

<script>
function addDripStep() {
    const timeline = document.querySelector('.drip-timeline');
    const stepCount = timeline.querySelectorAll('.drip-step').length + 1;
    
    const newStep = document.createElement('div');
    newStep.className = 'drip-step';
    newStep.dataset.step = stepCount;
    newStep.innerHTML = `
        <div class="step-header">
            <span class="step-number">${stepCount}</span>
            <input type="text" placeholder="Step name">
        </div>
        <div class="step-content">
            <select name="trigger">
                <option value="delay">Wait X days</option>
                <option value="condition">Wait for condition</option>
            </select>
            <input type="number" name="delay_days" placeholder="Days">
            <textarea name="content" placeholder="Email content"></textarea>
        </div>
        <button onclick="removeDripStep(${stepCount})">Remove</button>
    `;
    
    timeline.appendChild(newStep);
}
</script>

12. Unsubscribe Management

What: Handle opt-outs and preferences

Implementation:

<!-- Unsubscribe page -->
<div class="unsubscribe-page">
    <h2>Manage Your Preferences</h2>
    
    <div class="preference-options">
        <label>
            <input type="checkbox" name="marketing_emails" checked>
            Marketing emails
        </label>
        <label>
            <input type="checkbox" name="product_updates" checked>
            Product updates
        </label>
        <label>
            <input type="checkbox" name="newsletters" checked>
            Newsletters
        </label>
    </div>
    
    <div class="frequency-control">
        <label>Email frequency</label>
        <select name="frequency">
            <option value="daily">Daily</option>
            <option value="weekly">Weekly</option>
            <option value="monthly">Monthly</option>
        </select>
    </div>
    
    <div class="unsubscribe-actions">
        <button onclick="savePreferences()">Save Preferences</button>
        <button onclick="unsubscribeAll()" class="danger">Unsubscribe from All</button>
    </div>
</div>

<!-- Backend tracking -->
<script>
async function unsubscribeAll() {
    const token = new URLSearchParams(window.location.search).get('token');
    
    await fetch('/api/crm/unsubscribe', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ token, unsubscribe_all: true })
    });
    
    showMessage('You have been unsubscribed from all campaigns.');
}
</script>

13. Campaign Duplication

What: Clone existing campaigns for quick setup

Implementation:

async function duplicateCampaign(campaignId) {
    const response = await fetch(`/api/crm/campaigns/${campaignId}`);
    const campaign = await response.json();
    
    // Modify name
    campaign.name = `${campaign.name} (Copy)`;
    campaign.status = 'draft';
    delete campaign.id;
    delete campaign.created_at;
    
    // Create new campaign
    const newCampaign = await fetch('/api/crm/campaigns', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(campaign)
    });
    
    // Refresh list
    htmx.ajax('GET', '/api/crm/campaigns', '#campaignsList');
}

14. Campaign Scheduling Calendar

What: Visual calendar for scheduled campaigns

Implementation:

<div class="campaign-calendar">
    <div class="calendar-header">
        <button onclick="previousMonth()"></button>
        <h3 id="calendar-month">March 2026</h3>
        <button onclick="nextMonth()"></button>
    </div>
    
    <div class="calendar-grid">
        <!-- Days of week -->
        <div class="calendar-day-header">Sun</div>
        <div class="calendar-day-header">Mon</div>
        <div class="calendar-day-header">Tue</div>
        <div class="calendar-day-header">Wed</div>
        <div class="calendar-day-header">Thu</div>
        <div class="calendar-day-header">Fri</div>
        <div class="calendar-day-header">Sat</div>
        
        <!-- Calendar days with campaigns -->
        <div class="calendar-day" data-date="2026-03-15">
            <span class="day-number">15</span>
            <div class="day-campaigns">
                <div class="campaign-pill" data-id="123">
                    Welcome Series
                </div>
            </div>
        </div>
        <!-- ... more days -->
    </div>
</div>

<style>
.calendar-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 1px;
    background: var(--border);
}

.calendar-day {
    background: var(--surface);
    min-height: 100px;
    padding: 8px;
}

.campaign-pill {
    background: var(--accent);
    color: var(--bg);
    padding: 4px 8px;
    border-radius: 4px;
    font-size: 11px;
    margin-top: 4px;
    cursor: pointer;
}
</style>

15. Email Deliverability Monitoring

What: Track bounce rates and spam complaints

Implementation:

<div class="deliverability-dashboard">
    <h3>Email Health</h3>
    
    <div class="health-metrics">
        <div class="health-metric">
            <span class="metric-label">Bounce Rate</span>
            <span class="metric-value">2.3%</span>
            <div class="metric-bar">
                <div class="metric-fill" style="width: 2.3%; background: green;"></div>
            </div>
            <span class="metric-status good">Good (< 5%)</span>
        </div>
        
        <div class="health-metric">
            <span class="metric-label">Spam Complaints</span>
            <span class="metric-value">0.1%</span>
            <div class="metric-bar">
                <div class="metric-fill" style="width: 0.1%; background: green;"></div>
            </div>
            <span class="metric-status good">Good (< 0.5%)</span>
        </div>
        
        <div class="health-metric">
            <span class="metric-label">Unsubscribe Rate</span>
            <span class="metric-value">1.8%</span>
            <div class="metric-bar">
                <div class="metric-fill" style="width: 1.8%; background: yellow;"></div>
            </div>
            <span class="metric-status warning">Monitor (< 2%)</span>
        </div>
    </div>
    
    <div class="bounce-details">
        <h4>Recent Bounces</h4>
        <table>
            <thead>
                <tr>
                    <th>Email</th>
                    <th>Type</th>
                    <th>Reason</th>
                    <th>Date</th>
                </tr>
            </thead>
            <tbody hx-get="/api/crm/bounces" hx-trigger="load">
            </tbody>
        </table>
    </div>
</div>

Performance Optimizations

16. Pagination for Large Lists

<div class="campaigns-pagination">
    <button hx-get="/api/crm/campaigns?page=1" hx-target="#campaignsList">First</button>
    <button hx-get="/api/crm/campaigns?page={{prev_page}}" hx-target="#campaignsList">Previous</button>
    <span>Page {{current_page}} of {{total_pages}}</span>
    <button hx-get="/api/crm/campaigns?page={{next_page}}" hx-target="#campaignsList">Next</button>
    <button hx-get="/api/crm/campaigns?page={{total_pages}}" hx-target="#campaignsList">Last</button>
</div>

17. Infinite Scroll

<div class="campaigns-grid" 
     hx-get="/api/crm/campaigns?page=1" 
     hx-trigger="load"
     hx-swap="innerHTML">
</div>

<div hx-get="/api/crm/campaigns?page=2"
     hx-trigger="intersect once"
     hx-swap="beforeend"
     hx-target=".campaigns-grid">
</div>
<div class="campaign-search">
    <input type="text" 
           placeholder="Search campaigns..."
           hx-get="/api/crm/campaigns/search"
           hx-trigger="keyup changed delay:300ms"
           hx-target="#campaignsList"
           hx-include="[name='status'],[name='channel']">
    
    <select name="status" hx-get="/api/crm/campaigns" hx-trigger="change" hx-target="#campaignsList">
        <option value="">All Statuses</option>
        <option value="draft">Draft</option>
        <option value="scheduled">Scheduled</option>
        <option value="running">Running</option>
        <option value="completed">Completed</option>
    </select>
    
    <select name="channel" hx-get="/api/crm/campaigns" hx-trigger="change" hx-target="#campaignsList">
        <option value="">All Channels</option>
        <option value="email">Email</option>
        <option value="whatsapp">WhatsApp</option>
        <option value="social">Social</option>
    </select>
</div>

Backend Requirements

API Endpoints Needed

GET  /api/crm/campaigns?page={page}&status={status}&channel={channel}
POST /api/crm/campaigns
GET  /api/crm/campaigns/{id}
PUT  /api/crm/campaigns/{id}
DELETE /api/crm/campaigns/{id}
POST /api/crm/campaigns/{id}/launch
POST /api/crm/campaigns/{id}/pause
POST /api/crm/campaigns/{id}/duplicate

GET  /api/crm/lists
POST /api/crm/lists
GET  /api/crm/lists/{id}/contacts
POST /api/crm/lists/{id}/contacts

GET  /api/crm/templates
POST /api/crm/templates
GET  /api/crm/templates/{id}
PUT  /api/crm/templates/{id}

GET  /api/crm/analytics/sent
GET  /api/crm/analytics/open-rate
GET  /api/crm/analytics/click-rate
GET  /api/crm/analytics/conversion-rate
GET  /api/crm/analytics/top-campaigns

GET  /api/crm/bounces
POST /api/crm/unsubscribe

Database Schema Additions

-- Campaign tracking
CREATE TABLE campaign_sends (
    id UUID PRIMARY KEY,
    campaign_id UUID REFERENCES campaigns(id),
    contact_id UUID,
    sent_at TIMESTAMP,
    opened_at TIMESTAMP,
    clicked_at TIMESTAMP,
    converted_at TIMESTAMP,
    bounced BOOLEAN DEFAULT FALSE,
    bounce_reason TEXT,
    unsubscribed BOOLEAN DEFAULT FALSE
);

-- Contact lists
CREATE TABLE contact_lists (
    id UUID PRIMARY KEY,
    name VARCHAR(255),
    description TEXT,
    created_at TIMESTAMP,
    contact_count INTEGER DEFAULT 0
);

CREATE TABLE list_contacts (
    list_id UUID REFERENCES contact_lists(id),
    contact_id UUID,
    added_at TIMESTAMP,
    PRIMARY KEY (list_id, contact_id)
);

-- Templates
CREATE TABLE message_templates (
    id UUID PRIMARY KEY,
    name VARCHAR(255),
    channel VARCHAR(50),
    subject TEXT,
    content TEXT,
    variables JSONB,
    created_at TIMESTAMP
);

-- A/B Tests
CREATE TABLE ab_tests (
    id UUID PRIMARY KEY,
    campaign_id UUID REFERENCES campaigns(id),
    variant_a_content TEXT,
    variant_b_content TEXT,
    variant_a_percentage INTEGER,
    variant_b_percentage INTEGER,
    success_metric VARCHAR(50),
    winner VARCHAR(1), -- 'A' or 'B'
    completed_at TIMESTAMP
);

-- Unsubscribes
CREATE TABLE unsubscribes (
    id UUID PRIMARY KEY,
    email VARCHAR(255),
    campaign_id UUID,
    unsubscribed_at TIMESTAMP,
    reason TEXT
);

Implementation Priority

  1. Critical (Do First):

    • Fix backend API endpoints
    • Add campaign card HTML template
    • Fix form submission and list refresh
    • Fix channel filtering
  2. High Priority:

    • Campaign builder wizard
    • Contact list management
    • Message template editor
    • Campaign analytics dashboard
  3. Medium Priority:

    • A/B testing
    • WhatsApp integration
    • Drip campaign automation
    • Unsubscribe management
  4. Nice to Have:

    • Campaign duplication
    • Scheduling calendar
    • Deliverability monitoring
    • Infinite scroll

Testing Checklist

  • Create new campaign via wizard
  • Upload contact list from CSV
  • Create message template with variables
  • Schedule campaign for future date
  • Launch campaign immediately
  • View campaign analytics
  • Filter campaigns by status/channel
  • Search campaigns by name
  • Duplicate existing campaign
  • Set up A/B test
  • Create drip campaign sequence
  • Handle unsubscribe requests
  • Track bounces and spam complaints
  • Send WhatsApp campaign with media
  • View campaign calendar

Integration Points

With CRM App

  • Import contacts from CRM deals/accounts
  • Track campaign responses in deal activities
  • Create deals from campaign conversions

With Bot Conversations

  • Trigger campaigns from bot conversations
  • Use bot data to personalize messages
  • Track campaign responses in bot analytics

With Email Service

  • Integrate with SendGrid/Mailgun/SES
  • Handle webhooks for opens/clicks/bounces
  • Manage sender reputation

With WhatsApp Business API

  • Send template messages
  • Handle incoming responses
  • Track delivery status