;
- };
- onEmailSent?: () => void;
-}
-
-export function SendEmailDialog({
- invoiceId,
- trigger,
- invoice,
- onEmailSent,
-}: SendEmailDialogProps) {
- const [isOpen, setIsOpen] = useState(false);
- const [activeTab, setActiveTab] = useState("compose");
- const [isSending, setIsSending] = useState(false);
- const [isConfirming, setIsConfirming] = useState(false);
-
- // Email content state
- const [subject, setSubject] = useState(() =>
- invoice
- ? `Invoice ${invoice.invoiceNumber} from ${invoice.business?.name ?? "Your Business"}`
- : "Invoice from Your Business",
- );
- const [ccEmail, setCcEmail] = useState("");
- const [bccEmail, setBccEmail] = useState("");
- const [customMessage, setCustomMessage] = useState("");
-
- const [emailContent, setEmailContent] = useState(() => {
- const getTimeOfDayGreeting = () => {
- const hour = new Date().getHours();
- if (hour < 12) return "Good morning";
- if (hour < 17) return "Good afternoon";
- return "Good evening";
- };
-
- const formatDate = (date: Date) => {
- return new Intl.DateTimeFormat("en-US", {
- year: "numeric",
- month: "long",
- day: "numeric",
- }).format(new Date(date));
- };
-
- if (!invoice) return "";
-
- const businessName = invoice.business?.name ?? "Your Business";
-
- const issueDate = formatDate(invoice.issueDate);
-
- // Calculate total from items
- const subtotal =
- invoice.items?.reduce((sum, item) => sum + item.hours * item.rate, 0) ??
- 0;
- const taxAmount = subtotal * (invoice.taxRate / 100);
- const total = subtotal + taxAmount;
-
- return `${getTimeOfDayGreeting()},
-
-I hope this email finds you well. Please find attached invoice ${invoice.invoiceNumber} dated ${issueDate}.
-
-The invoice details are as follows:
-
- - Invoice Number: ${invoice.invoiceNumber}
- - Issue Date: ${issueDate}
- - Amount Due: ${new Intl.NumberFormat("en-US", {
- style: "currency",
- currency: "USD",
- }).format(total)}
-
-
-Please let me know if you have any questions or need any clarification regarding this invoice. I appreciate your prompt attention to this matter.
-
-Thank you for your business!
-
-Best regards,
${businessName}
`;
- });
-
- // Get utils for cache invalidation
- const utils = api.useUtils();
-
- // Email sending mutation
- const sendEmailMutation = api.email.sendInvoice.useMutation({
- onSuccess: (data) => {
- toast.success("Email sent successfully!", {
- description: data.message,
- duration: 5000,
- });
-
- // Reset state and close dialog
- setIsOpen(false);
- setActiveTab("compose");
- setIsSending(false);
- setIsConfirming(false);
-
- // Refresh invoice data
- void utils.invoices.getById.invalidate({ id: invoiceId });
-
- // Callback for parent component
- onEmailSent?.();
- },
- onError: (error) => {
- console.error("Email send error:", error);
-
- let errorMessage = "Failed to send invoice email";
- let errorDescription = error.message;
-
- if (error.message.includes("Invalid recipient")) {
- errorMessage = "Invalid Email Address";
- errorDescription =
- "Please check the client's email address and try again.";
- } else if (error.message.includes("domain not verified")) {
- errorMessage = "Email Configuration Issue";
- errorDescription = "Please contact support to configure email sending.";
- } else if (error.message.includes("rate limit")) {
- errorMessage = "Too Many Emails";
- errorDescription = "Please wait a moment before sending another email.";
- } else if (error.message.includes("no email address")) {
- errorMessage = "No Email Address";
- errorDescription = "This client doesn't have an email address on file.";
- }
-
- toast.error(errorMessage, {
- description: errorDescription,
- duration: 6000,
- });
-
- setIsSending(false);
- setIsConfirming(false);
- },
- });
-
- const handleSendEmail = async () => {
- if (!invoice?.client?.email || invoice.client.email.trim() === "") {
- toast.error("No email address", {
- description: "This client doesn't have an email address on file.",
- });
- return;
- }
-
- if (!subject.trim()) {
- toast.error("Subject required", {
- description: "Please enter an email subject before sending.",
- });
- return;
- }
-
- if (!emailContent.trim()) {
- toast.error("Message required", {
- description: "Please enter an email message before sending.",
- });
- return;
- }
-
- setIsSending(true);
-
- try {
- // Use the enhanced API with custom subject and content
- await sendEmailMutation.mutateAsync({
- invoiceId,
- customSubject: subject,
- customContent: emailContent,
- customMessage: customMessage.trim() || undefined,
- useHtml: true,
- ccEmails: ccEmail.trim() || undefined,
- bccEmails: bccEmail.trim() || undefined,
- });
- } catch (error) {
- // Error handling is done in the mutation's onError
- console.error("Send email error:", error);
- }
- };
-
- const handleConfirmSend = () => {
- setIsConfirming(true);
- setActiveTab("confirm");
- };
-
- const fromEmail = invoice?.business?.email ?? "noreply@yourdomain.com";
- const toEmail = invoice?.client?.email ?? "";
-
- const canSend =
- !isSending &&
- subject.trim() &&
- emailContent.trim() &&
- toEmail &&
- toEmail.trim() !== "";
-
- return (
-
- );
-}