diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-04-28 02:13:30 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-04-28 02:13:30 +0000 |
| commit | ef4c533ebacc2cdc97e518f30e9a9350004fcdfb (patch) | |
| tree | 345251a3ed0f4429716fa5edaa31024d8f4cb560 /lib/basic-contract/template/basic-contract-template-columns.tsx | |
| parent | 9ceed79cf32c896f8a998399bf1b296506b2cd4a (diff) | |
~20250428 작업사항
Diffstat (limited to 'lib/basic-contract/template/basic-contract-template-columns.tsx')
| -rw-r--r-- | lib/basic-contract/template/basic-contract-template-columns.tsx | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/lib/basic-contract/template/basic-contract-template-columns.tsx b/lib/basic-contract/template/basic-contract-template-columns.tsx new file mode 100644 index 00000000..b0486fe4 --- /dev/null +++ b/lib/basic-contract/template/basic-contract-template-columns.tsx @@ -0,0 +1,245 @@ +"use client"
+
+import * as React from "react"
+import { type DataTableRowAction } from "@/types/table"
+import { type ColumnDef } from "@tanstack/react-table"
+import { Download, Ellipsis, Paperclip } from "lucide-react"
+import { toast } from "sonner"
+
+import { getErrorMessage } from "@/lib/handle-error"
+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,
+ DropdownMenuRadioGroup,
+ DropdownMenuRadioItem,
+ DropdownMenuSeparator,
+ DropdownMenuShortcut,
+ DropdownMenuSub,
+ DropdownMenuSubContent,
+ DropdownMenuSubTrigger,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu"
+
+import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header"
+import { basicContractTemplateColumnsConfig } from "@/config/basicContractColumnsConfig"
+import { BasicContractTemplate } from "@/db/schema"
+
+interface GetColumnsProps {
+ setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<BasicContractTemplate> | null>>
+}
+
+/**
+ * 파일 다운로드 함수
+ */
+const handleFileDownload = (filePath: string, fileName: string) => {
+ try {
+ // 전체 URL 생성
+ const fullUrl = `${window.location.origin}${filePath}`;
+
+ // a 태그를 생성하여 다운로드 실행
+ const link = document.createElement('a');
+ link.href = fullUrl;
+ link.download = fileName; // 다운로드될 파일명 설정
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+
+ toast.success("파일 다운로드를 시작합니다.");
+ } catch (error) {
+ console.error("파일 다운로드 오류:", error);
+ toast.error("파일 다운로드 중 오류가 발생했습니다.");
+ }
+};
+
+/**
+ * tanstack table 컬럼 정의 (중첩 헤더 버전)
+ */
+export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<BasicContractTemplate>[] {
+ // ----------------------------------------------------------------
+ // 1) select 컬럼 (체크박스)
+ // ----------------------------------------------------------------
+ const selectColumn: ColumnDef<BasicContractTemplate> = {
+ 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"
+ />
+ ),
+ maxSize: 30,
+ enableSorting: false,
+ enableHiding: false,
+ }
+
+ // ----------------------------------------------------------------
+ // 2) 파일 다운로드 컬럼 (아이콘)
+ // ----------------------------------------------------------------
+ const downloadColumn: ColumnDef<BasicContractTemplate> = {
+ id: "download",
+ header: "",
+ cell: ({ row }) => {
+ const template = row.original;
+
+ return (
+ <Button
+ variant="ghost"
+ size="icon"
+ onClick={() => handleFileDownload(template.filePath, template.fileName)}
+ title={`${template.fileName} 다운로드`}
+ className="hover:bg-muted"
+ >
+ <Paperclip className="h-4 w-4" />
+ <span className="sr-only">다운로드</span>
+ </Button>
+ );
+ },
+ maxSize: 30,
+ enableSorting: false,
+ }
+
+ // ----------------------------------------------------------------
+ // 3) actions 컬럼 (Dropdown 메뉴)
+ // ----------------------------------------------------------------
+ const actionsColumn: ColumnDef<BasicContractTemplate> = {
+ id: "actions",
+ enableHiding: false,
+ cell: function Cell({ row }) {
+ const [isUpdatePending, startUpdateTransition] = React.useTransition()
+
+ 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-40">
+ <DropdownMenuItem
+ onSelect={() => setRowAction({ row, type: "update" })}
+ >
+ Edit
+ </DropdownMenuItem>
+
+ <DropdownMenuSeparator />
+ <DropdownMenuItem
+ onSelect={() => setRowAction({ row, type: "delete" })}
+ >
+ Delete
+ <DropdownMenuShortcut>⌘⌫</DropdownMenuShortcut>
+ </DropdownMenuItem>
+ </DropdownMenuContent>
+ </DropdownMenu>
+ )
+ },
+ maxSize: 30,
+ }
+
+ // ----------------------------------------------------------------
+ // 4) 일반 컬럼들을 "그룹"별로 묶어 중첩 columns 생성
+ // ----------------------------------------------------------------
+ // 4-1) groupMap: { [groupName]: ColumnDef<BasicContractTemplate>[] }
+ const groupMap: Record<string, ColumnDef<BasicContractTemplate>[]> = {}
+
+ basicContractTemplateColumnsConfig.forEach((cfg) => {
+ // 만약 group가 없으면 "_noGroup" 처리
+ const groupName = cfg.group || "_noGroup"
+
+ if (!groupMap[groupName]) {
+ groupMap[groupName] = []
+ }
+
+ // child column 정의
+ const childCol: ColumnDef<BasicContractTemplate> = {
+ accessorKey: cfg.id,
+ enableResizing: true,
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title={cfg.label} />
+ ),
+ meta: {
+ excelHeader: cfg.excelHeader,
+ group: cfg.group,
+ type: cfg.type,
+ },
+ cell: ({ row, cell }) => {
+ // 날짜 형식 처리
+ if (cfg.id === "createdAt" || cfg.id === "updatedAt") {
+ const dateVal = cell.getValue() as Date
+ return formatDateTime(dateVal)
+ }
+
+ // Status 컬럼에 Badge 적용
+ if (cfg.id === "status") {
+ const status = row.getValue(cfg.id) as string
+ const isActive = status === "ACTIVE"
+
+ return (
+ <Badge
+ variant={isActive ? "default" : "secondary"}
+ >
+ {isActive ? "활성" : "비활성"}
+ </Badge>
+ )
+ }
+
+ // 나머지 컬럼은 그대로 값 표시
+ return row.getValue(cfg.id) ?? ""
+ },
+ minSize:80
+ }
+
+ groupMap[groupName].push(childCol)
+ })
+
+ // ----------------------------------------------------------------
+ // 4-2) groupMap에서 실제 상위 컬럼(그룹)을 만들기
+ // ----------------------------------------------------------------
+ const nestedColumns: ColumnDef<BasicContractTemplate>[] = []
+
+ // 순서를 고정하고 싶다면 group 순서를 미리 정의하거나 sort해야 함
+ // 여기서는 그냥 Object.entries 순서
+ Object.entries(groupMap).forEach(([groupName, colDefs]) => {
+ if (groupName === "_noGroup") {
+ // 그룹 없음 → 그냥 최상위 레벨 컬럼
+ nestedColumns.push(...colDefs)
+ } else {
+ // 상위 컬럼
+ nestedColumns.push({
+ id: groupName,
+ header: groupName, // "Basic Info", "Metadata" 등
+ columns: colDefs,
+ })
+ }
+ })
+
+ // ----------------------------------------------------------------
+ // 5) 최종 컬럼 배열: select, download, nestedColumns, actions
+ // ----------------------------------------------------------------
+ return [
+ selectColumn,
+ downloadColumn, // 다운로드 컬럼 추가
+ ...nestedColumns,
+ actionsColumn,
+ ]
+}
\ No newline at end of file |
