"use client" import { useState, useEffect, useCallback, useRef } from "react" import { Loader2, Check, Send, Sparkles, Trophy, CheckCheck, ChevronDown, Clock, MessageCircle } from "lucide-react" import Link from "next/link" import { resolvePreview, STEP_META } from "@/lib/templates" /** * /dashboard/automations * * THE PAGE IS THE CONVERSATION. * * Aaisha's question: "What do my donors get?" * Answer: a WhatsApp chat showing all 4 messages, in order, * with timestamps between them. That's the entire page. * * Click a message → it becomes editable (inline). * Click ✨ → AI generates a smarter version. * That's it. * * No tabs. No matrix. No toolbar. No panels. * Just a phone with the messages your donors receive. */ interface Template { id: string; step: number; channel: string; variant: string name: string; subject: string | null; body: string isActive: boolean; splitPercent: number sentCount: number; convertedCount: number } interface Config { isActive: boolean; step1Delay: number; step2Delay: number; step3Delay: number strategy: string; channelMatrix: Record | null } interface ChannelStatus { whatsapp: boolean; email: { provider: string; fromAddress: string } | null; sms: { provider: string; fromNumber: string } | null } interface Stats { whatsapp: { sent: number; failed: number }; email: { sent: number; failed: number }; sms: { sent: number; failed: number }; total: number; deliveryRate: number } export default function AutomationsPage() { const [loading, setLoading] = useState(true) const [templates, setTemplates] = useState([]) const [config, setConfig] = useState(null) const [channels, setChannels] = useState(null) const [stats, setStats] = useState(null) const [editing, setEditing] = useState(null) // step being edited const [editBody, setEditBody] = useState("") const [saving, setSaving] = useState(false) const [saved, setSaved] = useState(null) const [aiLoading, setAiLoading] = useState(null) // step being AI'd const [showTiming, setShowTiming] = useState(false) const editorRef = useRef(null) const load = useCallback(async () => { try { const res = await fetch("/api/automations") const data = await res.json() if (data.templates) setTemplates(data.templates) if (data.config) setConfig(data.config) if (data.channels) setChannels(data.channels) if (data.stats) setStats(data.stats) } catch { /* */ } setLoading(false) }, []) useEffect(() => { load() }, [load]) // Get template for a step (WhatsApp variant A preferred) const tpl = (step: number, variant = "A") => templates.find(t => t.step === step && t.channel === "whatsapp" && t.variant === variant) || templates.find(t => t.step === step && t.variant === variant) const startEdit = (step: number) => { const t = tpl(step) if (t) { setEditBody(t.body); setEditing(step) } setTimeout(() => editorRef.current?.focus(), 50) } const cancelEdit = () => { setEditing(null); setEditBody("") } const saveEdit = async (step: number) => { setSaving(true) const t = tpl(step) try { await fetch("/api/automations", { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ templates: [{ step, channel: t?.channel || "whatsapp", variant: "A", name: t?.name || "Message", body: editBody }] }), }) setSaved(step); setEditing(null) setTimeout(() => setSaved(null), 2000) await load() } catch { /* */ } setSaving(false) } const aiGenerate = async (step: number) => { setAiLoading(step) try { const res = await fetch("/api/automations/ai", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "generate_variant", step, channel: "whatsapp" }), }) const data = await res.json() if (data.ok) await load() } catch { /* */ } setAiLoading(null) } const pickWinners = async () => { setAiLoading(-1) try { const res = await fetch("/api/automations/ai", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "check_winners" }), }) if (res.ok) await load() } catch { /* */ } setAiLoading(null) } const removeVariant = async (step: number) => { try { await fetch("/api/automations", { method: "DELETE", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ step, channel: "whatsapp", variant: "B" }), }) await load() } catch { /* */ } } const saveTiming = async (key: string, value: number) => { try { await fetch("/api/automations", { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ config: { [key]: value } }), }) await load() } catch { /* */ } } if (loading) return
const waConnected = !!channels?.whatsapp const anyAB = templates.some(t => t.variant === "B") const delays = [0, config?.step1Delay || 2, config?.step2Delay || 7, config?.step3Delay || 14] return (
{/* Header — one line */}

What your donors receive

4 messages over {delays[3]} days. Click any to edit.

{/* Status line */} {!waConnected ? (

WhatsApp not connected. These messages will start sending once you connect WhatsApp.

) : stats && stats.total > 0 ? (

Working · {stats.total} sent this week · {stats.deliveryRate}% delivered

) : (

Connected · Messages will send as donors pledge

)} {/* Pick winners button — only when A/B tests exist */} {anyAB && ( )} {/* ── THE CONVERSATION ── */}
{/* WhatsApp header */}

Your charity

Automated messages

{/* Chat area */}
{STEP_META.map((meta, step) => { const a = tpl(step, "A") const b = tpl(step, "B") const isEditing = editing === step const justSaved = saved === step const isAiLoading = aiLoading === step const delay = delays[step] const previewA = a ? resolvePreview(a.body) : "" const previewB = b ? resolvePreview(b.body) : "" return (
{/* Timestamp divider */}
{step === 0 ? "Instantly" : `Day ${delay} · if not paid`}
{/* A/B split — two bubbles stacked */} {b && !isEditing ? (
startEdit(step)} /> startEdit(step)} isAI />
) : isEditing ? ( /* ── Editing mode ── */