import React, { useState, useEffect } from 'react';
import {
Box,
Typography,
Paper,
Button,
Stepper,
Step,
StepLabel,
Grid,
Card,
CardContent,
CircularProgress,
Alert,
TextField,
FormControlLabel,
Checkbox,
Radio,
RadioGroup,
FormControl,
FormLabel,
Chip,
Divider,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
IconButton,
List,
ListItem,
ListItemText,
Tooltip,
MenuItem,
Select,
InputLabel
} from '@mui/material';
import {
ArrowBack as ArrowBackIcon,
Send as SendIcon,
Schedule as ScheduleIcon,
Preview as PreviewIcon,
InfoOutlined as InfoIcon,
WarningAmber as WarningIcon,
CheckCircle as CheckCircleIcon
} from '@mui/icons-material';
import { useNavigate, useParams } from 'react-router-dom';
import { format, addHours, addDays, setHours, setMinutes } from 'date-fns';
import {
useEmailCampaign,
useMailingList,
useSendCampaign,
useScheduleCampaign,
usePreviewCampaign
} from '@hooks/emailCampaignHooks';
const CampaignSendPage = () => {
const navigate = useNavigate();
const { id } = useParams();
// Stepper state
const [activeStep, setActiveStep] = useState(0);
const steps = ['Review Campaign', 'Select Delivery Options', 'Confirm & Send'];
// Custom date time picker state
const initialDate = addHours(new Date(), 1);
const [scheduledDate, setScheduledDate] = useState(initialDate);
const [selectedDate, setSelectedDate] = useState(format(initialDate, 'yyyy-MM-dd'));
const [selectedHour, setSelectedHour] = useState(initialDate.getHours());
const [selectedMinute, setSelectedMinute] = useState(initialDate.getMinutes());
// Delivery options
const [deliveryOption, setDeliveryOption] = useState('send_now');
const [confirmChecked, setConfirmChecked] = useState(false);
// Preview dialog
const [previewDialogOpen, setPreviewDialogOpen] = useState(false);
const [testEmailAddress, setTestEmailAddress] = useState('');
// Fetch campaign and related data
const { data: campaign, isLoading: campaignLoading, error: campaignError } = useEmailCampaign(id);
// Load lists information for selected lists
const { data: listData, isLoading: listLoading } = useMailingList(
campaign?.list_ids?.length > 0 ? campaign.list_ids[0] : null
);
// Mutations
const sendCampaign = useSendCampaign();
const scheduleCampaign = useScheduleCampaign();
const previewCampaign = usePreviewCampaign();
// Generate hours and minutes for dropdowns
const hours = Array.from({ length: 24 }, (_, i) => i);
const minutes = Array.from({ length: 60 }, (_, i) => i);
// Update the scheduledDate when date/time components change
useEffect(() => {
try {
const dateObj = new Date(selectedDate);
dateObj.setHours(selectedHour);
dateObj.setMinutes(selectedMinute);
setScheduledDate(dateObj);
} catch (error) {
console.error("Invalid date selection:", error);
}
}, [selectedDate, selectedHour, selectedMinute]);
// Handle step changes
const handleNext = () => {
setActiveStep((prevStep) => Math.min(prevStep + 1, steps.length - 1));
};
const handleBack = () => {
setActiveStep((prevStep) => Math.max(prevStep - 1, 0));
};
// Handle delivery option change
const handleDeliveryOptionChange = (event) => {
setDeliveryOption(event.target.value);
};
// Handle date change
const handleDateChange = (event) => {
setSelectedDate(event.target.value);
};
// Handle hour change
const handleHourChange = (event) => {
setSelectedHour(Number(event.target.value));
};
// Handle minute change
const handleMinuteChange = (event) => {
setSelectedMinute(Number(event.target.value));
};
// Handle preview
const handleOpenPreview = () => {
setPreviewDialogOpen(true);
};
// Send test email
const handleSendTest = async () => {
if (!testEmailAddress || !campaign) return;
try {
await previewCampaign.mutateAsync({
campaignId: campaign.id,
email: testEmailAddress
});
// Clear the field after successful send
if (!previewCampaign.error) {
setTestEmailAddress('');
}
} catch (error) {
console.error('Failed to send test email:', error);
}
};
// Handle final campaign send/schedule
const handleSendCampaign = async () => {
if (!campaign) return;
try {
if (deliveryOption === 'send_now') {
await sendCampaign.mutateAsync(campaign.id);
} else {
await scheduleCampaign.mutateAsync({
campaignId: campaign.id,
scheduledDate: scheduledDate.toISOString()
});
}
// Navigate back to campaigns list on success
if (!sendCampaign.error && !scheduleCampaign.error) {
navigate('/admin/email-campaigns');
}
} catch (error) {
console.error('Failed to send/schedule campaign:', error);
}
};
// Get estimated recipient count
const getRecipientCount = () => {
if (!campaign || !campaign.list_ids || campaign.list_ids.length === 0) {
return 0;
}
return listData?.subscriber_count || 0;
};
// Format hour for display (add leading zero if needed)
const formatTimeValue = (value) => {
return value.toString().padStart(2, '0');
};
// Validate that selected date is in the future
const isDateInPast = () => {
const now = new Date();
return scheduledDate < now;
};
// Loading state
if (campaignLoading) {
return ;
}
// Error state
if (campaignError) {
return {campaignError.message};
}
// Campaign not found
if (!campaign) {
return (
Campaign not found. Please select a valid campaign.
);
}
return (
{/* Header with back button */}
navigate(`/admin/email-campaigns/${id}`)} sx={{ mr: 1 }}>
Send Campaign: {campaign.name}
{/* Stepper */}
{steps.map((label) => (
{label}
))}
{/* Step content */}
{/* Step 1: Review Campaign */}
{activeStep === 0 && (
Review Campaign Details
Campaign Details
Name:
{campaign.name}
Subject:
{campaign.subject}
From:
{campaign.from_name} ({campaign.from_email})
{campaign.preheader && (
<>
Preheader:
{campaign.preheader}
>
)}
Audience
Mailing Lists:
{listLoading ? (
) : (
)}
Estimated Recipients: {getRecipientCount()}
{getRecipientCount() === 0 && (
No recipients in selected mailing lists. Your campaign won't be delivered to anyone.
)}
}>
Preview Campaign
)}
{/* Step 2: Delivery Options */}
{activeStep === 1 && (
Delivery Options
When should this campaign be sent?
}
label="Send immediately"
/>
}
label="Schedule for later"
/>
{deliveryOption === 'schedule' && (
{/* Custom Date Time Picker */}
Hour
Minute
{isDateInPast() && (
Selected time is in the past. Please choose a future date and time.
)}
Selected Date/Time: {format(scheduledDate, 'PPpp')}
Campaigns are sent in your local timezone: {Intl.DateTimeFormat().resolvedOptions().timeZone}
)}
Send Test Email
Send a test email to verify how your campaign will look before sending to your list.
setTestEmailAddress(e.target.value)}
/>
{previewCampaign.isSuccess && (
Test email sent successfully!
)}
{previewCampaign.error && (
{previewCampaign.error.message || 'Failed to send test email'}
)}
)}
{/* Step 3: Confirm & Send */}
{activeStep === 2 && (
Confirm & Send
Please review your campaign before sending
Once a campaign is sent, it cannot be recalled or edited.
Sending Summary
setConfirmChecked(e.target.checked)}
/>
}
label="I confirm that this campaign is ready to send"
/>
: }
onClick={handleSendCampaign}
disabled={!confirmChecked || sendCampaign.isLoading || scheduleCampaign.isLoading || (deliveryOption === 'schedule' && isDateInPast())}
>
{sendCampaign.isLoading || scheduleCampaign.isLoading ? (
) : (
deliveryOption === 'send_now' ? 'Send Campaign' : 'Schedule Campaign'
)}
{(sendCampaign.error || scheduleCampaign.error) && (
{sendCampaign.error?.message || scheduleCampaign.error?.message || 'An error occurred'}
)}
)}
{/* Preview Dialog */}
);
};
export default CampaignSendPage;