diff options
Diffstat (limited to 'lib/approval-log/table/approval-log-table-column.tsx')
| -rw-r--r-- | lib/approval-log/table/approval-log-table-column.tsx | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/lib/approval-log/table/approval-log-table-column.tsx b/lib/approval-log/table/approval-log-table-column.tsx new file mode 100644 index 00000000..8b466c69 --- /dev/null +++ b/lib/approval-log/table/approval-log-table-column.tsx @@ -0,0 +1,265 @@ +"use client" + +import * as React from "react" +import { ColumnDef } from "@tanstack/react-table" +import { Badge } from "@/components/ui/badge" +import { Button } from "@/components/ui/button" +import { Checkbox } from "@/components/ui/checkbox" +import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header" +import { type ApprovalLog } from "../service" +import { formatDate } from "@/lib/utils" +import { getApprovalStatusText } from "@/lib/knox-api/approval/approval" +import { MoreHorizontal, Eye } from "lucide-react" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" + +interface GetColumnsProps { + setRowAction: React.Dispatch<React.SetStateAction<{ + type: "view"; + row: { original: ApprovalLog }; + } | null>>; +} + +export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<ApprovalLog>[] { + return [ + { + id: "select", + header: ({ table }) => ( + <Checkbox + checked={ + table.getIsAllPageRowsSelected() || + (table.getIsSomePageRowsSelected() && "indeterminate") + } + onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)} + aria-label="Select all" + className="translate-y-[2px]" + /> + ), + cell: ({ row }) => ( + <Checkbox + checked={row.getIsSelected()} + onCheckedChange={(value) => row.toggleSelected(!!value)} + aria-label="Select row" + className="translate-y-[2px]" + /> + ), + enableSorting: false, + enableHiding: false, + }, + { + accessorKey: "apInfId", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="결재 ID" /> + ), + cell: ({ row }) => { + const apInfId = row.getValue("apInfId") as string; + return ( + <div className="flex space-x-2"> + <span className="max-w-[150px] truncate font-mono text-sm"> + {apInfId} + </span> + </div> + ) + }, + }, + { + accessorKey: "subject", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="결재 제목" /> + ), + cell: ({ row }) => { + return ( + <div className="flex space-x-2"> + <span className="max-w-[300px] truncate font-medium"> + {row.getValue("subject")} + </span> + </div> + ) + }, + }, + { + accessorKey: "status", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="상태" /> + ), + cell: ({ row }) => { + const status = row.getValue("status") as string; + const statusText = getApprovalStatusText(status); + + const getStatusVariant = (status: string) => { + switch (status) { + case '2': return 'default'; // 완결 + case '3': return 'destructive'; // 반려 + case '4': return 'destructive'; // 상신취소 + case '5': return 'default'; // 전결 + case '6': return 'default'; // 후완결 + case '1': return 'secondary'; // 진행중 + default: return 'outline'; // 기타 + } + }; + + return ( + <div className="flex space-x-2"> + <Badge variant={getStatusVariant(status)}> + {statusText} + </Badge> + </div> + ) + }, + }, + { + accessorKey: "userId", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="사용자 ID" /> + ), + cell: ({ row }) => { + return ( + <div className="flex space-x-2"> + <span className="max-w-[120px] truncate"> + {row.getValue("userId") || "-"} + </span> + </div> + ) + }, + }, + { + accessorKey: "emailAddress", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="이메일" /> + ), + cell: ({ row }) => { + return ( + <div className="flex space-x-2"> + <span className="max-w-[200px] truncate"> + {row.getValue("emailAddress")} + </span> + </div> + ) + }, + }, + { + accessorKey: "sbmDt", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="상신일시" /> + ), + cell: ({ row }) => { + const sbmDt = row.getValue("sbmDt") as string; + if (!sbmDt) return <span>-</span>; + + // YYYYMMDDHHMMSS 형식을 YYYY-MM-DD HH:MM:SS로 변환 + const formatted = sbmDt.replace( + /(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/, + '$1-$2-$3 $4:$5:$6' + ); + + return ( + <div className="flex space-x-2"> + <span className="max-w-[150px] truncate"> + {formatted} + </span> + </div> + ) + }, + }, + { + accessorKey: "urgYn", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="긴급" /> + ), + cell: ({ row }) => { + const urgYn = row.getValue("urgYn") as string; + if (urgYn === 'Y') { + return ( + <div className="flex space-x-2"> + <Badge variant="destructive">긴급</Badge> + </div> + ); + } + return <span>-</span>; + }, + }, + { + accessorKey: "docSecuType", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="보안등급" /> + ), + cell: ({ row }) => { + const docSecuType = row.getValue("docSecuType") as string; + const getSecurityVariant = (type: string) => { + switch (type) { + case 'CONFIDENTIAL_STRICT': return 'destructive'; + case 'CONFIDENTIAL': return 'secondary'; + default: return 'outline'; + } + }; + + const getSecurityText = (type: string) => { + switch (type) { + case 'CONFIDENTIAL_STRICT': return '극비'; + case 'CONFIDENTIAL': return '기밀'; + case 'PERSONAL': return '개인'; + default: return type || '개인'; + } + }; + + return ( + <div className="flex space-x-2"> + <Badge variant={getSecurityVariant(docSecuType)}> + {getSecurityText(docSecuType)} + </Badge> + </div> + ) + }, + }, + { + accessorKey: "createdAt", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="생성일" /> + ), + cell: ({ row }) => { + return ( + <div className="flex space-x-2"> + <span className="max-w-[150px] truncate"> + {formatDate(row.getValue("createdAt"))} + </span> + </div> + ) + }, + }, + { + id: "actions", + cell: ({ row }) => { + 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 + onClick={() => { + setRowAction({ type: "view", row }); + }} + > + <Eye className="mr-2 size-4" aria-hidden="true" /> + 상세보기 + </DropdownMenuItem> + </DropdownMenuContent> + </DropdownMenu> + ); + }, + enableSorting: false, + enableHiding: false, + size: 80, + }, + ] +} |
