mirror of
https://github.com/beilunyang/moemail.git
synced 2026-04-22 23:07:25 +08:00
142 lines
4.2 KiB
TypeScript
142 lines
4.2 KiB
TypeScript
"use client"
|
|
|
|
import { useState } from "react"
|
|
import { useTranslations } from "next-intl"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Input } from "@/components/ui/input"
|
|
import { Textarea } from "@/components/ui/textarea"
|
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"
|
|
import { Send } from "lucide-react"
|
|
import { useToast } from "@/components/ui/use-toast"
|
|
import {
|
|
Tooltip,
|
|
TooltipContent,
|
|
TooltipProvider,
|
|
TooltipTrigger,
|
|
} from "@/components/ui/tooltip"
|
|
|
|
interface SendDialogProps {
|
|
emailId: string
|
|
fromAddress: string
|
|
onSendSuccess?: () => void
|
|
}
|
|
|
|
export function SendDialog({ emailId, fromAddress, onSendSuccess }: SendDialogProps) {
|
|
const t = useTranslations("emails.send")
|
|
const tList = useTranslations("emails.list")
|
|
const tCommon = useTranslations("common.actions")
|
|
const [open, setOpen] = useState(false)
|
|
const [loading, setLoading] = useState(false)
|
|
const [to, setTo] = useState("")
|
|
const [subject, setSubject] = useState("")
|
|
const [content, setContent] = useState("")
|
|
const { toast } = useToast()
|
|
|
|
const handleSend = async () => {
|
|
if (!to.trim() || !subject.trim() || !content.trim()) {
|
|
toast({
|
|
title: tList("error"),
|
|
description: t("toPlaceholder") + ", " + t("subjectPlaceholder") + ", " + t("contentPlaceholder"),
|
|
variant: "destructive"
|
|
})
|
|
return
|
|
}
|
|
|
|
setLoading(true)
|
|
try {
|
|
const response = await fetch(`/api/emails/${emailId}/send`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ to, subject, content })
|
|
})
|
|
|
|
if (!response.ok) {
|
|
const data = await response.json()
|
|
toast({
|
|
title: tList("error"),
|
|
description: (data as { error: string }).error,
|
|
variant: "destructive"
|
|
})
|
|
return
|
|
}
|
|
|
|
toast({
|
|
title: tList("success"),
|
|
description: t("success")
|
|
})
|
|
setOpen(false)
|
|
setTo("")
|
|
setSubject("")
|
|
setContent("")
|
|
|
|
onSendSuccess?.()
|
|
|
|
} catch {
|
|
toast({
|
|
title: tList("error"),
|
|
description: t("failed"),
|
|
variant: "destructive"
|
|
})
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={setOpen}>
|
|
<TooltipProvider>
|
|
<Tooltip>
|
|
<DialogTrigger asChild>
|
|
<TooltipTrigger asChild>
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
className="h-8 gap-2 hover:bg-primary/10 hover:text-primary transition-colors"
|
|
>
|
|
<Send className="h-4 w-4" />
|
|
<span className="hidden sm:inline">{t("title")}</span>
|
|
</Button>
|
|
</TooltipTrigger>
|
|
</DialogTrigger>
|
|
<TooltipContent className="sm:hidden">
|
|
<p>{t("title")}</p>
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
</TooltipProvider>
|
|
<DialogContent>
|
|
<DialogHeader>
|
|
<DialogTitle>{t("title")}</DialogTitle>
|
|
</DialogHeader>
|
|
<div className="space-y-4 py-4">
|
|
<div className="text-sm text-muted-foreground">
|
|
{t("from")}: {fromAddress}
|
|
</div>
|
|
<Input
|
|
value={to}
|
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setTo(e.target.value)}
|
|
placeholder={t("toPlaceholder")}
|
|
/>
|
|
<Input
|
|
value={subject}
|
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setSubject(e.target.value)}
|
|
placeholder={t("subjectPlaceholder")}
|
|
/>
|
|
<Textarea
|
|
value={content}
|
|
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setContent(e.target.value)}
|
|
placeholder={t("contentPlaceholder")}
|
|
rows={6}
|
|
/>
|
|
</div>
|
|
<div className="flex justify-end gap-2">
|
|
<Button variant="outline" onClick={() => setOpen(false)} disabled={loading}>
|
|
{tCommon("cancel")}
|
|
</Button>
|
|
<Button onClick={handleSend} disabled={loading}>
|
|
{loading ? t("sending") : t("send")}
|
|
</Button>
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
)
|
|
}
|