diff options
Diffstat (limited to 'lib/vendor-document-list/ship-all/enhanced-doc-table-columns.tsx')
| -rw-r--r-- | lib/vendor-document-list/ship-all/enhanced-doc-table-columns.tsx | 540 |
1 files changed, 540 insertions, 0 deletions
diff --git a/lib/vendor-document-list/ship-all/enhanced-doc-table-columns.tsx b/lib/vendor-document-list/ship-all/enhanced-doc-table-columns.tsx new file mode 100644 index 00000000..6c9a9ab6 --- /dev/null +++ b/lib/vendor-document-list/ship-all/enhanced-doc-table-columns.tsx @@ -0,0 +1,540 @@ +// simplified-doc-table-columns.tsx +"use client" + +import * as React from "react" +import { ColumnDef } from "@tanstack/react-table" +import { formatDate, formatDateTime } from "@/lib/utils" +import { Checkbox } from "@/components/ui/checkbox" +import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header" +import { DataTableRowAction } from "@/types/table" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" +import { Button } from "@/components/ui/button" +import { + Ellipsis, + FileText, + Eye, + Edit, + Trash2, +} from "lucide-react" +import { cn } from "@/lib/utils" +import { SimplifiedDocumentsView } from "@/db/schema" + +// DocumentSelectionContext를 import (실제 파일 경로에 맞게 수정 필요) +// 예: import { DocumentSelectionContextAll } from "../user-vendor-document-display" +// 또는: import { DocumentSelectionContextAll } from "./user-vendor-document-display" +import { DocumentSelectionContextAll } from "@/components/ship-vendor-document-all/user-vendor-document-table-container" + +interface GetColumnsProps { + setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<SimplifiedDocumentsView> | null>> +} + +// 날짜 표시 컴포넌트 (간단 버전) +const DateDisplay = ({ date, isSelected = false }: { date: string | null, isSelected?: boolean }) => { + if (!date) return <span className="text-gray-400">-</span> + + return ( + <span className={cn( + "text-sm", + isSelected && "text-blue-600 font-semibold" + )}> + {formatDate(date)} + </span> + ) +} + +export function getSimplifiedDocumentColumns({ + setRowAction, +}: GetColumnsProps): ColumnDef<SimplifiedDocumentsView>[] { + + const columns: ColumnDef<SimplifiedDocumentsView>[] = [ + // 라디오 버튼 같은 체크박스 선택 + { + id: "select", + header: ({ table }) => ( + <div className="flex items-center justify-center"> + <span className="text-xs text-gray-500">Select</span> + </div> + ), + cell: ({ row }) => { + const doc = row.original + + return ( + <SelectCell documentId={doc.documentId} /> + ) + }, + size: 40, + enableSorting: false, + enableHiding: false, + }, + + // 문서번호 (선택된 행 하이라이트 적용) + { + accessorKey: "docNumber", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="Document No" /> + ), + cell: ({ row }) => { + const doc = row.original + + return ( + <DocNumberCell doc={doc} /> + ) + }, + size: 120, + enableResizing: true, + meta: { + excelHeader: "Document No" + }, + }, + + // 문서명 (선택된 행 하이라이트 적용) + { + accessorKey: "title", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="Title" /> + ), + cell: ({ row }) => { + const doc = row.original + + return ( + <TitleCell doc={doc} /> + ) + }, + enableResizing: true, + maxSize:300, + meta: { + excelHeader: "Title" + }, + }, + + // 프로젝트 코드 + { + accessorKey: "projectCode", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="Project" /> + ), + cell: ({ row }) => { + const projectCode = row.original.projectCode + + return ( + <ProjectCodeCell projectCode={projectCode} documentId={row.original.documentId} /> + ) + }, + enableResizing: true, + maxSize:100, + meta: { + excelHeader: "Project" + }, + }, + + // 벤더명 + { + accessorKey: "vendorName", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="Vendor Name" /> + ), + cell: ({ row }) => { + const vendorName = row.original.vendorName + + return ( + <VendorNameCell vendorName={vendorName} documentId={row.original.documentId} /> + ) + }, + enableResizing: true, + maxSize: 200, + meta: { + excelHeader: "Vendor Name" + }, + }, + + // 벤더 코드 + { + accessorKey: "vendorCode", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="Vendor Code" /> + ), + cell: ({ row }) => { + const vendorCode = row.original.vendorCode + + return ( + <VendorCodeCell vendorCode={vendorCode} documentId={row.original.documentId} /> + ) + }, + enableResizing: true, + maxSize: 120, + meta: { + excelHeader: "Vendor Code" + }, + }, + + // 1차 스테이지 그룹 + { + id: "firstStageGroup", + header: ({ table }) => { + // 첫 번째 행의 firstStageName을 그룹 헤더로 사용 + const firstRow = table.getRowModel().rows[0]?.original + const stageName = firstRow?.firstStageName || "First Stage" + return ( + <div className="text-center font-medium text-gray-700"> + {stageName} + </div> + ) + }, + columns: [ + { + accessorKey: "firstStagePlanDate", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="Planned Date" /> + ), + cell: ({ row }) => { + return <FirstStagePlanDateCell row={row} /> + }, + enableResizing: true, + meta: { + excelHeader: "First Planned Date" + }, + }, + { + accessorKey: "firstStageActualDate", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="Actual Date" /> + ), + cell: ({ row }) => { + return <FirstStageActualDateCell row={row} /> + }, + enableResizing: true, + meta: { + excelHeader: "First Actual Date" + }, + }, + ], + }, + + // 2차 스테이지 그룹 + { + id: "secondStageGroup", + header: ({ table }) => { + // 첫 번째 행의 secondStageName을 그룹 헤더로 사용 + const firstRow = table.getRowModel().rows[0]?.original + const stageName = firstRow?.secondStageName || "Second Stage" + return ( + <div className="text-center font-medium text-gray-700"> + {stageName} + </div> + ) + }, + columns: [ + { + accessorKey: "secondStagePlanDate", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="Planned Date" /> + ), + cell: ({ row }) => { + return <SecondStagePlanDateCell row={row} /> + }, + enableResizing: true, + meta: { + excelHeader: "Second Planned Date" + }, + }, + { + accessorKey: "secondStageActualDate", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="Actual Date" /> + ), + cell: ({ row }) => { + return <SecondStageActualDateCell row={row} /> + }, + enableResizing: true, + meta: { + excelHeader: "Second Actual Date" + }, + }, + ], + }, + + // 첨부파일 수 + { + accessorKey: "attachmentCount", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="Files" /> + ), + cell: ({ row }) => { + const count = row.original.attachmentCount || 0 + + return ( + <AttachmentCountCell count={count} documentId={row.original.documentId} /> + ) + }, + size: 60, + enableResizing: true, + meta: { + excelHeader: "Attachments" + }, + }, + + // 업데이트 일시 + { + accessorKey: "updatedAt", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="Updated" /> + ), + cell: ({ cell, row }) => { + return ( + <UpdatedAtCell updatedAt={cell.getValue() as Date} documentId={row.original.documentId} /> + ) + }, + enableResizing: true, + meta: { + excelHeader: "Updated" + }, + }, + + // 액션 버튼 + // { + // id: "actions", + // header: () => <span className="sr-only">Actions</span>, + // cell: ({ row }) => { + // const doc = row.original + // return ( + // <DropdownMenu> + // <DropdownMenuTrigger asChild> + // <Button variant="ghost" className="h-8 w-8 p-0"> + // <span className="sr-only">Open menu</span> + // <Ellipsis className="h-4 w-4" /> + // </Button> + // </DropdownMenuTrigger> + // <DropdownMenuContent align="end"> + // <DropdownMenuItem + // onClick={() => setRowAction({ type: "view", row: doc })} + // > + // <Eye className="mr-2 h-4 w-4" /> + // 보기 + // </DropdownMenuItem> + // <DropdownMenuItem + // onClick={() => setRowAction({ type: "edit", row: doc })} + // > + // <Edit className="mr-2 h-4 w-4" /> + // 편집 + // </DropdownMenuItem> + // <DropdownMenuSeparator /> + // <DropdownMenuItem + // onClick={() => setRowAction({ type: "delete", row: doc })} + // className="text-red-600" + // > + // <Trash2 className="mr-2 h-4 w-4" /> + // 삭제 + // <DropdownMenuShortcut>⌫</DropdownMenuShortcut> + // </DropdownMenuItem> + // </DropdownMenuContent> + // </DropdownMenu> + // ) + // }, + // size: 50, + // enableSorting: false, + // enableHiding: false, + // }, + ] + + return columns +} + +// 개별 셀 컴포넌트들 (Context 사용) +function SelectCell({ documentId }: { documentId: number }) { + const { selectedDocumentId, setSelectedDocumentId } = React.useContext(DocumentSelectionContextAll); + const isSelected = selectedDocumentId === documentId; + + return ( + <div className="flex items-center justify-center"> + <input + type="radio" + checked={isSelected} + onChange={() => { + const newSelection = isSelected ? null : documentId; + setSelectedDocumentId(newSelection); + }} + className="cursor-pointer w-4 h-4" + /> + </div> + ); +} + +function DocNumberCell({ doc }: { doc: SimplifiedDocumentsView }) { + const { selectedDocumentId, setSelectedDocumentId } = React.useContext(DocumentSelectionContextAll); + const isSelected = selectedDocumentId === doc.documentId; + + return ( + <div + className={cn( + "font-mono text-sm font-medium cursor-pointer px-2 py-1 rounded transition-colors", + isSelected + ? "text-blue-600 font-bold bg-blue-50" + : "hover:bg-gray-50" + )} + onClick={() => { + const newSelection = isSelected ? null : doc.documentId; + setSelectedDocumentId(newSelection); + }} + > + {doc.docNumber} + </div> + ); +} + +function TitleCell({ doc }: { doc: SimplifiedDocumentsView }) { + const { selectedDocumentId, setSelectedDocumentId } = React.useContext(DocumentSelectionContextAll); + const isSelected = selectedDocumentId === doc.documentId; + + return ( + <div + className={cn( + "font-medium text-gray-900 truncate max-w-[300px] cursor-pointer px-2 py-1 rounded transition-colors", + isSelected + ? "text-blue-600 font-bold bg-blue-50" + : "hover:bg-gray-50" + )} + title={doc.title} + onClick={() => { + const newSelection = isSelected ? null : doc.documentId; + setSelectedDocumentId(newSelection); + }} + > + {doc.title} + </div> + ); +} + +function ProjectCodeCell({ projectCode, documentId }: { projectCode: string | null, documentId: number }) { + const { selectedDocumentId } = React.useContext(DocumentSelectionContextAll); + const isSelected = selectedDocumentId === documentId; + + if (!projectCode) return <span className="text-gray-400">-</span>; + + return ( + <span className={cn( + "text-sm font-medium", + isSelected && "text-blue-600 font-bold" + )}> + {projectCode} + </span> + ); +} + +function VendorNameCell({ vendorName, documentId }: { vendorName: string | null, documentId: number }) { + const { selectedDocumentId } = React.useContext(DocumentSelectionContextAll); + const isSelected = selectedDocumentId === documentId; + + if (!vendorName) return <span className="text-gray-400">-</span>; + + return ( + <div + className={cn( + "text-sm font-medium truncate max-w-[200px]", + isSelected && "text-blue-600 font-bold" + )} + title={vendorName} + > + {vendorName} + </div> + ); +} + +function VendorCodeCell({ vendorCode, documentId }: { vendorCode: string | null, documentId: number }) { + const { selectedDocumentId } = React.useContext(DocumentSelectionContextAll); + const isSelected = selectedDocumentId === documentId; + + if (!vendorCode) return <span className="text-gray-400">-</span>; + + return ( + <span className={cn( + "text-sm font-medium font-mono", + isSelected && "text-blue-600 font-bold" + )}> + {vendorCode} + </span> + ); +} + +function FirstStagePlanDateCell({ row }: { row: any }) { + const { selectedDocumentId } = React.useContext(DocumentSelectionContextAll); + const isSelected = selectedDocumentId === row.original.documentId; + + return <DateDisplay date={row.original.firstStagePlanDate} isSelected={isSelected} />; +} + +function FirstStageActualDateCell({ row }: { row: any }) { + const { selectedDocumentId } = React.useContext(DocumentSelectionContextAll); + const isSelected = selectedDocumentId === row.original.documentId; + const date = row.original.firstStageActualDate; + + return ( + <div className={cn( + date ? "text-green-600 font-medium" : "", + isSelected && date && "text-green-700 font-bold" + )}> + <DateDisplay date={date} isSelected={isSelected && !date} /> + {date && <span className="text-xs block">✓ 완료</span>} + </div> + ); +} + +function SecondStagePlanDateCell({ row }: { row: any }) { + const { selectedDocumentId } = React.useContext(DocumentSelectionContextAll); + const isSelected = selectedDocumentId === row.original.documentId; + + return <DateDisplay date={row.original.secondStagePlanDate} isSelected={isSelected} />; +} + +function SecondStageActualDateCell({ row }: { row: any }) { + const { selectedDocumentId } = React.useContext(DocumentSelectionContextAll); + const isSelected = selectedDocumentId === row.original.documentId; + const date = row.original.secondStageActualDate; + + return ( + <div className={cn( + date ? "text-green-600 font-medium" : "", + isSelected && date && "text-green-700 font-bold" + )}> + <DateDisplay date={date} isSelected={isSelected && !date} /> + {date && <span className="text-xs block">✓ 완료</span>} + </div> + ); +} + +function AttachmentCountCell({ count, documentId }: { count: number, documentId: number }) { + const { selectedDocumentId } = React.useContext(DocumentSelectionContextAll); + const isSelected = selectedDocumentId === documentId; + + return ( + <div className="flex items-center justify-center gap-1"> + <FileText className="w-4 h-4 text-gray-400" /> + <span className={cn( + "text-sm font-medium", + isSelected && "text-blue-600 font-bold" + )}> + {count} + </span> + </div> + ); +} + +function UpdatedAtCell({ updatedAt, documentId }: { updatedAt: Date, documentId: number }) { + const { selectedDocumentId } = React.useContext(DocumentSelectionContextAll); + const isSelected = selectedDocumentId === documentId; + + return ( + <span className={cn( + "text-sm text-gray-600", + isSelected && "text-blue-600 font-semibold" + )}> + {formatDateTime(updatedAt)} + </span> + ); +}
\ No newline at end of file |
