// ─── Automations Page ──────────────────────────────────────────────────────── const AUTO_JOB_STATUS = { sent: 'green', pending: 'amber', failed: 'red', processing: 'blue', launched: 'purple', completed: 'green' }; const AUTO_CHANNEL_C = { whatsapp: 'green', call: 'blue' }; function AutomationsPage() { const [autoTab, setAutoTab] = React.useState('Jobs'); const [jobs, setJobs] = React.useState([]); const [templates, setTemplates] = React.useState([]); const [assets, setAssets] = React.useState([]); const [loading, setLoading] = React.useState(true); const [statusFilter, setStatusFilter] = React.useState('all'); const [channelFilter, setChannelFilter] = React.useState('all'); const reload = () => Promise.all([ fetch('/api/automation/jobs').then(r => r.json()).catch(() => []), fetch('/api/whatsapp/templates').then(r => r.json()).catch(() => []), fetch('/api/message-assets').then(r => r.json()).catch(() => []), ]).then(([j, t, a]) => { setJobs(Array.isArray(j) ? j : (Array.isArray(j?.items) ? j.items : [])); setTemplates(Array.isArray(t.items) ? t.items : []); setAssets(Array.isArray(a) ? a : (Array.isArray(a?.items) ? a.items : [])); setLoading(false); }); React.useEffect(() => { reload(); }, []); const filtered = jobs.filter(j => { if (statusFilter !== 'all' && j.status !== statusFilter) return false; if (channelFilter !== 'all' && j.channel !== channelFilter) return false; return true; }); const stats = { total: jobs.length, sent: jobs.filter(j => j.status === 'sent' || j.status === 'completed').length, pending: jobs.filter(j => j.status === 'pending').length, failed: jobs.filter(j => j.status === 'failed').length, }; const handleRun = async id => { await fetch(`/api/automation/jobs/${id}/launch-call`, { method: 'POST' }).catch(() => {}); reload(); }; const handleRetry = async id => { await fetch(`/api/automation/jobs/${id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ status: 'pending' }), }).catch(() => {}); reload(); }; const handleDelete = async id => { await fetch(`/api/automation/jobs/${id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ status: 'cancelled' }), }).catch(() => {}); setJobs(p => p.filter(j => j.id !== id)); }; return (
{autoTab === 'Jobs' && (loading ? : (
}> [
{j.contact_name || 'Unknown'}
{j.phone_number}
, {j.channel}, {(j.trigger || j.type || '').replace(/_/g, ' ')}, {j.template_name || '—'}, {j.scheduled_for ? new Date(j.scheduled_for).toLocaleString('en-IN', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', hour12: true }) : '—'}, {j.status},
{j.status === 'pending' && handleRun(j.id)}>Run} {j.status === 'failed' && handleRetry(j.id)}>Retry} handleDelete(j.id)}>Del
, ])} /> ))} {autoTab === 'Template Manager' && (
{templates.length === 0 ? (
No templates yet.
) : templates.map(t => (
{t.name} {t.active !== false ? 'Active' : 'Inactive'} {t.category && {t.category}}
{t.body &&
{t.body}
} {t.variables &&
Variables: {t.variables.map(v => `{${v}}`).join(', ')}
}
Edit Preview
))} + New Template
)} {autoTab === 'Asset Library' && (
Upload Asset { const file = e.target.files?.[0]; if (!file) return; const fd = new FormData(); fd.append('file', file); await fetch('/api/message-assets/upload', { method: 'POST', body: fd }).catch(() => null); reload(); }} /> }> [ {a.filename || a.name}, {a.content_type?.split('/')[1] || 'file'}, {a.size ? `${(a.size / 1024).toFixed(0)} KB` : '—'}, {a.public_url || a.url || '—'},
navigator.clipboard?.writeText(a.public_url || a.url || '')}>Copy URL Delete
, ])} />
)}
); } Object.assign(window, { AutomationsPage });