From e9897d416b3e7327bbd4d4aef887eee37751ae82 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Fri, 27 Jun 2025 01:16:20 +0000 Subject: (대표님) 20250627 오전 10시 작업사항 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/mail/mail-templates-client.tsx | 218 ++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 components/mail/mail-templates-client.tsx (limited to 'components/mail/mail-templates-client.tsx') diff --git a/components/mail/mail-templates-client.tsx b/components/mail/mail-templates-client.tsx new file mode 100644 index 00000000..7c4dafdf --- /dev/null +++ b/components/mail/mail-templates-client.tsx @@ -0,0 +1,218 @@ +'use client'; + +import { useState, useEffect, useTransition } from 'react'; +import Link from 'next/link'; +import { useParams } from 'next/navigation'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from '@/components/ui/table'; +import { Search, Edit, FileText, ChevronUp, ChevronDown } from 'lucide-react'; +import { toast } from 'sonner'; +import { getTemplatesAction, TemplateFile } from '@/lib/mail/service'; + +type Template = TemplateFile; + +interface MailTemplatesClientProps { + initialData?: Template[]; +} + +type SortField = 'name' | 'lastModified'; +type SortDirection = 'asc' | 'desc'; + +export default function MailTemplatesClient({ initialData = [] }: MailTemplatesClientProps) { + const params = useParams(); + const lng = (params?.lng as string) || 'ko'; + + const [templates, setTemplates] = useState(initialData); + const [loading, setLoading] = useState(false); + const [searchQuery, setSearchQuery] = useState(''); + const [sortField, setSortField] = useState('name'); + const [sortDirection, setSortDirection] = useState('asc'); + const [, startTransition] = useTransition(); + + // 템플릿 목록 조회 + const fetchTemplates = async () => { + try { + setLoading(true); + const search = searchQuery || undefined; + + startTransition(async () => { + const result = await getTemplatesAction(search); + + if (result.success && result.data) { + setTemplates(result.data); + } else { + toast.error(result.error || '템플릿 목록을 가져오는데 실패했습니다.'); + } + setLoading(false); + }); + } catch (error) { + console.error('Error fetching templates:', error); + toast.error('템플릿 목록을 가져오는데 실패했습니다.'); + setLoading(false); + } + }; + + // 검색 핸들러 + const handleSearch = () => { + fetchTemplates(); + }; + + // 정렬 함수 + const sortTemplates = (templates: Template[]) => { + return [...templates].sort((a, b) => { + let aValue: string | Date; + let bValue: string | Date; + + if (sortField === 'name') { + aValue = a.name; + bValue = b.name; + } else { + aValue = new Date(a.lastModified); + bValue = new Date(b.lastModified); + } + + if (aValue < bValue) { + return sortDirection === 'asc' ? -1 : 1; + } + if (aValue > bValue) { + return sortDirection === 'asc' ? 1 : -1; + } + return 0; + }); + }; + + // 정렬 핸들러 + const handleSort = (field: SortField) => { + if (sortField === field) { + setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc'); + } else { + setSortField(field); + setSortDirection('asc'); + } + }; + + // 정렬된 템플릿 목록 + const sortedTemplates = sortTemplates(templates); + + useEffect(() => { + if (searchQuery !== '') { + fetchTemplates(); + } else { + setTemplates(initialData); + } + }, [searchQuery, initialData]); + + return ( +
+ {/* 검색 */} +
+
+ + setSearchQuery(e.target.value)} + className="pl-10" + onKeyPress={(e) => e.key === 'Enter' && handleSearch()} + /> +
+ + +
+ + {/* 템플릿 테이블 */} +
+ + + + + + + + + + 작업 + + + + {loading ? ( + + + 로딩 중... + + + ) : templates.length === 0 ? ( + + + 템플릿이 없습니다. + + + ) : ( + sortedTemplates.map((template) => ( + + +
+ + {template.name} +
+
+ + {new Date(template.lastModified).toLocaleString('ko-KR')} + + +
+ + + +
+
+
+ )) + )} +
+
+
+
+ ); +} \ No newline at end of file -- cgit v1.2.3