summaryrefslogtreecommitdiff
path: root/lib/vendor-document-list/ship/enhanced-doc-table-columns.tsx
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-06-13 07:08:01 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-06-13 07:08:01 +0000
commitc72d0897f7b37843109c86f61d97eba05ba3ca0d (patch)
tree887dd877f3f8beafa92b4d9a7b16c84b4a5795d8 /lib/vendor-document-list/ship/enhanced-doc-table-columns.tsx
parentff902243a658067fae858a615c0629aa2e0a4837 (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.tsx427
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