diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-24 11:06:32 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-24 11:06:32 +0000 |
| commit | 1dc24d48e52f2e490f5603ceb02842586ecae533 (patch) | |
| tree | 8fca2c5b5b52cc10557b5ba6e55b937ae3c57cf6 /lib/gtc-contract/status/gtc-documents-table-columns.tsx | |
| parent | ed0d6fcc98f671280c2ccde797b50693da88152e (diff) | |
(대표님) 정기평가 피드백 반영, 설계 피드백 반영, (최겸) 기술영업 피드백 반영
Diffstat (limited to 'lib/gtc-contract/status/gtc-documents-table-columns.tsx')
| -rw-r--r-- | lib/gtc-contract/status/gtc-documents-table-columns.tsx | 291 |
1 files changed, 291 insertions, 0 deletions
diff --git a/lib/gtc-contract/status/gtc-documents-table-columns.tsx b/lib/gtc-contract/status/gtc-documents-table-columns.tsx new file mode 100644 index 00000000..2d5f08b9 --- /dev/null +++ b/lib/gtc-contract/status/gtc-documents-table-columns.tsx @@ -0,0 +1,291 @@ +"use client" + +import * as React from "react" +import { type DataTableRowAction } from "@/types/table" +import { type ColumnDef } from "@tanstack/react-table" +import { Ellipsis, Eye } from "lucide-react" + +import { formatDate, formatDateTime } from "@/lib/utils" +import { Badge } from "@/components/ui/badge" +import { Button } from "@/components/ui/button" +import { Checkbox } from "@/components/ui/checkbox" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" + +import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header" +import { type GtcDocumentWithRelations } from "@/db/schema/gtc" +import { useRouter } from "next/navigation" + +interface GetColumnsProps { + setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<GtcDocumentWithRelations> | null>> +} + +/** + * GTC Documents 테이블 컬럼 정의 + */ +export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<GtcDocumentWithRelations>[] { + const router = useRouter() + + // ---------------------------------------------------------------- + // 1) select 컬럼 (체크박스) + // ---------------------------------------------------------------- + const selectColumn: ColumnDef<GtcDocumentWithRelations> = { + 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, + } + + // ---------------------------------------------------------------- + // 2) 기본 정보 그룹 + // ---------------------------------------------------------------- + const basicInfoColumns: ColumnDef<GtcDocumentWithRelations>[] = [ + { + accessorKey: "type", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="구분" />, + cell: ({ row }) => { + const type = row.getValue("type") as string; + return ( + <Badge variant={type === "standard" ? "default" : "secondary"}> + {type === "standard" ? "표준" : "프로젝트"} + </Badge> + ); + }, + size: 100, + enableResizing: true, + meta: { + excelHeader: "구분", + }, + }, + { + accessorKey: "project", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="프로젝트" />, + cell: ({ row }) => { + const project = row.original.project; + if (!project) { + return <span className="text-muted-foreground">-</span>; + } + return ( + <div className="flex flex-col min-w-0"> + <span className="font-medium truncate">{project.name}</span> + <span className="text-xs text-muted-foreground">{project.code}</span> + </div> + ); + }, + size: 200, + enableResizing: true, + meta: { + excelHeader: "프로젝트", + }, + }, + { + accessorKey: "revision", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="Rev." />, + cell: ({ row }) => { + const revision = row.getValue("revision") as number; + return <span className="font-mono text-sm">v{revision}</span>; + }, + size: 80, + enableResizing: true, + meta: { + excelHeader: "Rev.", + }, + }, + ]; + + // ---------------------------------------------------------------- + // 3) 등록/수정 정보 그룹 + // ---------------------------------------------------------------- + const auditColumns: ColumnDef<GtcDocumentWithRelations>[] = [ + { + accessorKey: "createdAt", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="최초등록일" />, + cell: ({ row }) => { + const date = row.getValue("createdAt") as Date; + return date ? formatDate(date, "KR") : "-"; + }, + size: 120, + enableResizing: true, + meta: { + excelHeader: "최초등록일", + }, + }, + { + accessorKey: "createdBy", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="최초등록자" />, + cell: ({ row }) => { + const createdBy = row.original.createdBy; + return createdBy ? ( + <span className="text-sm">{createdBy.name}</span> + ) : ( + <span className="text-muted-foreground">-</span> + ); + }, + size: 120, + enableResizing: true, + meta: { + excelHeader: "최초등록자", + }, + }, + { + accessorKey: "updatedAt", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="최종수정일" />, + cell: ({ row }) => { + const date = row.getValue("updatedAt") as Date; + return date ? formatDate(date, "KR") : "-"; + }, + size: 120, + enableResizing: true, + meta: { + excelHeader: "최종수정일", + }, + }, + { + accessorKey: "updatedBy", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="최종수정자" />, + cell: ({ row }) => { + const updatedBy = row.original.updatedBy; + return updatedBy ? ( + <span className="text-sm">{updatedBy.name}</span> + ) : ( + <span className="text-muted-foreground">-</span> + ); + }, + size: 120, + enableResizing: true, + meta: { + excelHeader: "최종수정자", + }, + }, + { + accessorKey: "editReason", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="최종 편집사유" />, + cell: ({ row }) => { + const reason = row.getValue("editReason") as string; + return reason ? ( + <span className="text-sm" title={reason}> + {reason.length > 30 ? `${reason.substring(0, 30)}...` : reason} + </span> + ) : ( + <span className="text-muted-foreground">-</span> + ); + }, + size: 200, + enableResizing: true, + meta: { + excelHeader: "최종 편집사유", + }, + }, + ]; + + // ---------------------------------------------------------------- + // 4) actions 컬럼 (Dropdown 메뉴) + // ---------------------------------------------------------------- + const actionsColumn: ColumnDef<GtcDocumentWithRelations> = { + id: "actions", + enableHiding: false, + cell: function Cell({ row }) { + const [isUpdatePending, startUpdateTransition] = React.useTransition() + const gtcDocument = row.original; + + const handleViewDetails = () => { + router.push(`/evcp/gtc-documents/${gtcDocument.id}`); + }; + + const handleCreateNewRevision = () => { + setRowAction({ row, type: "createRevision" }); + }; + + return ( + <DropdownMenu> + <DropdownMenuTrigger asChild> + <Button + aria-label="Open menu" + variant="ghost" + className="flex size-8 p-0 data-[state=open]:bg-muted" + > + <Ellipsis className="size-4" aria-hidden="true" /> + </Button> + </DropdownMenuTrigger> + <DropdownMenuContent align="end" className="w-48"> + <DropdownMenuItem onSelect={handleViewDetails}> + <Eye className="mr-2 h-4 w-4" /> + View Details + </DropdownMenuItem> + + <DropdownMenuSeparator /> + + <DropdownMenuItem + onSelect={() => setRowAction({ row, type: "update" })} + > + Edit + </DropdownMenuItem> + + <DropdownMenuItem onSelect={handleCreateNewRevision}> + Create New Revision + </DropdownMenuItem> + + <DropdownMenuSeparator /> + <DropdownMenuItem + onSelect={() => setRowAction({ row, type: "delete" })} + > + Delete + <DropdownMenuShortcut>⌘⌫</DropdownMenuShortcut> + </DropdownMenuItem> + </DropdownMenuContent> + </DropdownMenu> + ) + }, + size: 40, + } + + // ---------------------------------------------------------------- + // 5) 중첩 컬럼 그룹 생성 + // ---------------------------------------------------------------- + const nestedColumns: ColumnDef<GtcDocumentWithRelations>[] = [ + { + id: "기본 정보", + header: "기본 정보", + columns: basicInfoColumns, + }, + { + id: "등록/수정 정보", + header: "등록/수정 정보", + columns: auditColumns, + }, + ] + + // ---------------------------------------------------------------- + // 6) 최종 컬럼 배열 + // ---------------------------------------------------------------- + return [ + selectColumn, + ...nestedColumns, + actionsColumn, + ] +}
\ No newline at end of file |
