diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-21 07:19:52 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-21 07:19:52 +0000 |
| commit | 9da494b0e3bbe7b513521d0915510fe9ee376b8b (patch) | |
| tree | f936f69626bf2808ac409ce7cad97433465b3672 /lib/email-template/table/template-table-columns.tsx | |
| parent | e275618ff8a1ce6977d3e2567d943edb941897f9 (diff) | |
(대표님, 최겸) 작업사항 - 이메일 템플릿, 메일링, 기술영업 요구사항 반영
Diffstat (limited to 'lib/email-template/table/template-table-columns.tsx')
| -rw-r--r-- | lib/email-template/table/template-table-columns.tsx | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/lib/email-template/table/template-table-columns.tsx b/lib/email-template/table/template-table-columns.tsx new file mode 100644 index 00000000..d20739cc --- /dev/null +++ b/lib/email-template/table/template-table-columns.tsx @@ -0,0 +1,296 @@ +"use client" + +import * as React from "react" +import { type ColumnDef } from "@tanstack/react-table" +import { ArrowUpDown, Copy, MoreHorizontal, Edit, Trash, Eye } from "lucide-react" +import Link from "next/link" + +import { Badge } from "@/components/ui/badge" +import { Button } from "@/components/ui/button" +import { Checkbox } from "@/components/ui/checkbox" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" +import { toast } from "sonner" +import { formatDate } from "@/lib/utils" +import { type TemplateListView } from "@/db/schema" +import { type DataTableRowAction } from "@/types/table" +import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header" +import { getCategoryDisplayName, getCategoryVariant } from "../validations" + +interface GetColumnsProps { + setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<TemplateListView> | null>> +} + +export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<TemplateListView>[] { + return [ + // 체크박스 컬럼 + { + 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" + /> + ), + enableSorting: false, + enableHiding: false, + }, + + // 템플릿 이름 컬럼 (클릭 시 세부 페이지로) + { + accessorKey: "name", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="템플릿 이름" /> + ), + cell: ({ row }) => { + const template = row.original + return ( + <div className="flex flex-col gap-1"> + <Link + href={`/evcp/email-template/${template.slug}`} + className="font-medium text-blue-600 hover:text-blue-800 hover:underline" + > + {template.name} + </Link> + {template.description && ( + <div className="text-xs text-muted-foreground line-clamp-2"> + {template.description} + </div> + )} + </div> + ) + }, + enableSorting: true, + enableHiding: false, + size:200 + }, + + { + accessorKey: "subject", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="이메일 제목" /> + ), + cell: ({ getValue }) => { + const subject = getValue() as string + return ( + <div className="text-sm text-muted-foreground"> + {subject} + </div> + ) + }, + enableSorting: true, + size:250 + }, + + // Slug 컬럼 + { + accessorKey: "slug", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="Slug" /> + ), + cell: ({ getValue }) => { + const slug = getValue() as string + return ( + <div className="font-mono text-sm text-muted-foreground"> + {slug} + </div> + ) + }, + enableSorting: true, + size:120 + + }, + + // 카테고리 컬럼 + { + accessorKey: "category", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="카테고리" /> + ), + cell: ({ row }) => { + const category = row.original.category + const displayName = getCategoryDisplayName(category) + const variant = getCategoryVariant(category) + + return ( + <Badge variant={variant}> + {displayName} + </Badge> + ) + }, + enableSorting: true, + filterFn: (row, id, value) => { + return value.includes(row.getValue(id)) + }, + size:120 + + }, + + // 변수 개수 컬럼 + { + accessorKey: "variableCount", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="변수" /> + ), + cell: ({ row }) => { + const variableCount = row.original.variableCount + const requiredCount = row.original.requiredVariableCount + + return ( + <div className="text-center"> + <div className="text-sm font-medium">{variableCount}</div> + {requiredCount > 0 && ( + <div className="text-xs text-muted-foreground"> + 필수: {requiredCount} + </div> + )} + </div> + ) + }, + enableSorting: true, + size:80 + + }, + + // 버전 컬럼 + { + accessorKey: "version", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="버전" /> + ), + cell: ({ getValue }) => { + const version = getValue() as number + return ( + <div className="text-center"> + <Badge variant="outline" className="font-mono text-xs"> + v{version} + </Badge> + </div> + ) + }, + enableSorting: true, + size:80 + + }, + + // 생성일 컬럼 + { + accessorKey: "createdAt", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="생성일" /> + ), + cell: ({ cell }) => { + const date = cell.getValue() as Date + return ( + <div className="text-sm text-muted-foreground"> + {formatDate(date)} + </div> + ) + }, + enableSorting: true, + size:200 + + }, + + // 수정일 컬럼 + { + accessorKey: "updatedAt", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="수정일" /> + ), + cell: ({ cell }) => { + const date = cell.getValue() as Date + return ( + <div className="text-sm text-muted-foreground"> + {formatDate(date)} + </div> + ) + }, + enableSorting: true, + size:200 + + }, + + // Actions 컬럼 + { + id: "actions", + cell: ({ row }) => { + const template = row.original + + return ( + <DropdownMenu> + <DropdownMenuTrigger asChild> + <Button + aria-label="Open menu" + variant="ghost" + className="flex size-8 p-0 data-[state=open]:bg-muted" + > + <MoreHorizontal className="size-4" aria-hidden="true" /> + </Button> + </DropdownMenuTrigger> + <DropdownMenuContent align="end" className="w-40"> + <DropdownMenuItem asChild> + <Link href={`/evcp/templates/${template.slug}`}> + <Eye className="mr-2 size-4" aria-hidden="true" /> + 보기 + </Link> + </DropdownMenuItem> + + <DropdownMenuItem + onClick={() => { + setRowAction({ type: "update", row }) + }} + > + <Edit className="mr-2 size-4" aria-hidden="true" /> + 수정 + </DropdownMenuItem> + + <DropdownMenuItem + onClick={() => { + setRowAction({ type: "duplicate", row }) + }} + > + <Copy className="mr-2 size-4" aria-hidden="true" /> + 복제 + </DropdownMenuItem> + + <DropdownMenuSeparator /> + + <DropdownMenuItem + onClick={() => { + setRowAction({ type: "delete", row }) + }} + className="text-destructive focus:text-destructive" + > + <Trash className="mr-2 size-4" aria-hidden="true" /> + 삭제 + </DropdownMenuItem> + </DropdownMenuContent> + </DropdownMenu> + ) + }, + enableSorting: false, + enableHiding: false, + size:80 + + }, + ] +}
\ No newline at end of file |
