ERP-node/frontend/app/(main)/admin/mail/accounts/page.tsx

226 lines
7.6 KiB
TypeScript

"use client";
import React, { useState, useEffect } from "react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Mail, Plus, Loader2, RefreshCw } from "lucide-react";
import {
MailAccount,
getMailAccounts,
createMailAccount,
updateMailAccount,
deleteMailAccount,
testMailAccountConnection,
CreateMailAccountDto,
UpdateMailAccountDto,
} from "@/lib/api/mail";
import MailAccountModal from "@/components/mail/MailAccountModal";
import MailAccountTable from "@/components/mail/MailAccountTable";
import ConfirmDeleteModal from "@/components/mail/ConfirmDeleteModal";
export default function MailAccountsPage() {
const [accounts, setAccounts] = useState<MailAccount[]>([]);
const [loading, setLoading] = useState(false);
const [isModalOpen, setIsModalOpen] = useState(false);
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [selectedAccount, setSelectedAccount] = useState<MailAccount | null>(null);
const [modalMode, setModalMode] = useState<'create' | 'edit'>('create');
const loadAccounts = async () => {
setLoading(true);
try {
const data = await getMailAccounts();
// 배열인지 확인하고 설정
if (Array.isArray(data)) {
setAccounts(data);
} else {
console.error('API 응답이 배열이 아닙니다:', data);
setAccounts([]);
}
} catch (error) {
console.error('계정 로드 실패:', error);
setAccounts([]); // 에러 시 빈 배열로 설정
// alert('계정 목록을 불러오는데 실패했습니다.');
} finally {
setLoading(false);
}
};
useEffect(() => {
loadAccounts();
}, []);
const handleOpenCreateModal = () => {
setModalMode('create');
setSelectedAccount(null);
setIsModalOpen(true);
};
const handleOpenEditModal = (account: MailAccount) => {
setModalMode('edit');
setSelectedAccount(account);
setIsModalOpen(true);
};
const handleOpenDeleteModal = (account: MailAccount) => {
setSelectedAccount(account);
setIsDeleteModalOpen(true);
};
const handleSaveAccount = async (data: CreateMailAccountDto | UpdateMailAccountDto) => {
try {
if (modalMode === 'create') {
await createMailAccount(data as CreateMailAccountDto);
} else if (modalMode === 'edit' && selectedAccount) {
await updateMailAccount(selectedAccount.id, data as UpdateMailAccountDto);
}
await loadAccounts();
setIsModalOpen(false);
} catch (error) {
throw error; // 모달에서 에러 처리
}
};
const handleDeleteAccount = async () => {
if (!selectedAccount) return;
try {
await deleteMailAccount(selectedAccount.id);
await loadAccounts();
alert('계정이 삭제되었습니다.');
} catch (error) {
console.error('계정 삭제 실패:', error);
alert('계정 삭제에 실패했습니다.');
}
};
const handleToggleStatus = async (account: MailAccount) => {
try {
const newStatus = account.status === 'active' ? 'inactive' : 'active';
await updateMailAccount(account.id, { status: newStatus });
await loadAccounts();
} catch (error) {
console.error('상태 변경 실패:', error);
alert('상태 변경에 실패했습니다.');
}
};
const handleTestConnection = async (account: MailAccount) => {
try {
setLoading(true);
const result = await testMailAccountConnection(account.id);
if (result.success) {
alert(`✅ SMTP 연결 성공!\n\n${result.message || '정상적으로 연결되었습니다.'}`);
} else {
alert(`❌ SMTP 연결 실패\n\n${result.message || '연결에 실패했습니다.'}`);
}
} catch (error: any) {
console.error('연결 테스트 실패:', error);
alert(`❌ SMTP 연결 테스트 실패\n\n${error.message || '알 수 없는 오류가 발생했습니다.'}`);
} finally {
setLoading(false);
}
};
return (
<div className="min-h-screen bg-gray-50">
<div className="w-full max-w-none px-4 py-8 space-y-8">
{/* 페이지 제목 */}
<div className="flex items-center justify-between bg-white rounded-lg shadow-sm border p-6">
<div>
<h1 className="text-3xl font-bold text-gray-900"> </h1>
<p className="mt-2 text-gray-600">SMTP </p>
</div>
<div className="flex gap-2">
<Button
variant="outline"
size="sm"
onClick={loadAccounts}
disabled={loading}
>
<RefreshCw className={`w-4 h-4 mr-2 ${loading ? 'animate-spin' : ''}`} />
</Button>
<Button
className="bg-orange-500 hover:bg-orange-600"
onClick={handleOpenCreateModal}
>
<Plus className="w-4 h-4 mr-2" />
</Button>
</div>
</div>
{/* 메인 컨텐츠 */}
{loading ? (
<Card className="shadow-sm">
<CardContent className="flex justify-center items-center py-16">
<Loader2 className="w-8 h-8 animate-spin text-orange-500" />
</CardContent>
</Card>
) : (
<Card className="shadow-sm">
<CardContent className="p-6">
<MailAccountTable
accounts={accounts}
onEdit={handleOpenEditModal}
onDelete={handleOpenDeleteModal}
onToggleStatus={handleToggleStatus}
onTestConnection={handleTestConnection}
/>
</CardContent>
</Card>
)}
{/* 안내 정보 */}
<Card className="bg-gradient-to-r from-orange-50 to-amber-50 border-orange-200 shadow-sm">
<CardHeader>
<CardTitle className="text-lg flex items-center">
<Mail className="w-5 h-5 mr-2 text-orange-500" />
</CardTitle>
</CardHeader>
<CardContent>
<p className="text-gray-700 mb-4">
💡 SMTP !
</p>
<ul className="space-y-2 text-sm text-gray-600">
<li className="flex items-start">
<span className="text-orange-500 mr-2"></span>
<span>Gmail, Naver, SMTP </span>
</li>
<li className="flex items-start">
<span className="text-orange-500 mr-2"></span>
<span> </span>
</li>
<li className="flex items-start">
<span className="text-orange-500 mr-2"></span>
<span> </span>
</li>
</ul>
</CardContent>
</Card>
</div>
{/* 모달들 */}
<MailAccountModal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
onSave={handleSaveAccount}
account={selectedAccount}
mode={modalMode}
/>
<ConfirmDeleteModal
isOpen={isDeleteModalOpen}
onClose={() => setIsDeleteModalOpen(false)}
onConfirm={handleDeleteAccount}
title="메일 계정 삭제"
message="이 메일 계정을 삭제하시겠습니까?"
itemName={selectedAccount?.name}
/>
</div>
);
}