diff --git a/src/app/dashboard/invoices/[id]/send/page.tsx b/src/app/dashboard/invoices/[id]/send/page.tsx index 33ba231..e424671 100644 --- a/src/app/dashboard/invoices/[id]/send/page.tsx +++ b/src/app/dashboard/invoices/[id]/send/page.tsx @@ -292,7 +292,7 @@ export default function SendEmailPage() { if (!invoice) { return ( -
+
Invoice not found. @@ -302,7 +302,7 @@ export default function SendEmailPage() { } return ( -
+
; - }; - 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 ( - - {trigger} - - - - - - Send Invoice Email - - - Compose and preview your invoice email before sending to{" "} - {invoice?.client?.name ?? "client"}. - - - - {/* Warning for missing email */} - {(!toEmail || toEmail.trim() === "") && ( - - - - This client doesn't have an email address. Please add an - email address to the client before sending the invoice. - - - )} - - {/* Branded Template Info */} - - - - Professional Email Template: Your email will be - sent using a beautifully designed, beenvoice-branded template with - proper fonts and styling. Any custom content you add will be - incorporated into the professional template automatically. - - - - - - - - Compose - - - - Preview - - - - Confirm - - - -
- - - - - - - - - -
- - - - You're about to send this email to{" "} - {toEmail}. The invoice PDF will be - automatically attached. - - - - - - {invoice?.status === "draft" && ( - - - - This invoice is currently in draft{" "} - status. Sending it will automatically change the status to{" "} - sent. - - - )} -
-
-
-
- - -
- {activeTab === "compose" && ( - - )} - - {activeTab === "preview" && ( - <> - - - - )} - - {activeTab === "confirm" && ( - <> - - - - )} -
- - -
-
-
- ); -}