Files
wr.do/components/email/SendEmailModal.tsx
2025-10-20 11:34:05 +08:00

179 lines
5.2 KiB
TypeScript

"use client";
import { useState, useTransition } from "react";
import { useTranslations } from "next-intl";
import { toast } from "sonner";
import { Icons } from "../shared/icons";
import { Button } from "../ui/button";
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerHeader,
DrawerTitle,
} from "../ui/drawer";
import { Input } from "../ui/input";
import { EmailEditor } from "./EmailEditor";
interface SendEmailModalProps {
className?: string;
emailAddress: string | null;
triggerButton?: React.ReactNode; // 自定义触发按钮
onSuccess?: () => void; // 发送成功后的回调
}
export function SendEmailModal({
className,
emailAddress,
triggerButton,
onSuccess,
}: SendEmailModalProps) {
const [isOpen, setIsOpen] = useState(false);
const [sendForm, setSendForm] = useState({
to: "",
subject: "",
html: "",
text: "",
});
const [isPending, startTransition] = useTransition();
const t = useTranslations("Email");
const handleSendEmail = async () => {
if (!emailAddress) {
toast.error("No email address selected");
return;
}
console.log("sendForm", sendForm);
// return;
if (!sendForm.to || !sendForm.subject || !sendForm.html) {
toast.error("Please fill in all required fields");
return;
}
startTransition(async () => {
try {
const response = await fetch("/api/email/send", {
method: "POST",
body: JSON.stringify({
from: emailAddress,
to: sendForm.to,
subject: sendForm.subject,
html: sendForm.html,
text: sendForm.text,
}),
});
if (response.ok) {
toast.success("Email sent successfully");
setIsOpen(false);
setSendForm({ to: "", subject: "", html: "", text: "" });
onSuccess?.();
} else {
toast.error("Failed to send email", {
description: await response.text(),
});
}
} catch (error) {
toast.error(error.message || "Error sending email");
}
});
};
return (
<>
{triggerButton ? (
<div onClick={() => setIsOpen(true)}>{triggerButton}</div>
) : (
<Button
className={className}
variant="ghost"
size="sm"
onClick={() => setIsOpen(true)}
>
<Icons.send size={17} />
</Button>
)}
<Drawer
handleOnly
open={isOpen}
direction="right"
onOpenChange={setIsOpen}
>
<DrawerContent className="fixed bottom-0 right-0 top-0 w-full rounded-none sm:max-w-5xl">
<DrawerHeader>
<DrawerTitle className="flex items-center gap-1">
{t("Send Email")}{" "}
{/* <Icons.help className="size-5 text-neutral-600 hover:text-neutral-400" /> */}
<Button
className="ml-auto"
onClick={handleSendEmail}
disabled={isPending}
variant="ghost"
>
{isPending ? (
<Icons.spinner className="size-4 animate-spin" />
) : (
<Icons.send className="size-4 text-blue-600" />
)}
</Button>
<DrawerClose asChild>
<Button variant="ghost">
<Icons.close className="size-5" />
</Button>
</DrawerClose>
</DrawerTitle>
</DrawerHeader>
<div className="scrollbar-hidden h-[calc(100vh)] space-y-1 overflow-y-auto px-4 pb-4">
<div className="flex items-center justify-between border-b">
<label className="text-nowrap text-sm font-medium text-neutral-700 dark:text-neutral-300">
{t("Subject")}
</label>
<Input
value={sendForm.subject}
onChange={(e) =>
setSendForm({ ...sendForm, subject: e.target.value })
}
placeholder="Your subject"
className="border-none"
/>
</div>
<div className="flex items-center justify-between border-b">
<label className="text-nowrap text-sm font-medium text-neutral-700 dark:text-neutral-300">
{t("From")}
</label>
<Input
value={emailAddress || ""}
disabled
className="border-none"
/>
</div>
<div className="flex items-center justify-between border-b">
<label className="text-nowrap text-sm font-medium text-neutral-700 dark:text-neutral-300">
{t("To")}
</label>
<Input
value={sendForm.to}
onChange={(e) =>
setSendForm({ ...sendForm, to: e.target.value })
}
placeholder="recipient@example.com"
className="border-none"
/>
</div>
<div>
<EmailEditor
onGetEditorValue={(e, t) =>
setSendForm({ ...sendForm, html: e, text: t })
}
/>
</div>
</div>
</DrawerContent>
</Drawer>
</>
);
}