diff options
| author | joonhoekim <26rote@gmail.com> | 2025-10-29 15:59:04 +0900 |
|---|---|---|
| committer | joonhoekim <26rote@gmail.com> | 2025-10-29 15:59:04 +0900 |
| commit | 2ecdac866c19abea0b5389708fcdf5b3889c969a (patch) | |
| tree | e02a02cfa0890691fb28a7df3a96ef495b3d4b79 /lib/swp/table/swp-table-columns.tsx | |
| parent | 2fc9e5492e220041ba322d9a1479feb7803228cf (diff) | |
(김준회) SWP 파일 업로드 취소 기능 추가, 업로드 파일명 검증로직에서 파일명 비필수로 변경
Diffstat (limited to 'lib/swp/table/swp-table-columns.tsx')
| -rw-r--r-- | lib/swp/table/swp-table-columns.tsx | 353 |
1 files changed, 18 insertions, 335 deletions
diff --git a/lib/swp/table/swp-table-columns.tsx b/lib/swp/table/swp-table-columns.tsx index 9aecea96..e6abd2a0 100644 --- a/lib/swp/table/swp-table-columns.tsx +++ b/lib/swp/table/swp-table-columns.tsx @@ -2,43 +2,26 @@ 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 { useState } from "react"; -import { toast } from "sonner"; +import type { DocumentListItem } from "@/lib/swp/document-service"; -export const swpDocumentColumns: ColumnDef<SwpDocumentWithStats>[] = [ +export const swpDocumentColumns: ColumnDef<DocumentListItem>[] = [ { id: "expander", header: () => null, - cell: () => { - return ( - <Button - variant="ghost" - size="sm" - className="h-8 w-8 p-0" - > - <ChevronRight className="h-4 w-4" /> - </Button> - ); - }, + cell: () => null, size: 50, }, { accessorKey: "LTST_ACTV_STAT", - header: "상태 (최신 액티비티)", + header: "상태", cell: ({ row }) => { const status = row.original.LTST_ACTV_STAT; if (!status) return "-"; - // 상태에 따른 색상 설정 (필요에 따라 조정 가능) const color = - status === "Complete" ? "bg-green-100 text-green-800" : - status === "In Progress" ? "bg-blue-100 text-blue-800" : - status === "Pending" ? "bg-yellow-100 text-yellow-800" : + status.includes("Complete") ? "bg-green-100 text-green-800" : + status.includes("Progress") ? "bg-blue-100 text-blue-800" : + status.includes("Pending") || status.includes("Ready") ? "bg-yellow-100 text-yellow-800" : "bg-gray-100 text-gray-800"; return ( @@ -47,7 +30,7 @@ export const swpDocumentColumns: ColumnDef<SwpDocumentWithStats>[] = [ </Badge> ); }, - size: 100, + size: 120, }, { accessorKey: "DOC_NO", @@ -61,7 +44,7 @@ export const swpDocumentColumns: ColumnDef<SwpDocumentWithStats>[] = [ accessorKey: "DOC_TITLE", header: "문서제목", cell: ({ row }) => ( - <div className="max-w-md" title={row.original.DOC_TITLE}> + <div className="max-w-md truncate" title={row.original.DOC_TITLE}> {row.original.DOC_TITLE} </div> ), @@ -74,7 +57,7 @@ export const swpDocumentColumns: ColumnDef<SwpDocumentWithStats>[] = [ <div> <div className="font-medium">{row.original.PROJ_NO}</div> {row.original.PROJ_NM && ( - <div className="text-xs text-muted-foreground max-w-[150px]"> + <div className="text-xs text-muted-foreground max-w-[150px] truncate"> {row.original.PROJ_NM} </div> )} @@ -127,325 +110,25 @@ export const swpDocumentColumns: ColumnDef<SwpDocumentWithStats>[] = [ }, { accessorKey: "LTST_REV_NO", - header: "마지막 REV NO", + header: "최신 REV", cell: ({ row }) => row.original.LTST_REV_NO || "-", size: 80, }, { id: "stats", - header: "REV/파일", + header: "파일", cell: ({ row }) => ( <div className="text-center"> <div className="text-sm font-medium"> - {row.original.revision_count} / {row.original.file_count} + {row.original.fileCount}개 </div> + {row.original.standbyFileCount > 0 && ( + <div className="text-xs text-yellow-600"> + 대기중 {row.original.standbyFileCount} + </div> + )} </div> ), size: 100, }, - { - accessorKey: "last_synced_at", - header: "동기화", - cell: ({ row }) => ( - <div className="text-xs text-muted-foreground"> - {formatDistanceToNow(new Date(row.original.last_synced_at), { - addSuffix: true, - locale: ko, - })} - </div> - ), - 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<RevisionRow>[] = [ - { - id: "expander", - header: () => null, - cell: ({ row }) => { - return row.getCanExpand() ? ( - <Button - variant="ghost" - size="sm" - className="h-8 w-8 p-0 ml-8" - > - {row.getIsExpanded() ? ( - <ChevronDown className="h-4 w-4" /> - ) : ( - <ChevronRight className="h-4 w-4" /> - )} - </Button> - ) : null; - }, - size: 100, - }, - { - accessorKey: "REV_NO", - header: "리비전", - cell: ({ row }) => ( - <Badge variant="secondary" className="font-mono"> - REV {row.original.REV_NO} - </Badge> - ), - 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 ( - <Badge variant="outline" className={color}> - {stage} - </Badge> - ); - }, - size: 100, - }, - { - accessorKey: "OFDC_NO", - header: "OFDC 번호", - cell: ({ row }) => ( - <div className="font-mono text-sm">{row.original.OFDC_NO || "-"}</div> - ), - size: 200, - }, - { - accessorKey: "ACTV_NO", - header: "Activity", - cell: ({ row }) => ( - <div className="font-mono text-xs text-muted-foreground"> - {row.original.ACTV_NO || "-"} - </div> - ), - size: 250, - }, - { - id: "file_count", - header: "파일 수", - cell: ({ row }) => ( - <div className="flex items-center gap-2"> - <FileIcon className="h-4 w-4 text-muted-foreground" /> - <span className="font-medium">{row.original.file_count}</span> - </div> - ), - size: 100, - }, - { - accessorKey: "last_synced_at", - header: "동기화", - cell: ({ row }) => ( - <div className="text-xs text-muted-foreground"> - {formatDistanceToNow(new Date(row.original.last_synced_at), { - addSuffix: true, - locale: ko, - })} - </div> - ), - 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<FileRow>[] = [ - { - id: "spacer", - header: () => null, - cell: () => <div className="w-16" />, - size: 150, - }, - { - accessorKey: "FILE_SEQ", - header: "순서", - cell: ({ row }) => ( - <Badge variant="outline" className="font-mono"> - #{row.original.FILE_SEQ} - </Badge> - ), - size: 80, - }, - { - accessorKey: "FILE_NM", - header: "파일명", - cell: ({ row }) => ( - <div className="flex items-center gap-2"> - <FileIcon className="h-4 w-4 text-blue-500" /> - <span className="font-mono text-sm">{row.original.FILE_NM}</span> - </div> - ), - 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 ( - <Badge variant="outline" className={color}> - {status} - </Badge> - ); - }, - size: 100, - }, - { - accessorKey: "FLD_PATH", - header: "경로", - cell: ({ row }) => ( - <div className="font-mono text-xs text-muted-foreground truncate max-w-[200px]" title={row.original.FLD_PATH || ""}> - {row.original.FLD_PATH || "-"} - </div> - ), - size: 200, - }, - { - accessorKey: "created_at", - header: "생성일", - cell: ({ row }) => ( - <div className="text-xs text-muted-foreground"> - {formatDistanceToNow(new Date(row.original.created_at), { - addSuffix: true, - locale: ko, - })} - </div> - ), - size: 100, - }, - { - id: "actions", - header: "작업", - cell: ({ row }) => ( - <DownloadButton fileId={row.original.id} fileName={row.original.FILE_NM} /> - ), - 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); - - // API Route 호출 (바이너리 직접 전송) - const response = await fetch(`/api/swp/download/${fileId}`); - - if (!response.ok) { - const errorData = await response.json().catch(() => ({ error: "다운로드 실패" })); - toast.error(errorData.error || "파일 다운로드 실패"); - return; - } - - // 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(`파일 다운로드 완료: ${fileName}`); - } catch (error) { - console.error("다운로드 오류:", error); - toast.error("파일 다운로드 중 오류가 발생했습니다."); - } finally { - setIsDownloading(false); - } - }; - - return ( - <Button - variant="outline" - size="sm" - onClick={handleDownload} - disabled={isDownloading} - > - {isDownloading ? ( - <> - <Loader2 className="h-4 w-4 mr-1 animate-spin" /> - 다운로드 중... - </> - ) : ( - <> - <Download className="h-4 w-4 mr-1" /> - 다운로드 - </> - )} - </Button> - ); -} - |
