커밋 메세지 메뉴별 대중소 정리
This commit is contained in:
201
frontend/app/(main)/admin/automaticMng/mail/drafts/page.tsx
Normal file
201
frontend/app/(main)/admin/automaticMng/mail/drafts/page.tsx
Normal file
@@ -0,0 +1,201 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { getSentMailList, updateDraft, deleteSentMail, bulkDeleteMails, type SentMailHistory } from "@/lib/api/mail";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Edit, Trash2, Loader2, Mail } from "lucide-react";
|
||||
import { format } from "date-fns";
|
||||
import { ko } from "date-fns/locale";
|
||||
|
||||
export default function DraftsPage() {
|
||||
const router = useRouter();
|
||||
const [drafts, setDrafts] = useState<SentMailHistory[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [deleting, setDeleting] = useState<string | null>(null);
|
||||
const [selectedIds, setSelectedIds] = useState<string[]>([]);
|
||||
const [bulkDeleting, setBulkDeleting] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
loadDrafts();
|
||||
}, []);
|
||||
|
||||
const loadDrafts = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await getSentMailList({
|
||||
status: "draft",
|
||||
sortBy: "updatedAt",
|
||||
sortOrder: "desc",
|
||||
});
|
||||
// console.log('📋 임시 저장 목록 조회:', response);
|
||||
// console.log('📋 임시 저장 개수:', response.items.length);
|
||||
setDrafts(response.items);
|
||||
} catch (error) {
|
||||
// console.error("❌ 임시 저장 메일 로드 실패:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleEdit = (draft: SentMailHistory) => {
|
||||
// 임시 저장 메일을 메일 발송 페이지로 전달
|
||||
const params = new URLSearchParams({
|
||||
draftId: draft.id,
|
||||
to: draft.to.join(","),
|
||||
cc: draft.cc?.join(",") || "",
|
||||
bcc: draft.bcc?.join(",") || "",
|
||||
subject: draft.subject,
|
||||
content: draft.htmlContent,
|
||||
accountId: draft.accountId,
|
||||
});
|
||||
router.push(`/admin/automaticMng/mail/send?${params.toString()}`);
|
||||
};
|
||||
|
||||
const handleDelete = async (id: string) => {
|
||||
if (!confirm("이 임시 저장 메일을 삭제하시겠습니까?")) return;
|
||||
|
||||
try {
|
||||
setDeleting(id);
|
||||
await deleteSentMail(id);
|
||||
setDrafts(drafts.filter((d) => d.id !== id));
|
||||
setSelectedIds(selectedIds.filter((selectedId) => selectedId !== id));
|
||||
} catch (error) {
|
||||
// console.error("임시 저장 메일 삭제 실패:", error);
|
||||
alert("삭제에 실패했습니다.");
|
||||
} finally {
|
||||
setDeleting(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleBulkDelete = async () => {
|
||||
if (selectedIds.length === 0) {
|
||||
alert("삭제할 메일을 선택해주세요.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm(`선택한 ${selectedIds.length}개의 임시 저장 메일을 삭제하시겠습니까?`)) return;
|
||||
|
||||
try {
|
||||
setBulkDeleting(true);
|
||||
const result = await bulkDeleteMails(selectedIds);
|
||||
setDrafts(drafts.filter((d) => !selectedIds.includes(d.id)));
|
||||
setSelectedIds([]);
|
||||
alert(result.message);
|
||||
} catch (error) {
|
||||
// console.error("일괄 삭제 실패:", error);
|
||||
alert("일괄 삭제에 실패했습니다.");
|
||||
} finally {
|
||||
setBulkDeleting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectAll = () => {
|
||||
if (selectedIds.length === drafts.length) {
|
||||
setSelectedIds([]);
|
||||
} else {
|
||||
setSelectedIds(drafts.map((d) => d.id));
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectOne = (id: string) => {
|
||||
if (selectedIds.includes(id)) {
|
||||
setSelectedIds(selectedIds.filter((selectedId) => selectedId !== id));
|
||||
} else {
|
||||
setSelectedIds([...selectedIds, id]);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<Loader2 className="w-8 h-8 animate-spin text-primary" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="p-3 space-y-3">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-foreground">임시보관함</h1>
|
||||
<p className="mt-2 text-muted-foreground">작성 중인 메일이 자동으로 저장됩니다</p>
|
||||
</div>
|
||||
|
||||
{drafts.length === 0 ? (
|
||||
<Card>
|
||||
<CardContent className="flex flex-col items-center justify-center py-12">
|
||||
<Mail className="w-12 h-12 text-muted-foreground mb-4" />
|
||||
<p className="text-muted-foreground">임시 저장된 메일이 없습니다</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : (
|
||||
<div className="grid gap-3">
|
||||
{drafts.map((draft) => (
|
||||
<Card key={draft.id} className="hover:shadow-md transition-shadow">
|
||||
<CardHeader className="pb-3">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1 min-w-0">
|
||||
<CardTitle className="text-lg truncate">
|
||||
{draft.subject || "(제목 없음)"}
|
||||
</CardTitle>
|
||||
<CardDescription className="mt-1">
|
||||
받는 사람: {draft.to.join(", ") || "(없음)"}
|
||||
</CardDescription>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 ml-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handleEdit(draft)}
|
||||
className="h-8"
|
||||
>
|
||||
<Edit className="w-4 h-4 mr-1" />
|
||||
편집
|
||||
</Button>
|
||||
<Button
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
onClick={() => handleDelete(draft.id)}
|
||||
disabled={deleting === draft.id}
|
||||
className="h-8"
|
||||
>
|
||||
{deleting === draft.id ? (
|
||||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
) : (
|
||||
<>
|
||||
<Trash2 className="w-4 h-4 mr-1" />
|
||||
삭제
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="pt-0">
|
||||
<div className="flex items-center justify-between text-sm text-muted-foreground">
|
||||
<span>계정: {draft.accountName || draft.accountEmail}</span>
|
||||
<span>
|
||||
{draft.updatedAt
|
||||
? format(new Date(draft.updatedAt), "yyyy-MM-dd HH:mm", { locale: ko })
|
||||
: format(new Date(draft.sentAt), "yyyy-MM-dd HH:mm", { locale: ko })}
|
||||
</span>
|
||||
</div>
|
||||
{draft.htmlContent && (
|
||||
<div
|
||||
className="mt-2 text-sm text-muted-foreground line-clamp-2"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: draft.htmlContent.replace(/<[^>]*>/g, "").substring(0, 100),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user