import React, { useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useNavigate } from 'react-router-dom'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Plus, Search, Send, Clock, CheckCircle2, AlertCircle, Trash2, Edit, MoreHorizontal, Copy } from 'lucide-react'; import { toast } from 'sonner'; import { api } from '@/lib/api'; import { __ } from '@/lib/i18n'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; interface Campaign { id: number; title: string; subject: string; status: 'draft' | 'scheduled' | 'sending' | 'sent' | 'failed'; recipient_count: number; sent_count: number; failed_count: number; scheduled_at: string | null; sent_at: string | null; created_at: string; } const statusConfig = { draft: { label: 'Draft', icon: Edit, className: 'bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-300' }, scheduled: { label: 'Scheduled', icon: Clock, className: 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300' }, sending: { label: 'Sending', icon: Send, className: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300' }, sent: { label: 'Sent', icon: CheckCircle2, className: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300' }, failed: { label: 'Failed', icon: AlertCircle, className: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300' }, }; export default function Campaigns() { const [searchQuery, setSearchQuery] = useState(''); const [deleteId, setDeleteId] = useState(null); const navigate = useNavigate(); const queryClient = useQueryClient(); const { data, isLoading } = useQuery({ queryKey: ['campaigns'], queryFn: async () => { const response: any = await api.get('/newsletter/campaigns'); return Array.isArray(response) ? (response as Campaign[]) : ((response?.data || []) as Campaign[]); }, }); const deleteMutation = useMutation({ mutationFn: async (id: number) => { await api.del(`/campaigns/${id}`); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['campaigns'] }); toast.success(__('Campaign deleted')); setDeleteId(null); }, onError: () => { toast.error(__('Failed to delete campaign')); }, }); const duplicateMutation = useMutation({ mutationFn: async (campaign: Campaign) => { const response = await api.post('/campaigns', { title: `${campaign.title} (Copy)`, subject: campaign.subject, content: '', status: 'draft', }); return response; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['campaigns'] }); toast.success(__('Campaign duplicated')); }, onError: () => { toast.error(__('Failed to duplicate campaign')); }, }); const campaigns = data || []; const filteredCampaigns = campaigns.filter((c) => c.title.toLowerCase().includes(searchQuery.toLowerCase()) || c.subject?.toLowerCase().includes(searchQuery.toLowerCase()) ); const formatDate = (dateStr: string | null) => { if (!dateStr) return '-'; return new Date(dateStr).toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', }); }; return (
{/* Actions Bar */}
setSearchQuery(e.target.value)} className="!pl-9" />
{/* Campaigns Table */} {isLoading ? (
{__('Loading campaigns...')}
) : filteredCampaigns.length === 0 ? (
{searchQuery ? __('No campaigns found matching your search') : (

{__('No campaigns yet')}

)}
) : (
{__('Title')} {__('Status')} {__('Recipients')} {__('Date')} {__('Actions')} {filteredCampaigns.map((campaign) => { const status = statusConfig[campaign.status] || statusConfig.draft; const StatusIcon = status.icon; return (
{campaign.title}
{campaign.subject && (
{campaign.subject}
)}
{__(status.label)} {campaign.status === 'sent' ? ( {campaign.sent_count}/{campaign.recipient_count} {campaign.failed_count > 0 && ( ({campaign.failed_count} {__('failed')}) )} ) : ( '-' )} {campaign.sent_at ? formatDate(campaign.sent_at) : campaign.scheduled_at ? `${__('Scheduled')}: ${formatDate(campaign.scheduled_at)}` : formatDate(campaign.created_at) } navigate(`/marketing/newsletter/campaigns/${campaign.id}`)}> {__('Edit')} duplicateMutation.mutate(campaign)}> {__('Duplicate')} setDeleteId(campaign.id)} className="text-red-600" > {__('Delete')}
); })}
)} {/* Delete Confirmation Dialog */} setDeleteId(null)}> {__('Delete Campaign')} {__('Are you sure you want to delete this campaign? This action cannot be undone.')} {__('Cancel')} deleteId && deleteMutation.mutate(deleteId)} className="bg-red-600 hover:bg-red-700" > {__('Delete')}
); }