import React, { useState, useEffect, useCallback } from 'react' import type { DashboardData } from '../../types' interface CampaignFormData { name: string; send_whatsapp: number; send_email: number; send_meta: number | boolean; instances: string[]; role_ids: string[]; user_ids: string[]; external_numbers: string; external_emails: string; billing_countries: string[]; wp_profile_languages: string[]; total_spent_min: string | number; orders_count_min: string | number; only_verified_phone: number; woo_order_statuses: string[]; woo_ordered_products: string[]; email_subject: string; email_message: string; email_content_json: string; message: string; start_datetime: string; min_whatsapp_interval: string | number; max_whatsapp_interval: string | number; wa_daily_limit: string | number; min_email_interval: string | number; max_email_interval: string | number; email_daily_limit: string | number; repeat_campaign: string; message_type: string; media_url: string; share_post_id: string; share_product_id: string; append_info: number; post_info: { title: number; excerpt: number; link: number; image: number;[key: string]: number }; product_info: { title: number; price: number; description: number; link: number; image: number;[key: string]: number }; email_template_id: string; meta_message_type: string; meta_template_id: string; repeat_days: string | number; append_post: number; append_product: number; // For other potential dynamic fields [key: string]: unknown; } import { Users, Send, Clock, ChevronRight, ChevronLeft, CheckCircle2, Zap, Rocket, Type, Loader2, ShoppingCart, Hash, Image as ImageIcon, Mail as MailIcon, CheckCircle2 as CheckIcon } from 'lucide-react' import { AdminButton, SecondaryButton, GhostButton, SettingInput } from '../ui/settings-ui' import { Label } from '../ui/label' import { Textarea } from '../ui/textarea' import { Separator } from '../ui/separator' import { Switch } from '../ui/switch' import { Checkbox } from '../ui/checkbox' import { motion, AnimatePresence } from 'framer-motion' import { Badge } from '../ui/badge' import { cn } from "src/lib/utils" import { MultiSelectTagify } from '../ui/multi-select-tagify' import { SearchableSelect } from '../ui/searchable-select' import { PersonalizationDialog } from '../ui/personalization-dialog' import { WhatsAppRuleConfig } from '../ui/whatsapp-rule-config' import { EmojiPickerButton } from '../ui/whatsapp-toolbar' import { ModernCardHeader, SettingCard } from '../ui/settings-ui' interface MetaTemplate { name: string; langs: string[]; id?: string | number; label?: string; value?: string; language?: string; } interface WizardProps { initialData?: Partial; onSave: (data: CampaignFormData) => void; onCancel: () => void; meta: { roles: { label: string; id: string }[]; users: { label: string; id: string }[]; products: { label: string; id: string }[]; posts: { label: string; id: string }[]; email_templates: { name: string; id: string }[]; instances: { label: string; id: string }[]; woo_countries: Record; templates: MetaTemplate[]; icon_base_url: string; nonces: { calc: string; preview: string; }; rootData: DashboardData; } } const getSteps = () => [ { id: 'basic', title: 'Channels', icon: }, { id: 'audience', title: 'Audience', icon: }, { id: 'filters', title: 'WooCommerce', icon: }, { id: 'content', title: 'Content', icon: }, { id: 'schedule', title: 'Scheduling', icon: }, { id: 'confirm', title: 'Review', icon: }, ] export default function CampaignWizard({ initialData, onSave, onCancel, meta }: WizardProps) { const steps = getSteps() const [currentStep, setCurrentStep] = useState(0) const [formData, setFormData] = useState(() => { const base = initialData || { name: '', send_whatsapp: 1, send_email: 0, send_meta: 0, instances: [], role_ids: [], user_ids: [], external_numbers: '', external_emails: '', billing_countries: [], wp_profile_languages: [], total_spent_min: 0, orders_count_min: 0, only_verified_phone: 0, woo_order_statuses: [], woo_ordered_products: [], email_subject: '', email_message: '', email_content_json: '', message: '', start_datetime: '', min_whatsapp_interval: 5, max_whatsapp_interval: 10, wa_daily_limit: 500, min_email_interval: 5, max_email_interval: 10, email_daily_limit: 0, repeat_campaign: 'no_repeat', message_type: 'text', media_url: '', share_post_id: '', share_product_id: '', append_info: 0, post_info: { title: 1, excerpt: 1, link: 1, image: 0 }, product_info: { title: 1, price: 1, description: 0, link: 1, image: 0 }, email_template_id: '', meta_message_type: 'session', meta_template_id: '', repeat_days: 1, append_post: 0, append_product: 0, }; if (!initialData) { const saved = localStorage.getItem('wawp_campaign_wizard_draft'); if (saved) { try { const parsed = JSON.parse(saved); return { ...base, ...parsed }; } catch (e: unknown) { console.error("Failed to load draft", e); } } } return base; }); const [isCalculating, setIsCalculating] = useState(false) const [recipientCount, setRecipientCount] = useState(null) const [personalizationOpen, setPersonalizationOpen] = useState(false) // Render-time sync for initialData changes const [prevInitialData, setPrevInitialData] = useState(initialData); if (initialData !== prevInitialData) { setPrevInitialData(initialData); if (initialData) { const normalized = { ...initialData } as Record; ['role_ids', 'user_ids', 'billing_countries', 'wp_profile_languages', 'woo_order_statuses', 'woo_ordered_products'].forEach(key => { if (normalized[key] && typeof normalized[key] === 'string') { normalized[key] = (normalized[key] as string).split(',').filter(Boolean); } else if (!normalized[key]) { normalized[key] = []; } }); if (normalized.repeat_type && !normalized.repeat_campaign) { normalized.repeat_campaign = normalized.repeat_type; } setFormData(normalized as CampaignFormData); } } const calculateRecipients = useCallback(async () => { if (Object.keys(formData).length < 5) return setIsCalculating(true) try { const res = await fetch(`${meta.rootData.global.apiRestUrl}/campaigns/calc`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': meta.rootData.global.wpRestNonce }, body: JSON.stringify(formData) }) const json = await res.json() const n = parseInt(json.data?.count || json.data || 0) setRecipientCount(isNaN(n) ? -1 : n) } catch (e: unknown) { console.error("Calculation failed", e) setRecipientCount(-1) } finally { setIsCalculating(false) } }, [formData, meta.rootData.global.apiRestUrl, meta.rootData.global.wpRestNonce]) useEffect(() => { const timer = setTimeout(() => { calculateRecipients() }, 1000) return () => clearTimeout(timer) }, [calculateRecipients]) // draftLoaded ref is no longer needed since we initialize from localStorage directly // useEffect for loading draft is removed to avoid set-state-in-effect error useEffect(() => { if (!initialData) { localStorage.setItem('wawp_campaign_wizard_draft', JSON.stringify(formData)); } }, [formData, initialData]); const handleNext = () => { if (currentStep < steps.length - 1) { setCurrentStep(currentStep + 1) } } const handleBack = () => { if (currentStep > 0) { setCurrentStep(currentStep - 1) } } const updateField = (field: string, value: unknown) => { setFormData((prev: CampaignFormData) => ({ ...prev, [field]: value })) } const toggleArrayItem = (field: string, item: string) => { setFormData((prev: CampaignFormData) => { const arr = (prev[field] as string[]) || [] const newArr = arr.includes(item) ? arr.filter((i: string) => i !== item) : [...arr, item] return { ...prev, [field]: newArr } }) } const insertAtCursor = (id: string, text: string, field: string) => { const el = document.getElementById(id) as HTMLTextAreaElement | HTMLInputElement; if (el) { const s = el.selectionStart || 0; const e = el.selectionEnd || 0; const val = (formData[field] as string) || ''; const next = val.substring(0, s) + text + val.substring(e); updateField(field, next); setTimeout(() => { el.focus(); el.setSelectionRange(s + text.length, s + text.length); }, 50); } else { const val = formData[field] || ''; updateField(field, val + text); } }; const onKeyDownWhatsApp = (e: React.KeyboardEvent, field: string) => { if (e.key === 'Enter') { const el = e.currentTarget; const val = el.value; const start = el.selectionStart; const lineStart = val.lastIndexOf('\n', start - 1) + 1; const currentLine = val.substring(lineStart, start); const bulletMatch = currentLine.match(/^(\* | - |> )/); const numberMatch = currentLine.match(/^(\d+)\. /); if (bulletMatch || numberMatch) { const prefix = bulletMatch ? bulletMatch[0] : `${parseInt(numberMatch![1]) + 1}. `; if (currentLine.trim() === prefix.trim().replace(/\d+/, '').replace('.', '')) { e.preventDefault(); const nextVal = val.substring(0, lineStart) + val.substring(start); updateField(field, nextVal); } else { e.preventDefault(); const nextVal = val.substring(0, start) + '\n' + prefix + val.substring(start); updateField(field, nextVal); setTimeout(() => { el.setSelectionRange(start + 1 + prefix.length, start + 1 + prefix.length); }, 50); } } } }; return ( {isCalculating ? (
Calculating...
) : (
{recipientCount?.toLocaleString() || 0} Recipients
)} } />
{steps.map((step, idx: number) => (
setCurrentStep(idx)} >
idx ? "bg-emerald-500 border-emerald-500 text-white" : "bg-white border-slate-200 text-slate-400" )}> {currentStep > idx ? : step.icon}
{step.title}
))}
{currentStep === 0 && (
updateField('name', val)} placeholder="E.g. Ramadan Sale 2024" className="text-lg" icon={Rocket} />
{[ { id: 'send_whatsapp', label: 'Whatsapp Web', description: 'Direct WAWP engine', iconPath: (meta.rootData.global?.pluginUrl || '') + "assets/img/senders/whatsapp.svg", color: 'text-emerald-600', bg: 'bg-emerald-50', border: 'border-emerald-100' }, { id: 'send_meta', label: 'Meta WhatsApp', description: 'Business API approved', iconPath: (meta.rootData.global?.pluginUrl || '') + "assets/img/senders/meta.svg", color: 'text-blue-600', bg: 'bg-blue-50', border: 'border-blue-100' }, { id: 'send_email', label: 'Email Dispatch', description: 'Secure SMTP delivery', iconPath: (meta.rootData.global?.pluginUrl || '') + "assets/img/senders/gmail.svg", color: 'text-orange-600', bg: 'bg-orange-50', border: 'border-orange-100' } ].map(channel => { const isActive = Number(formData[channel.id]) === 1; return (
); })}
{Number(formData.send_whatsapp) === 1 && (
({ label: i.label, value: i.id }))} selected={formData.instances} onChange={(vals) => updateField('instances', vals)} placeholder="Select WhatsApp Instances..." />
)}
)} {currentStep === 1 && (
({ label: r.label, value: r.id }))} selected={formData.role_ids} onChange={(vals) => updateField('role_ids', vals)} placeholder="Select roles..." />
({ label: u.label, value: u.id }))} selected={formData.user_ids} onChange={(vals) => updateField('user_ids', vals)} placeholder="Search specific users..." />

Enter one number per line.