diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-06-13 07:08:01 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-06-13 07:08:01 +0000 |
| commit | c72d0897f7b37843109c86f61d97eba05ba3ca0d (patch) | |
| tree | 887dd877f3f8beafa92b4d9a7b16c84b4a5795d8 /lib/vendor-document-list/ship/enhanced-doc-table-columns.tsx | |
| parent | ff902243a658067fae858a615c0629aa2e0a4837 (diff) | |
(대표님) 20250613 16시 08분 b-rfq, document 등
Diffstat (limited to 'lib/vendor-document-list/ship/enhanced-doc-table-columns.tsx')
| -rw-r--r-- | lib/vendor-document-list/ship/enhanced-doc-table-columns.tsx | 427 |
1 files changed, 427 insertions, 0 deletions
diff --git a/lib/vendor-document-list/ship/enhanced-doc-table-columns.tsx b/lib/vendor-document-list/ship/enhanced-doc-table-columns.tsx new file mode 100644 index 00000000..b80c0869 --- /dev/null +++ b/lib/vendor-document-list/ship/enhanced-doc-table-columns.tsx @@ -0,0 +1,427 @@ +// 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 { Badge } from "@/components/ui/badge" +import { + Ellipsis, + Calendar, + CalendarClock, + User, + FileText, + Eye, + Edit, + Trash2, + Building, + Code, + Settings +} from "lucide-react" +import { cn } from "@/lib/utils" +import { SimplifiedDocumentsView } from "@/db/schema" + +interface GetColumnsProps { + setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<SimplifiedDocumentsView> | null>> +} + +// 유틸리티 함수들 +const getDrawingKindText = (drawingKind: string) => { + switch (drawingKind) { + case 'B3': return 'B3 도면' + case 'B4': return 'B4 도면' + case 'B5': return 'B5 도면' + default: return drawingKind + } +} + +const getDrawingKindColor = (drawingKind: string) => { + switch (drawingKind) { + case 'B3': return 'bg-blue-100 text-blue-800' + case 'B4': return 'bg-green-100 text-green-800' + case 'B5': return 'bg-purple-100 text-purple-800' + default: return 'bg-gray-100 text-gray-800' + } +} + +// 스테이지별 이름 표시 컴포넌트 +const StageNameDisplay = ({ + stageName, + drawingKind, + isFirst = true +}: { + stageName: string | null, + drawingKind: string | null, + isFirst?: boolean +}) => { + if (!stageName) return <span className="text-gray-400">-</span> + + const stageType = isFirst ? "1차" : "2차" + const getExpectedStage = () => { + if (drawingKind === 'B4') return isFirst ? 'Pre' : 'Work' + if (drawingKind === 'B3') return isFirst ? 'Approval' : 'Work' + if (drawingKind === 'B5') return isFirst ? 'First' : 'Second' + return '' + } + + return ( + <div className="flex flex-col gap-1"> + <div className="text-xs text-gray-500">{stageType} 스테이지</div> + <div className="text-sm font-medium">{stageName}</div> + {getExpectedStage() && ( + <div className="text-xs text-gray-400">({getExpectedStage()})</div> + )} + </div> + ) +} + +// 날짜 정보 표시 컴포넌트 +const StageDateInfo = ({ + planDate, + actualDate, + stageName +}: { + planDate: string | null + actualDate: string | null + stageName: string | null +}) => { + if (!planDate && !actualDate) { + return <span className="text-gray-400">날짜 미설정</span> + } + + const isCompleted = !!actualDate + const isLate = actualDate && planDate && new Date(actualDate) > new Date(planDate) + + return ( + <div className="flex flex-col gap-1"> + {planDate && ( + <div className="text-sm"> + <span className="text-gray-500">계획: </span> + <span>{formatDate(planDate)}</span> + </div> + )} + {actualDate && ( + <div className="text-sm"> + <span className="text-gray-500">실제: </span> + <span className={cn( + isLate ? "text-red-600 font-medium" : "text-green-600 font-medium" + )}> + {formatDate(actualDate)} + </span> + </div> + )} + {!actualDate && planDate && ( + <div className="text-xs text-orange-600"> + 진행중 + </div> + )} + {isCompleted && ( + <div className="text-xs text-green-600"> + ✓ 완료 + </div> + )} + </div> + ) +} + +export function getSimplifiedDocumentColumns({ + setRowAction, +}: GetColumnsProps): ColumnDef<SimplifiedDocumentsView>[] { + + // 기본 컬럼들 + const baseColumns: ColumnDef<SimplifiedDocumentsView>[] = [ + // 체크박스 선택 + { + id: "select", + header: ({ table }) => ( + <Checkbox + checked={ + table.getIsAllPageRowsSelected() || + (table.getIsSomePageRowsSelected() && "indeterminate") + } + onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)} + aria-label="Select all" + className="translate-y-0.5" + /> + ), + cell: ({ row }) => ( + <Checkbox + checked={row.getIsSelected()} + onCheckedChange={(value) => row.toggleSelected(!!value)} + aria-label="Select row" + className="translate-y-0.5" + /> + ), + size: 40, + enableSorting: false, + enableHiding: false, + }, + + // 문서번호 + Drawing Kind + { + accessorKey: "docNumber", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="문서번호" /> + ), + cell: ({ row }) => { + const doc = row.original + return ( + <div className="flex flex-col gap-1 items-start"> + <span className="font-mono text-sm font-medium">{doc.docNumber}</span> + {doc.vendorDocNumber && ( + <span className="font-mono text-xs text-gray-500"> + 벤더: {doc.vendorDocNumber} + </span> + )} + {doc.drawingKind && ( + <Badge + variant="outline" + className={cn("text-xs", getDrawingKindColor(doc.drawingKind))} + > + {getDrawingKindText(doc.drawingKind)} + </Badge> + )} + </div> + ) + }, + size: 140, + enableResizing: true, + meta: { + excelHeader: "문서번호" + }, + }, + + // 문서명 + 프로젝트/벤더 정보 + { + accessorKey: "title", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="문서명" /> + ), + cell: ({ row }) => { + const doc = row.original + return ( + <div className="min-w-0 flex-1"> + <div className="font-medium text-gray-900 truncate" title={doc.title}> + {doc.title} + </div> + <div className="flex items-center gap-2 text-sm text-gray-500 mt-1"> + {doc.pic && ( + <span className="text-xs bg-gray-100 px-2 py-0.5 rounded"> + PIC: {doc.pic} + </span> + )} + {doc.projectCode && ( + <div className="flex items-center gap-1"> + <Building className="w-3 h-3" /> + <span>{doc.projectCode}</span> + </div> + )} + {doc.vendorName && ( + <div className="flex items-center gap-1"> + <Code className="w-3 h-3" /> + <span className="truncate max-w-[100px]">{doc.vendorName}</span> + </div> + )} + </div> + </div> + ) + }, + size: 200, + enableResizing: true, + meta: { + excelHeader: "문서명" + }, + }, + + // 첫 번째 스테이지 정보 + { + accessorKey: "firstStageName", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="1차 스테이지" /> + ), + cell: ({ row }) => { + const doc = row.original + return ( + <StageNameDisplay + stageName={doc.firstStageName} + drawingKind={doc.drawingKind} + isFirst={true} + /> + ) + }, + size: 130, + enableResizing: true, + meta: { + excelHeader: "1차 스테이지" + }, + }, + + // 첫 번째 스테이지 날짜 + { + accessorKey: "firstStagePlanDate", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="1차 일정" /> + ), + cell: ({ row }) => { + const doc = row.original + return ( + <StageDateInfo + planDate={doc.firstStagePlanDate} + actualDate={doc.firstStageActualDate} + stageName={doc.firstStageName} + /> + ) + }, + size: 140, + enableResizing: true, + meta: { + excelHeader: "1차 일정" + }, + }, + + // 두 번째 스테이지 정보 + { + accessorKey: "secondStageName", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="2차 스테이지" /> + ), + cell: ({ row }) => { + const doc = row.original + return ( + <StageNameDisplay + stageName={doc.secondStageName} + drawingKind={doc.drawingKind} + isFirst={false} + /> + ) + }, + size: 130, + enableResizing: true, + meta: { + excelHeader: "2차 스테이지" + }, + }, + + // 두 번째 스테이지 날짜 + { + accessorKey: "secondStagePlanDate", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="2차 일정" /> + ), + cell: ({ row }) => { + const doc = row.original + return ( + <StageDateInfo + planDate={doc.secondStagePlanDate} + actualDate={doc.secondStageActualDate} + stageName={doc.secondStageName} + /> + ) + }, + size: 140, + enableResizing: true, + meta: { + excelHeader: "2차 일정" + }, + }, + + // 첨부파일 수 + { + accessorKey: "attachmentCount", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="첨부파일" /> + ), + cell: ({ row }) => { + const count = row.original.attachmentCount || 0 + return ( + <div className="flex items-center gap-1"> + <FileText className="w-4 h-4 text-gray-400" /> + <span className="text-sm font-medium">{count}</span> + </div> + ) + }, + size: 80, + enableResizing: true, + meta: { + excelHeader: "첨부파일" + }, + }, + + // 업데이트 일시 + { + accessorKey: "updatedAt", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="업데이트" /> + ), + cell: ({ cell }) => ( + <span className="text-sm text-gray-600"> + {formatDateTime(cell.getValue() as Date)} + </span> + ), + size: 140, + enableResizing: true, + meta: { + excelHeader: "업데이트" + }, + }, + + // 액션 버튼 + { + 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 baseColumns +}
\ No newline at end of file |
