/* global React, Icon, StatusPill, fmtAED */ const { useState } = React; /* ============================================================ LINKEDIN CAMPAIGN — list + builder Connection campaigns: pick a lead list, set a daily cap, send N connection requests/day until the list is finished. ============================================================ */ const liTh = { padding: '10px 20px', textAlign: 'left', fontSize: 9, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--text-dim)' }; const liTd = { padding: '12px 20px', verticalAlign: 'middle', color: 'var(--text-secondary)' }; function LinkedInCampaigns({ goto }) { return (
Outreach · LinkedIn

LinkedIn Campaign

{window.LINKEDIN_CAMPAIGNS.length} campaigns · {window.LINKEDIN_CAMPAIGNS.filter(c => c.status === 'active').length} active
{window.LINKEDIN_CAMPAIGNS.map(c => { const list = window.LEAD_LISTS.find(l => l.id === c.listId); const total = list?.count || 0; const pct = total ? Math.round((c.sent / total) * 100) : 0; const acceptRate = c.sent ? ((c.accepted / c.sent) * 100).toFixed(1) : '0.0'; const replyRate = c.accepted ? ((c.replied / c.accepted) * 100).toFixed(1) : '0.0'; return ( goto('linkedin-builder', c.id)} style={{ borderBottom: '1px solid var(--divider)', cursor: 'pointer' }}> ); })}
Name Lead List Status Daily Cap Progress Accepted Replied
{c.name}
{c.account}
{list?.name || '—'}
{total} leads
{c.dailyCap}/day
{c.sent}/{total}
{c.accepted} ({acceptRate}%) {c.replied} ({replyRate}%)
); } function LinkedInCampaignBuilder({ campaignId, goto }) { const c = window.LINKEDIN_CAMPAIGNS.find(x => x.id === campaignId) || window.LINKEDIN_CAMPAIGNS[0]; const [listId, setListId] = useState(c.listId); const [cap, setCap] = useState(c.dailyCap); const [account, setAccount] = useState(c.account); const list = window.LEAD_LISTS.find(l => l.id === listId); const total = list?.count || 0; const pct = total ? Math.round((c.sent / total) * 100) : 0; const remaining = Math.max(0, total - c.sent); const eta = cap > 0 ? Math.ceil(remaining / cap) : 0; const acceptRate = c.sent ? ((c.accepted / c.sent) * 100).toFixed(1) : '0.0'; return (

{c.name}

{c.status === 'draft' && } {c.status === 'active' && }
{/* LIVE PROGRESS — shown when active/completed */} {(c.status === 'active' || c.status === 'completed') && (
Progress
{pct}% complete {remaining} remaining
)} {/* SETUP */}
1
Lead List
Which list to send connection requests from
{window.LEAD_LISTS.map(l => ( ))}
{/* SENDING ACCOUNT */}
2
Sending Account
Which connected LinkedIn account sends from
{/* DAILY CAP + INVITE NOTE */}
3
Daily Send Cap
Connection requests per day until the list is finished
setCap(parseInt(e.target.value))} style={{ flex: 1, accentColor: 'var(--accent)' }} />
{cap} /day
5 (safe) 20 (recommended) 50 (aggressive)
Sending {cap} per day to {total} leads {' · finishes in '} ~{Math.ceil(total / cap)} days
{/* INVITE MESSAGE */}
4
Invite Message
Optional · 200 char max · sent with the connection request