summaryrefslogtreecommitdiff
path: root/lib/avl/table/avl-table-columns.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/avl/table/avl-table-columns.tsx')
-rw-r--r--lib/avl/table/avl-table-columns.tsx351
1 files changed, 351 insertions, 0 deletions
diff --git a/lib/avl/table/avl-table-columns.tsx b/lib/avl/table/avl-table-columns.tsx
new file mode 100644
index 00000000..77361f36
--- /dev/null
+++ b/lib/avl/table/avl-table-columns.tsx
@@ -0,0 +1,351 @@
+import { Checkbox } from "@/components/ui/checkbox"
+import { Button } from "@/components/ui/button"
+import { Badge } from "@/components/ui/badge"
+import { Eye, Edit, Trash2 } from "lucide-react"
+import { type ColumnDef, TableMeta } from "@tanstack/react-table"
+import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header"
+import { EditableCell } from "@/components/data-table/editable-cell"
+import { AvlListItem } from "../types"
+
+interface GetColumnsProps {
+ selectedRows?: number[]
+ onRowSelect?: (id: number, selected: boolean) => void
+}
+
+// 수정 여부 확인 헬퍼 함수
+const getIsModified = (table: any, rowId: string, fieldName: string) => {
+ const pendingChanges = table.options.meta?.getPendingChanges?.() || {}
+ return String(rowId) in pendingChanges && fieldName in pendingChanges[String(rowId)]
+}
+
+// 테이블 메타 타입 확장
+declare module "@tanstack/react-table" {
+ interface TableMeta<TData> {
+ onCellUpdate?: (id: string, field: keyof TData, newValue: any) => Promise<void>
+ onCellCancel?: (id: string, field: keyof TData) => void
+ onAction?: (action: string, data?: any) => void
+ onSaveEmptyRow?: (tempId: string) => Promise<void>
+ onCancelEmptyRow?: (tempId: string) => void
+ isEmptyRow?: (id: string) => boolean
+ }
+}
+
+// 테이블 컬럼 정의 함수
+export function getColumns({ selectedRows = [], onRowSelect }: GetColumnsProps): ColumnDef<AvlListItem>[] {
+ const columns: ColumnDef<AvlListItem>[] = [
+ // 기본 정보 그룹
+ {
+ header: "기본 정보",
+ columns: [
+ {
+ id: "select",
+ header: () => <div className="text-center">선택</div>,
+ cell: ({ row }) => (
+ <div className="flex justify-center">
+ <Checkbox
+ checked={selectedRows.includes(row.original.id)}
+ onCheckedChange={(checked) => {
+ onRowSelect?.(row.original.id, !!checked)
+ }}
+ aria-label="행 선택"
+ className="translate-y-[2px]"
+ />
+ </div>
+ ),
+ enableSorting: false,
+ enableHiding: false,
+ size: 40,
+ },
+ {
+ accessorKey: "no",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="No" />
+ ),
+ cell: ({ getValue }) => <div className="text-center">{getValue() as number}</div>,
+ size: 60,
+ },
+ {
+ accessorKey: "isTemplate",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="AVL 분류" />
+ ),
+ cell: ({ getValue, row, table }) => {
+ const value = getValue() as boolean
+ const isModified = getIsModified(table, row.id, "isTemplate")
+ return (
+ <EditableCell
+ value={value ? "표준 AVL" : "프로젝트 AVL"}
+ isModified={isModified}
+ type="select"
+ options={[
+ { value: false, label: "프로젝트 AVL" },
+ { value: true, label: "표준 AVL" },
+ ]}
+ onUpdate={(newValue) => {
+ table.options.meta?.onCellUpdate?.(row.id, "isTemplate", newValue === "true")
+ }}
+ onCancel={() => {
+ table.options.meta?.onCellCancel?.(row.id, "isTemplate")
+ }}
+ />
+ )
+ },
+ size: 120,
+ },
+ {
+ accessorKey: "constructionSector",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="공사부문" />
+ ),
+ cell: ({ getValue, row, table }) => {
+ const value = getValue() as string
+ const isModified = getIsModified(table, row.id, "constructionSector")
+ return (
+ <EditableCell
+ value={value}
+ isModified={isModified}
+ type="select"
+ options={[
+ { value: "조선", label: "조선" },
+ { value: "해양", label: "해양" },
+ ]}
+ onUpdate={(newValue) => {
+ table.options.meta?.onCellUpdate?.(row.id, "constructionSector", newValue)
+ }}
+ onCancel={() => {
+ table.options.meta?.onCellCancel?.(row.id, "constructionSector")
+ }}
+ />
+ )
+ },
+ size: 100,
+ },
+ {
+ accessorKey: "projectCode",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="프로젝트 코드" />
+ ),
+ cell: ({ getValue, row, table }) => {
+ const value = getValue() as string
+ const isModified = getIsModified(table, row.id, "projectCode")
+ return (
+ <EditableCell
+ value={value}
+ isModified={isModified}
+ onUpdate={(newValue) => {
+ table.options.meta?.onCellUpdate?.(row.id, "projectCode", newValue)
+ }}
+ onCancel={() => {
+ table.options.meta?.onCellCancel?.(row.id, "projectCode")
+ }}
+ />
+ )
+ },
+ size: 140,
+ },
+ {
+ accessorKey: "shipType",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="선종" />
+ ),
+ cell: ({ getValue, row, table }) => {
+ const value = getValue() as string
+ const isModified = getIsModified(table, row.id, "shipType")
+ return (
+ <EditableCell
+ value={value}
+ isModified={isModified}
+ onUpdate={(newValue) => {
+ table.options.meta?.onCellUpdate?.(row.id, "shipType", newValue)
+ }}
+ onCancel={() => {
+ table.options.meta?.onCellCancel?.(row.id, "shipType")
+ }}
+ />
+ )
+ },
+ size: 100,
+ },
+ {
+ accessorKey: "avlKind",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="AVL 종류" />
+ ),
+ cell: ({ getValue, row, table }) => {
+ const value = getValue() as string
+ const isModified = getIsModified(table, row.id, "avlKind")
+ return (
+ <EditableCell
+ value={value}
+ isModified={isModified}
+ onUpdate={(newValue) => {
+ table.options.meta?.onCellUpdate?.(row.id, "avlKind", newValue)
+ }}
+ onCancel={() => {
+ table.options.meta?.onCellCancel?.(row.id, "avlKind")
+ }}
+ />
+ )
+ },
+ size: 120,
+ },
+ {
+ accessorKey: "htDivision",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="H/T 구분" />
+ ),
+ cell: ({ getValue, row, table }) => {
+ const value = getValue() as string
+ const isModified = getIsModified(table, row.id, "htDivision")
+ return (
+ <EditableCell
+ value={value}
+ isModified={isModified}
+ type="select"
+ options={[
+ { value: "H", label: "H" },
+ { value: "T", label: "T" },
+ ]}
+ onUpdate={(newValue) => {
+ table.options.meta?.onCellUpdate?.(row.id, "htDivision", newValue)
+ }}
+ onCancel={() => {
+ table.options.meta?.onCellCancel?.(row.id, "htDivision")
+ }}
+ />
+ )
+ },
+ size: 80,
+ },
+ {
+ accessorKey: "rev",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="Rev" />
+ ),
+ cell: ({ getValue, row, table }) => {
+ const value = getValue() as number
+ const isModified = getIsModified(table, row.id, "rev")
+ return (
+ <EditableCell
+ value={value?.toString() || ""}
+ isModified={isModified}
+ type="number"
+ onUpdate={(newValue) => {
+ table.options.meta?.onCellUpdate?.(row.id, "rev", parseInt(newValue))
+ }}
+ onCancel={() => {
+ table.options.meta?.onCellCancel?.(row.id, "rev")
+ }}
+ />
+ )
+ },
+ size: 80,
+ },
+ ],
+ },
+
+ // 등록 정보 그룹
+ {
+ header: "등록 정보",
+ columns: [
+ {
+ accessorKey: "createdAt",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="등재일" />
+ ),
+ cell: ({ getValue }) => {
+ const date = getValue() as string
+ return <div className="text-center text-sm">{date}</div>
+ },
+ size: 100,
+ },
+ {
+ accessorKey: "updatedAt",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="수정일" />
+ ),
+ cell: ({ getValue }) => {
+ const date = getValue() as string
+ return <div className="text-center text-sm">{date}</div>
+ },
+ size: 100,
+ },
+ ],
+ },
+
+ // 액션 그룹
+ {
+ id: "actions",
+ header: "액션",
+ columns: [
+ {
+ id: "actions",
+ header: () => <div className="text-center">액션</div>,
+ cell: ({ row, table }) => {
+ const isEmptyRow = table.options.meta?.isEmptyRow?.(row.id) || false
+
+ if (isEmptyRow) {
+ return (
+ <div className="flex items-center justify-center gap-1">
+ <Button
+ variant="ghost"
+ size="sm"
+ onClick={() => table.options.meta?.onSaveEmptyRow?.(row.id)}
+ className="h-8 w-8 p-0"
+ >
+ 저장
+ </Button>
+ <Button
+ variant="ghost"
+ size="sm"
+ onClick={() => table.options.meta?.onCancelEmptyRow?.(row.id)}
+ className="h-8 w-8 p-0 text-destructive hover:text-destructive"
+ >
+ 취소
+ </Button>
+ </div>
+ )
+ }
+
+ return (
+ <div className="flex items-center justify-center gap-1">
+ <Button
+ variant="ghost"
+ size="sm"
+ onClick={() => table.options.meta?.onAction?.("view-detail", { id: row.original.id })}
+ className="h-8 w-8 p-0"
+ title="상세보기"
+ >
+ <Eye className="h-4 w-4" />
+ </Button>
+ <Button
+ variant="ghost"
+ size="sm"
+ onClick={() => table.options.meta?.onAction?.("edit", { id: row.original.id })}
+ className="h-8 w-8 p-0"
+ title="수정"
+ >
+ <Edit className="h-4 w-4" />
+ </Button>
+ <Button
+ variant="ghost"
+ size="sm"
+ onClick={() => table.options.meta?.onAction?.("delete", { id: row.original.id })}
+ className="h-8 w-8 p-0 text-destructive hover:text-destructive"
+ title="삭제"
+ >
+ <Trash2 className="h-4 w-4" />
+ </Button>
+ </div>
+ )
+ },
+ enableSorting: false,
+ enableHiding: false,
+ size: 120,
+ },
+ ],
+ },
+ ]
+
+ return columns
+}