"use client"; import { ColumnDef } from "@tanstack/react-table"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { ChevronDown, ChevronRight, FileIcon, Download, Loader2 } from "lucide-react"; import { formatDistanceToNow } from "date-fns"; import { ko } from "date-fns/locale"; import type { SwpDocumentWithStats } from "../actions"; import { downloadSwpFile } from "../actions"; import { useState } from "react"; import { toast } from "sonner"; export const swpDocumentColumns: ColumnDef[] = [ { id: "expander", header: () => null, cell: () => { return ( ); }, size: 50, }, { accessorKey: "DOC_NO", header: "문서번호", cell: ({ row }) => (
{row.original.DOC_NO}
), size: 250, }, { accessorKey: "DOC_TITLE", header: "문서제목", cell: ({ row }) => (
{row.original.DOC_TITLE}
), size: 300, }, { accessorKey: "PROJ_NO", header: "프로젝트", cell: ({ row }) => (
{row.original.PROJ_NO}
{row.original.PROJ_NM && (
{row.original.PROJ_NM}
)}
), size: 150, }, { accessorKey: "PKG_NO", header: "패키지", cell: ({ row }) => row.original.PKG_NO || "-", size: 100, }, { accessorKey: "VNDR_CD", header: "업체", cell: ({ row }) => (
{row.original.VNDR_CD && (
{row.original.VNDR_CD}
)} {row.original.CPY_NM && (
{row.original.CPY_NM}
)}
), size: 120, }, { accessorKey: "STAGE", header: "스테이지", cell: ({ row }) => { const stage = row.original.STAGE; if (!stage) return "-"; const color = stage === "IFC" ? "bg-green-100 text-green-800" : stage === "IFA" ? "bg-blue-100 text-blue-800" : "bg-gray-100 text-gray-800"; return ( {stage} ); }, size: 80, }, { accessorKey: "LTST_REV_NO", header: "최신 REV", cell: ({ row }) => row.original.LTST_REV_NO || "-", size: 80, }, { id: "stats", header: "REV/파일", cell: ({ row }) => (
{row.original.revision_count} / {row.original.file_count}
), size: 100, }, { accessorKey: "sync_status", header: "상태", cell: ({ row }) => { const status = row.original.sync_status; const color = status === "synced" ? "bg-green-100 text-green-800" : status === "pending" ? "bg-yellow-100 text-yellow-800" : "bg-red-100 text-red-800"; return ( {status} ); }, size: 80, }, { accessorKey: "last_synced_at", header: "동기화", cell: ({ row }) => (
{formatDistanceToNow(new Date(row.original.last_synced_at), { addSuffix: true, locale: ko, })}
), size: 100, }, ]; // ============================================================================ // 리비전 컬럼 (서브 테이블용) // ============================================================================ export interface RevisionRow { id: number; DOC_NO: string; REV_NO: string; STAGE: string; ACTV_NO: string | null; OFDC_NO: string | null; sync_status: "synced" | "pending" | "error"; last_synced_at: Date; file_count: number; } export const swpRevisionColumns: ColumnDef[] = [ { id: "expander", header: () => null, cell: ({ row }) => { return row.getCanExpand() ? ( ) : null; }, size: 100, }, { accessorKey: "REV_NO", header: "리비전", cell: ({ row }) => ( REV {row.original.REV_NO} ), size: 100, }, { accessorKey: "STAGE", header: "스테이지", cell: ({ row }) => { const stage = row.original.STAGE; const color = stage === "IFC" ? "bg-green-100 text-green-800" : stage === "IFA" ? "bg-blue-100 text-blue-800" : "bg-gray-100 text-gray-800"; return ( {stage} ); }, size: 100, }, { accessorKey: "OFDC_NO", header: "OFDC 번호", cell: ({ row }) => (
{row.original.OFDC_NO || "-"}
), size: 200, }, { accessorKey: "ACTV_NO", header: "Activity", cell: ({ row }) => (
{row.original.ACTV_NO || "-"}
), size: 250, }, { id: "file_count", header: "파일 수", cell: ({ row }) => (
{row.original.file_count}
), size: 100, }, { accessorKey: "sync_status", header: "상태", cell: ({ row }) => { const status = row.original.sync_status; const color = status === "synced" ? "bg-green-100 text-green-800" : status === "pending" ? "bg-yellow-100 text-yellow-800" : "bg-red-100 text-red-800"; return ( {status} ); }, size: 80, }, { accessorKey: "last_synced_at", header: "동기화", cell: ({ row }) => (
{formatDistanceToNow(new Date(row.original.last_synced_at), { addSuffix: true, locale: ko, })}
), size: 100, }, ]; // ============================================================================ // 파일 컬럼 (서브 서브 테이블용) // ============================================================================ export interface FileRow { id: number; FILE_NM: string; FILE_SEQ: string; FILE_SZ: string | null; FLD_PATH: string | null; STAT: string | null; STAT_NM: string | null; sync_status: "synced" | "pending" | "error"; created_at: Date; } export const swpFileColumns: ColumnDef[] = [ { id: "spacer", header: () => null, cell: () =>
, size: 150, }, { accessorKey: "FILE_SEQ", header: "순서", cell: ({ row }) => ( #{row.original.FILE_SEQ} ), size: 80, }, { accessorKey: "FILE_NM", header: "파일명", cell: ({ row }) => (
{row.original.FILE_NM}
), size: 400, }, { accessorKey: "FILE_SZ", header: "크기", cell: ({ row }) => { const size = row.original.FILE_SZ; if (!size) return "-"; const bytes = parseInt(size, 10); if (isNaN(bytes)) return size; const kb = bytes / 1024; const mb = kb / 1024; return mb >= 1 ? `${mb.toFixed(2)} MB` : `${kb.toFixed(2)} KB`; }, size: 100, }, { accessorKey: "STAT_NM", header: "상태", cell: ({ row }) => { const status = row.original.STAT_NM; if (!status) return "-"; const color = status === "Complete" ? "bg-green-100 text-green-800" : "bg-gray-100 text-gray-800"; return ( {status} ); }, size: 100, }, { accessorKey: "FLD_PATH", header: "경로", cell: ({ row }) => (
{row.original.FLD_PATH || "-"}
), size: 200, }, { accessorKey: "created_at", header: "생성일", cell: ({ row }) => (
{formatDistanceToNow(new Date(row.original.created_at), { addSuffix: true, locale: ko, })}
), size: 100, }, { id: "actions", header: "작업", cell: ({ row }) => ( ), size: 120, }, ]; // ============================================================================ // 다운로드 버튼 컴포넌트: 임시 구성. Download.aspx 동작 안해서 일단 네트워크드라이브 사용하도록 처리 // ============================================================================ interface DownloadButtonProps { fileId: number; fileName: string; } function DownloadButton({ fileId, fileName }: DownloadButtonProps) { const [isDownloading, setIsDownloading] = useState(false); const handleDownload = async () => { try { setIsDownloading(true); // 서버 액션 호출 const result = await downloadSwpFile(fileId); if (!result.success || !result.data) { toast.error(result.error || "파일 다운로드 실패"); return; } // Blob 생성 및 다운로드 const blob = new Blob([result.data as unknown as BlobPart], { type: result.mimeType }); const url = window.URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = result.fileName || fileName; document.body.appendChild(link); link.click(); document.body.removeChild(link); window.URL.revokeObjectURL(url); toast.success(`파일 다운로드 완료: ${result.fileName}`); } catch (error) { console.error("다운로드 오류:", error); toast.error("파일 다운로드 중 오류가 발생했습니다."); } finally { setIsDownloading(false); } }; return ( ); }