"use client" import React, { useState, useEffect, useTransition } from "react" import { useRouter, useParams } from "next/navigation" import { useTranslation } from "@/i18n/client" 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, Download, Database, RefreshCw } from "lucide-react" import { toast } from "sonner" import { formatDate } from "@/lib/utils" import { getInformationLists, syncInformationFromMenuAssignments, getInformationDetail } from "@/lib/information/service" import type { PageInformation } from "@/db/schema/information" type PageInformationWithUpdatedBy = PageInformation & { updatedByName?: string | null updatedByEmail?: string | null } import { UpdateInformationDialog } from "@/lib/information/table/update-information-dialog" interface InformationClientProps { initialData?: PageInformation[] } type SortField = "pageName" | "pagePath" | "updatedAt" type SortDirection = "asc" | "desc" export function InformationClient({ initialData = [] }: InformationClientProps) { const router = useRouter() const params = useParams() const lng = (params?.lng as string) || 'ko' const { t } = useTranslation(lng, 'menu') // 안전한 번역 함수 (키가 없을 때 원본 키 반환) const safeTranslate = (key: string): string => { try { const translated = t(key) // 번역 키가 그대로 반환되는 경우 원본 키 사용 if (translated === key) { return key } return translated || key } catch (error) { console.warn(`Translation failed for key: ${key}`, error) return key } } const [informations, setInformations] = useState(initialData) const [loading, setLoading] = useState(false) const [searchQuery, setSearchQuery] = useState("") const [sortField, setSortField] = useState("updatedAt") const [sortDirection, setSortDirection] = useState("desc") const [editingInformation, setEditingInformation] = useState(null) const [isEditDialogOpen, setIsEditDialogOpen] = useState(false) const [isSyncing, setIsSyncing] = useState(false) const [, startTransition] = useTransition() // 정보 목록 조회 const fetchInformations = async () => { try { setLoading(true) startTransition(async () => { const result = await getInformationLists() if (result?.data) { setInformations(result.data) } else { toast.error("정보 목록을 가져오는데 실패했습니다.") } setLoading(false) }) } catch (error) { console.error("Error fetching informations:", error) toast.error("정보 목록을 가져오는데 실패했습니다.") setLoading(false) } } // 클라이언트 사이드 필터링 및 정렬 const filteredAndSortedInformations = React.useMemo(() => { let filtered = informations // 검색 필터 (페이지명으로 검색) if (searchQuery) { filtered = filtered.filter(info => safeTranslate(info.pageName).toLowerCase().includes(searchQuery.toLowerCase()) || info.pagePath.toLowerCase().includes(searchQuery.toLowerCase()) ) } // 정렬 filtered = filtered.sort((a, b) => { let aValue: string | Date let bValue: string | Date switch (sortField) { case "pageName": aValue = safeTranslate(a.pageName) bValue = safeTranslate(b.pageName) break case "pagePath": aValue = a.pagePath bValue = b.pagePath break case "updatedAt": aValue = new Date(a.updatedAt) bValue = new Date(b.updatedAt) break default: return 0 } if (aValue < bValue) return sortDirection === "asc" ? -1 : 1 if (aValue > bValue) return sortDirection === "asc" ? 1 : -1 return 0 }) return filtered }, [informations, searchQuery, sortField, sortDirection, safeTranslate]) // 검색 핸들러 (클라이언트 사이드에서 필터링하므로 별도 동작 불필요) const handleSearch = () => { // 클라이언트 사이드 필터링이므로 별도 서버 요청 불필요 } // 정렬 함수 const sortInformations = (informations: PageInformation[]) => { return [...informations].sort((a, b) => { let aValue: string | Date let bValue: string | Date if (sortField === "pageName") { aValue = safeTranslate(a.pageName) bValue = safeTranslate(b.pageName) } else if (sortField === "pagePath") { aValue = a.pagePath bValue = b.pagePath } else { aValue = new Date(a.updatedAt) bValue = new Date(b.updatedAt) } 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 handleEdit = async (information: PageInformation) => { try { // 첨부파일 정보까지 포함해서 가져오기 const detailData = await getInformationDetail(information.id) if (detailData) { setEditingInformation(detailData) } else { // 실패시 기본 정보라도 사용 setEditingInformation(information) } setIsEditDialogOpen(true) } catch (error) { console.error("Failed to load information detail:", error) // 에러시 기본 정보라도 사용 setEditingInformation(information) setIsEditDialogOpen(true) } } // 편집 완료 핸들러 const handleEditClose = () => { setIsEditDialogOpen(false) setEditingInformation(null) // 데이터 새로고침 fetchInformations() } // 다운로드 핸들러 (다중 첨부파일은 dialog에서 처리) const handleDownload = async (information: PageInformation) => { try { // 첨부파일 정보까지 포함해서 가져오기 const detailData = await getInformationDetail(information.id) if (detailData) { setEditingInformation(detailData) } else { // 실패시 기본 정보라도 사용 setEditingInformation(information) } setIsEditDialogOpen(true) } catch (error) { console.error("Failed to load information detail:", error) // 에러시 기본 정보라도 사용 setEditingInformation(information) setIsEditDialogOpen(true) } } // 메뉴 동기화 핸들러 const handleSync = async () => { setIsSyncing(true) try { const result = await syncInformationFromMenuAssignments() if (result.success) { toast.success(result.message) // 동기화 후 데이터 새로고침 fetchInformations() } else { toast.error(result.message) } } catch (error) { console.error("동기화 오류:", error) toast.error("메뉴 동기화 중 오류가 발생했습니다.") } finally { setIsSyncing(false) } } useEffect(() => { if (initialData.length > 0) { setInformations(initialData) } else { fetchInformations() } }, []) // searchQuery 변경 시 클라이언트 사이드 필터링으로 처리되므로 useEffect 제거 return (
{/* 검색 */}
setSearchQuery(e.target.value)} className="pl-10" onKeyPress={(e) => e.key === "Enter"} />
{/* 정보 테이블 */}
정보 내용 상태 수정자 수정일 작업 {loading ? ( 로딩 중... ) : filteredAndSortedInformations.length === 0 ? ( 안내사항이 없습니다. ) : ( filteredAndSortedInformations.map((information) => (
{(information as any).translatedPageName || safeTranslate(information.pageName)}
{information.pagePath}
{information.isActive ? "활성" : "비활성"} {information.updatedByName || '시스템'} {formatDate(information.updatedAt, "KR")} {information.updatedAt && information.updatedAt !== information.createdAt ? formatDate(information.updatedAt, "KR") : '-' } )) )}
{/* 편집 다이얼로그 */} {editingInformation && ( )}
) }