'use client'; import { useState } from 'react'; import { useRouter } from 'next/navigation'; import { createClient } from '@/lib/supabase/client'; import { useAuth } from '@/hooks/use-auth'; import { toast } from 'sonner'; import { MessageTemplate } from '@/types'; import { Step1ChooseTemplate } from '@/components/broadcasts/step1-choose-template'; import { Step2SelectAudience } from '@/components/broadcasts/step2-select-audience'; import { Step3Personalize } from '@/components/broadcasts/step3-personalize'; import { Step4ScheduleSend } from '@/components/broadcasts/step4-schedule-send'; import { useBroadcastSending } from '@/hooks/use-broadcast-sending'; import { Check } from 'lucide-react'; const steps = [ { label: 'Template', key: 'template' }, { label: 'Audience', key: 'audience' }, { label: 'Personalize', key: 'personalize' }, { label: 'Send', key: 'send' }, ] as const; export default function NewBroadcastPage() { const router = useRouter(); const { accountId } = useAuth(); const { createAndSendBroadcast, isProcessing, progress } = useBroadcastSending(); const [currentStep, setCurrentStep] = useState(0); const [template, setTemplate] = useState(null); const [audience, setAudience] = useState<{ type: 'all' | 'tags' | 'custom_field' | 'csv'; tagIds?: string[]; customField?: { fieldId: string; operator: 'is' | 'is_not' | 'contains'; value: string; }; csvContacts?: { phone: string; name?: string }[]; excludeTagIds?: string[]; }>({ type: 'all' }); const [variables, setVariables] = useState< Record >({}); const [name, setName] = useState(''); async function handleSend() { if (!template) return; try { const broadcastId = await createAndSendBroadcast({ name, template, audience: { type: audience.type, tagIds: audience.tagIds, customField: audience.customField, csvContacts: audience.csvContacts, excludeTagIds: audience.excludeTagIds, }, variables, }); router.push(`/broadcasts/${broadcastId}`); } catch (err) { // Previously swallowed with console.error — the wizard would // just no-op, leaving the user confused. Surface the reason. const message = err instanceof Error ? err.message : 'Broadcast failed'; console.error('Broadcast failed:', err); toast.error(message); } } /** * Writes a draft broadcast row — no recipients, no sending. The user * can revisit it via the list page to finish the flow later. We * don't persist the in-progress audience/variable config here * because the current schema doesn't carry it past `audience_filter` * and `template_variables`; those are enough for the user to * recognize the draft but not to exactly round-trip into the wizard. * A full resume-draft UX is a future polish. */ async function handleSaveDraft() { if (!template || !name.trim()) { toast.error('Give the broadcast a name before saving a draft.'); return; } const supabase = createClient(); const { data: { session }, } = await supabase.auth.getSession(); const user = session?.user; if (!user) { toast.error('Not signed in.'); return; } if (!accountId) { toast.error('Your profile is not linked to an account.'); return; } const { error } = await supabase.from('broadcasts').insert({ user_id: user.id, account_id: accountId, name: name.trim(), template_name: template.name, template_language: template.language ?? 'en_US', template_variables: variables, audience_filter: { type: audience.type, tagIds: audience.tagIds, }, status: 'draft', total_recipients: 0, sent_count: 0, delivered_count: 0, read_count: 0, replied_count: 0, failed_count: 0, }); if (error) { toast.error(`Failed to save draft: ${error.message}`); return; } toast.success('Draft saved'); router.push('/broadcasts'); } return (
{/* Header */}

New Broadcast

Create and send a broadcast message to your contacts.

{/* Step Indicator */}
{steps.map((step, index) => { const isActive = index === currentStep; const isCompleted = index < currentStep; return (
{isCompleted ? : index + 1}
{step.label}
{index < steps.length - 1 && (
)}
); })}
{/* Step Content */}
{currentStep === 0 && ( setCurrentStep(1)} onBack={() => router.push('/broadcasts')} /> )} {currentStep === 1 && ( setCurrentStep(2)} onBack={() => setCurrentStep(0)} /> )} {currentStep === 2 && template && ( setCurrentStep(3)} onBack={() => setCurrentStep(1)} /> )} {currentStep === 3 && template && ( setCurrentStep(2)} isProcessing={isProcessing} progress={progress} /> )}
); }