"use client" import * as React from "react" import { type DataTableRowAction } from "@/types/table" import { type ColumnDef } from "@tanstack/react-table" import { Ellipsis, Paperclip, FileText } from "lucide-react" import { toast } from "sonner" import { formatDate, formatDateTime } from "@/lib/utils" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Checkbox } from "@/components/ui/checkbox" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header" import { ProjectGtcView } from "@/db/schema" interface GetColumnsProps { setRowAction: React.Dispatch | null>> } /** * 파일 다운로드 함수 */ const handleFileDownload = async (projectId: number, fileName: string) => { try { // API를 통해 파일 다운로드 const response = await fetch(`/api/project-gtc?action=download&projectId=${projectId}`, { method: 'GET', }); if (!response.ok) { throw new Error('파일 다운로드에 실패했습니다.'); } // 파일 blob 생성 const blob = await response.blob(); // 다운로드 링크 생성 const url = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = fileName; document.body.appendChild(link); link.click(); document.body.removeChild(link); // 메모리 정리 window.URL.revokeObjectURL(url); toast.success("파일 다운로드를 시작합니다."); } catch (error) { console.error("파일 다운로드 오류:", error); toast.error("파일 다운로드 중 오류가 발생했습니다."); } }; /** * tanstack table 컬럼 정의 (중첩 헤더 버전) */ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef[] { // ---------------------------------------------------------------- // 1) select 컬럼 (체크박스) // ---------------------------------------------------------------- const selectColumn: ColumnDef = { id: "select", header: ({ table }) => ( table.toggleAllPageRowsSelected(!!value)} aria-label="Select all" className="translate-y-0.5" /> ), cell: ({ row }) => ( row.toggleSelected(!!value)} aria-label="Select row" className="translate-y-0.5" /> ), maxSize: 30, enableSorting: false, enableHiding: false, } // ---------------------------------------------------------------- // 2) 파일 다운로드 컬럼 (아이콘) // ---------------------------------------------------------------- const downloadColumn: ColumnDef = { id: "download", header: "", cell: ({ row }) => { const project = row.original; if (!project.filePath || !project.originalFileName) { return null; } return ( ); }, maxSize: 30, enableSorting: false, } // ---------------------------------------------------------------- // 3) actions 컬럼 (Dropdown 메뉴) // ---------------------------------------------------------------- const actionsColumn: ColumnDef = { id: "actions", enableHiding: false, cell: function Cell({ row }) { return ( setRowAction({ type: "upload", row })} > Edit setRowAction({ type: "delete", row })} > Delete ⌘⌫ ) }, maxSize: 30, } // ---------------------------------------------------------------- // 4) 일반 컬럼들을 "그룹"별로 묶어 중첩 columns 생성 // ---------------------------------------------------------------- // 4-1) groupMap: { [groupName]: ColumnDef[] } const groupMap: Record[]> = {} // 프로젝트 정보 그룹 groupMap["기본 정보"] = [ { accessorKey: "code", header: ({ column }) => ( ), cell: ({ row }) => { return (
{row.getValue("code")}
) }, filterFn: (row, id, value) => { return value.includes(row.getValue(id)) }, minSize: 120, }, { accessorKey: "name", header: ({ column }) => ( ), cell: ({ row }) => { return (
{row.getValue("name")}
) }, filterFn: (row, id, value) => { return value.includes(row.getValue(id)) }, minSize: 200, }, { accessorKey: "type", header: ({ column }) => ( ), cell: ({ row }) => { const type = row.getValue("type") as string return (
{type}
) }, filterFn: (row, id, value) => { return value.includes(row.getValue(id)) }, minSize: 100, }, ] // 파일 정보 그룹 groupMap["파일 정보"] = [ { accessorKey: "originalFileName", header: ({ column }) => ( ), cell: ({ row }) => { const fileName = row.getValue("originalFileName") as string | null const filePath = row.original.filePath if (!fileName) { return (
파일 없음
) } return (
{filePath ? ( ) : ( {fileName} )}
) }, minSize: 200, }, ] // 날짜 정보 그룹 groupMap["날짜 정보"] = [ { accessorKey: "gtcCreatedAt", header: ({ column }) => ( ), cell: ({ row }) => { const date = row.getValue("gtcCreatedAt") as Date | null if (!date) { return - } return (
{formatDateTime(new Date(date))}
) }, minSize: 150, }, { accessorKey: "projectCreatedAt", header: ({ column }) => ( ), cell: ({ row }) => { const date = row.getValue("projectCreatedAt") as Date return (
{formatDate(new Date(date))}
) }, minSize: 120, }, ] // ---------------------------------------------------------------- // 4-2) groupMap에서 실제 상위 컬럼(그룹)을 만들기 // ---------------------------------------------------------------- const nestedColumns: ColumnDef[] = [] // 순서를 고정하고 싶다면 group 순서를 미리 정의하거나 sort해야 함 Object.entries(groupMap).forEach(([groupName, colDefs]) => { // 상위 컬럼 nestedColumns.push({ id: groupName, header: groupName, // "프로젝트 정보", "파일 정보", "날짜 정보" 등 columns: colDefs, }) }) // ---------------------------------------------------------------- // 5) 최종 컬럼 배열: select, download, nestedColumns, actions // ---------------------------------------------------------------- return [ selectColumn, downloadColumn, // 다운로드 컬럼 추가 ...nestedColumns, actionsColumn, ] }