'use client'; import { useState, useEffect } from 'react'; import { useLanguage } from '@/context/LanguageContext'; import { emailsApi, EmailTemplate, EmailLog, EmailStats } from '@/lib/api'; import Card from '@/components/ui/Card'; import Button from '@/components/ui/Button'; import Input from '@/components/ui/Input'; import { EnvelopeIcon, PencilIcon, DocumentDuplicateIcon, EyeIcon, PaperAirplaneIcon, ClockIcon, CheckCircleIcon, XCircleIcon, ExclamationTriangleIcon, ChevronLeftIcon, ChevronRightIcon, } from '@heroicons/react/24/outline'; import toast from 'react-hot-toast'; import clsx from 'clsx'; type TabType = 'templates' | 'logs' | 'compose'; const DRAFT_STORAGE_KEY = 'spanglish-email-draft'; interface EmailDraft { eventId: string; templateSlug: string; customSubject: string; customBody: string; recipientFilter: 'all' | 'confirmed' | 'pending' | 'checked_in'; savedAt: string; } export default function AdminEmailsPage() { const { t, locale } = useLanguage(); const [activeTab, setActiveTab] = useState('templates'); const [loading, setLoading] = useState(true); // Templates state const [templates, setTemplates] = useState([]); const [editingTemplate, setEditingTemplate] = useState(null); const [showTemplateForm, setShowTemplateForm] = useState(false); const [saving, setSaving] = useState(false); // Logs state const [logs, setLogs] = useState([]); const [logsOffset, setLogsOffset] = useState(0); const [logsTotal, setLogsTotal] = useState(0); const [selectedLog, setSelectedLog] = useState(null); // Stats state const [stats, setStats] = useState(null); // Preview state const [previewHtml, setPreviewHtml] = useState(null); const [previewSubject, setPreviewSubject] = useState(''); // Template form state const [templateForm, setTemplateForm] = useState({ name: '', slug: '', subject: '', subjectEs: '', bodyHtml: '', bodyHtmlEs: '', bodyText: '', bodyTextEs: '', description: '', isActive: true, }); // Compose/Draft state const [events, setEvents] = useState([]); const [composeForm, setComposeForm] = useState({ eventId: '', templateSlug: '', customSubject: '', customBody: '', recipientFilter: 'confirmed', savedAt: '', }); const [hasDraft, setHasDraft] = useState(false); const [sending, setSending] = useState(false); const [showRecipientPreview, setShowRecipientPreview] = useState(false); const [previewRecipients, setPreviewRecipients] = useState([]); useEffect(() => { loadData(); loadEvents(); loadDraft(); }, []); const loadEvents = async () => { try { const res = await fetch('/api/events', { headers: { 'Authorization': `Bearer ${localStorage.getItem('spanglish-token')}`, }, }); if (res.ok) { const data = await res.json(); setEvents(data.events || []); } } catch (error) { console.error('Failed to load events'); } }; const loadDraft = () => { try { const saved = localStorage.getItem(DRAFT_STORAGE_KEY); if (saved) { const draft = JSON.parse(saved) as EmailDraft; setComposeForm(draft); setHasDraft(true); } } catch (error) { console.error('Failed to load draft'); } }; const saveDraft = () => { try { const draft: EmailDraft = { ...composeForm, savedAt: new Date().toISOString(), }; localStorage.setItem(DRAFT_STORAGE_KEY, JSON.stringify(draft)); setHasDraft(true); toast.success('Draft saved'); } catch (error) { toast.error('Failed to save draft'); } }; const clearDraft = () => { localStorage.removeItem(DRAFT_STORAGE_KEY); setComposeForm({ eventId: '', templateSlug: '', customSubject: '', customBody: '', recipientFilter: 'confirmed', savedAt: '', }); setHasDraft(false); }; const loadRecipientPreview = async () => { if (!composeForm.eventId) { toast.error('Please select an event'); return; } try { const res = await fetch(`/api/events/${composeForm.eventId}/attendees`, { headers: { 'Authorization': `Bearer ${localStorage.getItem('spanglish-token')}`, }, }); if (res.ok) { const data = await res.json(); let attendees = data.attendees || []; // Apply filter if (composeForm.recipientFilter !== 'all') { attendees = attendees.filter((a: any) => a.status === composeForm.recipientFilter); } setPreviewRecipients(attendees); setShowRecipientPreview(true); } } catch (error) { toast.error('Failed to load recipients'); } }; const handleSendEmail = async () => { if (!composeForm.eventId || !composeForm.templateSlug) { toast.error('Please select an event and template'); return; } if (!confirm(`Are you sure you want to send this email to ${previewRecipients.length} recipients?`)) { return; } setSending(true); try { const res = await emailsApi.sendToEvent(composeForm.eventId, { templateSlug: composeForm.templateSlug, recipientFilter: composeForm.recipientFilter, customVariables: composeForm.customBody ? { customMessage: composeForm.customBody } : undefined, }); if (res.success || res.sentCount > 0) { toast.success(`Sent ${res.sentCount} emails successfully`); if (res.failedCount > 0) { toast.error(`${res.failedCount} emails failed`); } clearDraft(); setShowRecipientPreview(false); } else { toast.error('Failed to send emails'); } } catch (error: any) { toast.error(error.message || 'Failed to send emails'); } finally { setSending(false); } }; useEffect(() => { if (activeTab === 'logs') { loadLogs(); } }, [activeTab, logsOffset]); const loadData = async () => { try { const [templatesRes, statsRes] = await Promise.all([ emailsApi.getTemplates(), emailsApi.getStats(), ]); setTemplates(templatesRes.templates); setStats(statsRes.stats); } catch (error) { toast.error('Failed to load email data'); } finally { setLoading(false); } }; const loadLogs = async () => { try { const res = await emailsApi.getLogs({ limit: 20, offset: logsOffset }); setLogs(res.logs); setLogsTotal(res.pagination.total); } catch (error) { toast.error('Failed to load email logs'); } }; const resetTemplateForm = () => { setTemplateForm({ name: '', slug: '', subject: '', subjectEs: '', bodyHtml: '', bodyHtmlEs: '', bodyText: '', bodyTextEs: '', description: '', isActive: true, }); setEditingTemplate(null); }; const handleEditTemplate = (template: EmailTemplate) => { setTemplateForm({ name: template.name, slug: template.slug, subject: template.subject, subjectEs: template.subjectEs || '', bodyHtml: template.bodyHtml, bodyHtmlEs: template.bodyHtmlEs || '', bodyText: template.bodyText || '', bodyTextEs: template.bodyTextEs || '', description: template.description || '', isActive: template.isActive, }); setEditingTemplate(template); setShowTemplateForm(true); }; const handleSaveTemplate = async (e: React.FormEvent) => { e.preventDefault(); setSaving(true); try { const data = { ...templateForm, subjectEs: templateForm.subjectEs || undefined, bodyHtmlEs: templateForm.bodyHtmlEs || undefined, bodyText: templateForm.bodyText || undefined, bodyTextEs: templateForm.bodyTextEs || undefined, description: templateForm.description || undefined, }; if (editingTemplate) { await emailsApi.updateTemplate(editingTemplate.id, data); toast.success('Template updated'); } else { await emailsApi.createTemplate(data); toast.success('Template created'); } setShowTemplateForm(false); resetTemplateForm(); loadData(); } catch (error: any) { toast.error(error.message || 'Failed to save template'); } finally { setSaving(false); } }; const handlePreviewTemplate = async (template: EmailTemplate) => { try { const res = await emailsApi.preview({ templateSlug: template.slug, variables: { attendeeName: 'John Doe', attendeeEmail: 'john@example.com', ticketId: 'TKT-ABC123', eventTitle: 'Spanglish Night - January Edition', eventDate: 'January 28, 2026', eventTime: '7:00 PM', eventLocation: 'Casa Cultural, Asunción', eventLocationUrl: 'https://maps.google.com', eventPrice: '50,000 PYG', paymentAmount: '50,000 PYG', paymentMethod: 'Lightning', paymentReference: 'PAY-XYZ789', paymentDate: 'January 28, 2026', customMessage: 'This is a preview message.', }, locale, }); setPreviewSubject(res.subject); setPreviewHtml(res.bodyHtml); } catch (error) { toast.error('Failed to preview template'); } }; const handleDeleteTemplate = async (id: string) => { if (!confirm('Are you sure you want to delete this template?')) return; try { await emailsApi.deleteTemplate(id); toast.success('Template deleted'); loadData(); } catch (error: any) { toast.error(error.message || 'Failed to delete template'); } }; const getStatusIcon = (status: string) => { switch (status) { case 'sent': return ; case 'failed': return ; case 'pending': return ; case 'bounced': return ; default: return ; } }; const formatDate = (dateStr: string) => { return new Date(dateStr).toLocaleString(locale === 'es' ? 'es-ES' : 'en-US', { month: 'short', day: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit', timeZone: 'America/Asuncion', }); }; if (loading) { return (
); } return (

Email Center

{/* Stats Cards */} {stats && (

{stats.total}

Total Sent

{stats.sent}

Delivered

{stats.pending}

Pending

{stats.failed}

Failed

)} {/* Tabs */}
{/* Templates Tab */} {activeTab === 'templates' && (

Manage email templates for booking confirmations, receipts, and updates.

{templates.map((template) => (

{template.name}

{template.isSystem && ( System )} {!template.isActive && ( Inactive )}

{template.slug}

{template.description || 'No description'}

Subject: {template.subject}

{template.variables && template.variables.length > 0 && (
{template.variables.slice(0, 5).map((v: any) => ( {`{{${v.name}}}`} ))} {template.variables.length > 5 && ( +{template.variables.length - 5} more )}
)}
{!template.isSystem && ( )}
))}
)} {/* Compose Tab */} {activeTab === 'compose' && (

Compose Email to Event Attendees

{hasDraft && ( Draft saved {composeForm.savedAt ? new Date(composeForm.savedAt).toLocaleString(locale === 'es' ? 'es-ES' : 'en-US', { timeZone: 'America/Asuncion' }) : ''} )} {hasDraft && ( )}
{/* Event Selection */}
{/* Recipient Filter */}
{/* Template Selection */}
{/* Custom Message */}