From 795b4915069c44f500a91638e16ded67b9e16618 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Tue, 1 Jul 2025 11:46:33 +0000 Subject: (최겸) 정보시스템 공지사항 개발 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/information/information-client.tsx | 340 ++++++++++++++++++++++++++ 1 file changed, 340 insertions(+) create mode 100644 components/information/information-client.tsx (limited to 'components/information/information-client.tsx') diff --git a/components/information/information-client.tsx b/components/information/information-client.tsx new file mode 100644 index 00000000..513b8f20 --- /dev/null +++ b/components/information/information-client.tsx @@ -0,0 +1,340 @@ +"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, + Download +} from "lucide-react" +import { toast } from "sonner" +import { formatDate } from "@/lib/utils" +import { getInformationLists } from "@/lib/information/service" +import type { PageInformation } from "@/db/schema/information" +import { UpdateInformationDialog } from "@/lib/information/table/update-information-dialog" + +interface InformationClientProps { + initialData?: PageInformation[] +} + +type SortField = "pageName" | "pagePath" | "createdAt" +type SortDirection = "asc" | "desc" + +export function InformationClient({ initialData = [] }: InformationClientProps) { + const [informations, setInformations] = useState(initialData) + const [loading, setLoading] = useState(false) + const [searchQuery, setSearchQuery] = useState("") + const [sortField, setSortField] = useState("createdAt") + const [sortDirection, setSortDirection] = useState("desc") + const [editingInformation, setEditingInformation] = useState(null) + const [isEditDialogOpen, setIsEditDialogOpen] = useState(false) + const [, startTransition] = useTransition() + + // 정보 목록 조회 + const fetchInformations = async () => { + try { + setLoading(true) + const search = searchQuery || undefined + + startTransition(async () => { + const result = await getInformationLists({ + page: 1, + perPage: 50, + search: search, + sort: [{ id: sortField, desc: sortDirection === "desc" }], + flags: [], + filters: [], + joinOperator: "and", + pagePath: "", + pageName: "", + informationContent: "", + isActive: null, + }) + + if (result?.data) { + setInformations(result.data) + } else { + toast.error("정보 목록을 가져오는데 실패했습니다.") + } + setLoading(false) + }) + } catch (error) { + console.error("Error fetching informations:", error) + toast.error("정보 목록을 가져오는데 실패했습니다.") + setLoading(false) + } + } + + // 검색 핸들러 + const handleSearch = () => { + fetchInformations() + } + + // 정렬 함수 + const sortInformations = (informations: PageInformation[]) => { + return [...informations].sort((a, b) => { + let aValue: string | Date + let bValue: string | Date + + if (sortField === "pageName") { + aValue = a.pageName + bValue = b.pageName + } 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 handleEdit = (information: PageInformation) => { + setEditingInformation(information) + setIsEditDialogOpen(true) + } + + // 편집 완료 핸들러 + const handleEditClose = () => { + setIsEditDialogOpen(false) + setEditingInformation(null) + // 데이터 새로고침 + fetchInformations() + } + + // 다운로드 핸들러 + const handleDownload = (information: PageInformation) => { + if (information.attachmentFilePath && information.attachmentFileName) { + const link = document.createElement('a') + link.href = information.attachmentFilePath + link.download = information.attachmentFileName + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + } + } + + // 정렬된 정보 목록 + const sortedInformations = sortInformations(informations) + + useEffect(() => { + if (initialData.length > 0) { + setInformations(initialData) + } else { + fetchInformations() + } + }, []) + + useEffect(() => { + if (searchQuery !== "") { + fetchInformations() + } else if (initialData.length > 0) { + setInformations(initialData) + } + }, [searchQuery]) + + return ( +
+ {/* 검색 */} +
+
+
+ + setSearchQuery(e.target.value)} + className="pl-10" + onKeyPress={(e) => e.key === "Enter" && handleSearch()} + /> +
+ + +
+
+ + {/* 정보 테이블 */} +
+ + + + + + + + + + 정보 내용 + 첨부파일 + 상태 + + + + 작업 + + + + {loading ? ( + + + 로딩 중... + + + ) : informations.length === 0 ? ( + + + 정보가 없습니다. + + + ) : ( + sortedInformations.map((information) => ( + + +
+ + + {information.pageName} + +
+
+ + + {information.pagePath} + + + +
+ + + {information.attachmentFileName ? ( + + ) : ( + 없음 + )} + + + + {information.isActive ? "활성" : "비활성"} + + + + {formatDate(information.createdAt)} + + + + + + )) + )} + +
+
+ + {/* 편집 다이얼로그 */} + {editingInformation && ( + + )} +
+ ) +} \ No newline at end of file -- cgit v1.2.3