"use client" import { useState, useEffect, useTransition } from "react" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table" import { Badge } from "@/components/ui/badge" import { Search, Edit, FileText, ChevronUp, ChevronDown, Plus, Eye, Trash2 } from "lucide-react" import { toast } from "sonner" import { formatDate } from "@/lib/utils" import { getNoticeLists, deleteNotice, getPagePathList } from "@/lib/notice/service" import type { Notice } from "@/db/schema/notice" import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog" import { UpdateNoticeSheet } from "./notice-edit-sheet" import { NoticeCreateDialog } from "./notice-create-dialog" import { NoticeViewDialog } from "./notice-view-dialog" type NoticeWithAuthor = Notice & { authorName: string | null authorEmail: string | null } interface NoticeClientProps { initialData?: NoticeWithAuthor[] currentUserId?: number } type SortField = "title" | "pagePath" | "createdAt" type SortDirection = "asc" | "desc" export function NoticeClient({ initialData = [], currentUserId }: NoticeClientProps) { const [notices, setNotices] = useState(initialData) const [loading, setLoading] = useState(false) const [searchQuery, setSearchQuery] = useState("") const [sortField, setSortField] = useState("createdAt") const [sortDirection, setSortDirection] = useState("desc") const [, startTransition] = useTransition() const [isEditSheetOpen, setIsEditSheetOpen] = useState(false) const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false) const [isViewDialogOpen, setIsViewDialogOpen] = useState(false) const [selectedNotice, setSelectedNotice] = useState(null) const [pagePathOptions, setPagePathOptions] = useState>([]) // 공지사항 목록 조회 const fetchNotices = async () => { try { setLoading(true) const search = searchQuery || undefined startTransition(async () => { const result = await getNoticeLists({ page: 1, perPage: 50, search: search, sort: [{ id: sortField, desc: sortDirection === "desc" }], flags: [], filters: [], joinOperator: "and", pagePath: "", title: "", content: "", authorId: null, isActive: null, from: "", to: "", }) if (result?.data) { setNotices(result.data) } else { toast.error("공지사항 목록을 가져오는데 실패했습니다.") } setLoading(false) }) } catch (error) { console.error("Error fetching notices:", error) toast.error("공지사항 목록을 가져오는데 실패했습니다.") setLoading(false) } } // 검색 핸들러 const handleSearch = () => { fetchNotices() } // 정렬 함수 const sortNotices = (notices: NoticeWithAuthor[]) => { return [...notices].sort((a, b) => { let aValue: string | Date let bValue: string | Date if (sortField === "title") { aValue = a.title bValue = b.title } else if (sortField === "pagePath") { aValue = a.pagePath bValue = b.pagePath } else { aValue = new Date(a.createdAt) bValue = new Date(b.createdAt) } 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 handleDelete = async (notice: NoticeWithAuthor) => { try { const result = await deleteNotice(notice.id) if (result.success) { toast.success(result.message) setNotices(notices.filter(n => n.id !== notice.id)) } else { toast.error(result.message) } } catch (error) { console.error("Error deleting notice:", error) toast.error("공지사항 삭제에 실패했습니다.") } } // 정렬된 공지사항 목록 const sortedNotices = sortNotices(notices) // 페이지 경로 옵션 로딩 const loadPagePathOptions = async () => { try { const paths = await getPagePathList() const options = paths.map(path => ({ value: path.pagePath, label: `${path.pageName} (${path.pagePath})` })) setPagePathOptions(options) } catch (error) { console.error("페이지 경로 로딩 실패:", error) } } // View 다이얼로그 열기 const handleViewNotice = (notice: NoticeWithAuthor) => { setSelectedNotice(notice) setIsViewDialogOpen(true) } // Edit Sheet 열기 const handleEditNotice = (notice: NoticeWithAuthor) => { setSelectedNotice(notice) setIsEditSheetOpen(true) } // Create Dialog 열기 const handleCreateNotice = () => { setIsCreateDialogOpen(true) } useEffect(() => { if (initialData.length > 0) { setNotices(initialData) } else { fetchNotices() } loadPagePathOptions() }, []) useEffect(() => { if (searchQuery !== "") { fetchNotices() } else if (initialData.length > 0) { setNotices(initialData) } }, [searchQuery]) return (
{/* 검색 및 추가 버튼 */}
setSearchQuery(e.target.value)} className="pl-10" onKeyPress={(e) => e.key === "Enter" && handleSearch()} />
{/* 공지사항 테이블 */}
작성자 상태 작업 {loading ? ( 로딩 중... ) : notices.length === 0 ? ( 공지사항이 없습니다. ) : ( sortedNotices.map((notice) => (
{notice.title}
{notice.pagePath}
{notice.authorName || "알 수 없음"} {notice.authorEmail && ( {notice.authorEmail} )}
{notice.isActive ? "활성" : "비활성"} {formatDate(notice.createdAt)}
{/* View 버튼 - 다이얼로그 방식 */} {/* Edit 버튼 - 다이얼로그 방식 */} {/* 기존 페이지 방식 (비교용) */} 공지사항 삭제 이 공지사항을 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다. 취소 handleDelete(notice)} className="bg-red-600 hover:bg-red-700" > 삭제
)) )}
{/* 다이얼로그들과 시트 - 테이블 밖에서 단일 렌더링 */}
) }