diff --git a/frontend/app/(main)/mail/imap/page.tsx b/frontend/app/(main)/mail/imap/page.tsx index f66229bd..cf734b8a 100644 --- a/frontend/app/(main)/mail/imap/page.tsx +++ b/frontend/app/(main)/mail/imap/page.tsx @@ -13,6 +13,17 @@ import { DialogTitle, DialogFooter, } from "@/components/ui/dialog"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; +import { toast } from "sonner"; import { Switch } from "@/components/ui/switch"; import { ResizablePanelGroup, @@ -44,6 +55,8 @@ import { FolderOpen, Send, Download, + Eye, + EyeOff, } from "lucide-react"; import DOMPurify from "isomorphic-dompurify"; import { @@ -115,6 +128,9 @@ export default function ImapMailPage() { const [composeInReplyTo, setComposeInReplyTo] = useState(""); const [composeReferences, setComposeReferences] = useState(""); const [composeSending, setComposeSending] = useState(false); + const [pendingDeleteMail, setPendingDeleteMail] = useState(null); + const [pendingDeleteAccount, setPendingDeleteAccount] = useState(null); + const [showPassword, setShowPassword] = useState(false); const detailCacheRef = useRef>(new Map()); const prefetchingRef = useRef>(new Set()); @@ -321,7 +337,13 @@ export default function ImapMailPage() { async function handleDeleteMail(mail: ReceivedMail) { if (!selectedAccount) return; - if (!confirm("메일을 삭제하시겠습니까?")) return; + setPendingDeleteMail(mail); + } + + async function confirmDeleteMail() { + if (!selectedAccount || !pendingDeleteMail) return; + const mail = pendingDeleteMail; + setPendingDeleteMail(null); const seqno = parseInt(mail.id.split("-").pop() || "0"); try { await deleteUserMail(selectedAccount.id, seqno); @@ -334,7 +356,7 @@ export default function ImapMailPage() { if (selectedMail?.id === mail.id) setSelectedMail(null); loadFolders(selectedAccount); } catch (e: any) { - alert("메일 삭제 실패: " + e.message); + toast.error("메일 삭제 실패: " + e.message); } } @@ -393,6 +415,7 @@ export default function ImapMailPage() { setEditingAccount(null); setForm(DEFAULT_FORM); setTestResult(null); + setShowPassword(false); setShowDialog(true); } @@ -409,6 +432,7 @@ export default function ImapMailPage() { password: "", }); setTestResult(null); + setShowPassword(false); setShowDialog(true); } @@ -440,7 +464,13 @@ export default function ImapMailPage() { } async function handleDeleteAccount(account: UserMailAccount) { - if (!confirm(`"${account.displayName}" 계정을 삭제하시겠습니까?`)) return; + setPendingDeleteAccount(account); + } + + async function confirmDeleteAccount() { + if (!pendingDeleteAccount) return; + const account = pendingDeleteAccount; + setPendingDeleteAccount(null); try { await deleteUserMailAccount(account.id); if (selectedAccount?.id === account.id) { @@ -511,10 +541,6 @@ export default function ImapMailPage() { 작성 - @@ -527,7 +553,14 @@ export default function ImapMailPage() {
계정 목록 - {loadingAccounts && } + {loadingAccounts && } +
{imapAccounts.length === 0 && !loadingAccounts ? ( @@ -633,7 +666,7 @@ export default function ImapMailPage() { ) : filteredMails.length === 0 ? (
-

메일이 없습니다

+

{searchTerm ? `"${searchTerm}" 검색 결과가 없습니다` : "메일이 없습니다"}

) : ( <> @@ -756,13 +789,13 @@ export default function ImapMailPage() { try { const list = await getUserMailAttachments(accountId, seqno, currentFolder); const matched = list.find(a => a.filename === att.filename) || list[i]; - if (!matched) { alert('첨부파일 정보를 불러올 수 없습니다'); return; } + if (!matched) { toast.error('첨부파일 정보를 불러올 수 없습니다'); return; } await downloadAttachment( accountId, seqno, matched.partId, matched.filename, currentFolder, (pct) => setDownloadProgress(p => ({ ...p, [i]: pct })), matched.size ); - } catch (e: any) { alert(e.message); } + } catch (e: any) { toast.error(e.message); } finally { setDownloadProgress(p => { const next = { ...p }; delete next[i]; return next; }); } }} className="relative inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full bg-secondary hover:bg-accent border disabled:opacity-60 disabled:cursor-not-allowed cursor-pointer min-w-[80px] overflow-hidden"> @@ -873,7 +906,7 @@ export default function ImapMailPage() { setForm((p) => ({ ...p, email: e.target.value }))} + onChange={(e) => setForm((p) => ({ ...p, email: e.target.value, username: e.target.value }))} placeholder="user@example.com" />
@@ -909,12 +942,22 @@ export default function ImapMailPage() {
- setForm((p) => ({ ...p, password: e.target.value }))} - placeholder="••••••••" - /> +
+ setForm((p) => ({ ...p, password: e.target.value }))} + placeholder="••••••••" + className="pr-9" + /> + +
{editingAccount && (
@@ -958,6 +1001,34 @@ export default function ImapMailPage() { + + {/* 메일 삭제 확인 */} + { if (!open) setPendingDeleteMail(null); }}> + + + 메일 삭제 + 메일을 삭제하시겠습니까? + + + 취소 + 삭제 + + + + + {/* 계정 삭제 확인 */} + { if (!open) setPendingDeleteAccount(null); }}> + + + 계정 삭제 + "{pendingDeleteAccount?.displayName}" 계정을 삭제하시겠습니까? + + + 취소 + 삭제 + + +
); }