diff options
Diffstat (limited to 'lib/basic-contract/template/basic-contract-template-columns.tsx')
| -rw-r--r-- | lib/basic-contract/template/basic-contract-template-columns.tsx | 423 |
1 files changed, 347 insertions, 76 deletions
diff --git a/lib/basic-contract/template/basic-contract-template-columns.tsx b/lib/basic-contract/template/basic-contract-template-columns.tsx index 5f4433d1..3be46791 100644 --- a/lib/basic-contract/template/basic-contract-template-columns.tsx +++ b/lib/basic-contract/template/basic-contract-template-columns.tsx @@ -3,7 +3,7 @@ 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 { Download, Ellipsis, Paperclip, CheckCircle, XCircle } from "lucide-react"
import { toast } from "sonner"
import { getErrorMessage } from "@/lib/handle-error"
@@ -24,9 +24,10 @@ import { DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
+import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header"
-import { basicContractTemplateColumnsConfig } from "@/config/basicContractColumnsConfig"
+import { scopeHelpers } from "@/config/basicContractColumnsConfig"
import { BasicContractTemplate } from "@/db/schema"
interface GetColumnsProps {
@@ -40,7 +41,7 @@ const handleFileDownload = (filePath: string, fileName: string) => { try {
// 전체 URL 생성
const fullUrl = `${window.location.origin}${filePath}`;
-
+
// a 태그를 생성하여 다운로드 실행
const link = document.createElement('a');
link.href = fullUrl;
@@ -48,7 +49,7 @@ const handleFileDownload = (filePath: string, fileName: string) => { document.body.appendChild(link);
link.click();
document.body.removeChild(link);
-
+
toast.success("파일 다운로드를 시작합니다.");
} catch (error) {
console.error("파일 다운로드 오류:", error);
@@ -97,7 +98,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<BasicCo header: "",
cell: ({ row }) => {
const template = row.original;
-
+
return (
<Button
variant="ghost"
@@ -123,6 +124,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<BasicCo enableHiding: false,
cell: function Cell({ row }) {
const [isUpdatePending, startUpdateTransition] = React.useTransition()
+ const template = row.original;
return (
<DropdownMenu>
@@ -142,6 +144,22 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<BasicCo Edit
</DropdownMenuItem>
+ {template.status === 'ACTIVE' && (
+ <DropdownMenuItem
+ onSelect={() => setRowAction({ row, type: "dispose" })}
+ >
+ Dispose
+ </DropdownMenuItem>
+ )}
+
+ {template.status === 'DISPOSED' && template.disposedAt && (
+ <DropdownMenuItem
+ onSelect={() => setRowAction({ row, type: "restore" })}
+ >
+ Restore
+ </DropdownMenuItem>
+ )}
+
<DropdownMenuSeparator />
<DropdownMenuItem
onSelect={() => setRowAction({ row, type: "delete" })}
@@ -157,88 +175,341 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<BasicCo }
// ----------------------------------------------------------------
- // 4) 일반 컬럼들을 "그룹"별로 묶어 중첩 columns 생성
+ // 4) 직접 컬럼 정의 (그룹별로 중첩)
// ----------------------------------------------------------------
- // 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, "KR")
- }
-
- // 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) ?? ""
+ // 기본 정보 그룹
+ const basicInfoColumns: ColumnDef<BasicContractTemplate>[] = [
+ {
+ accessorKey: "status",
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="상태" />,
+ cell: ({ row }) => {
+ const status = row.getValue("status") as string;
+ const isActive = status === "ACTIVE";
+ return (
+ <Badge variant={isActive ? "default" : "secondary"}>
+ {isActive ? "활성" : "폐기"}
+ </Badge>
+ );
},
- minSize:80
- }
+ size: 80,
+ enableResizing: true,
+ },
- groupMap[groupName].push(childCol)
- })
+ {
+ accessorKey: "templateName",
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="계약문서명" />,
+ cell: ({ row }) => {
+ const template = row.original;
+ const scopeText = scopeHelpers.getScopeDisplayText(template);
+ return (
+ <div className="flex flex-col min-w-0">
+ <span className="truncate">{template.templateName}</span>
+ <TooltipProvider>
+ <Tooltip>
+ <TooltipTrigger asChild>
+ <span className="text-xs text-muted-foreground cursor-help truncate">
+ {scopeText}
+ </span>
+ </TooltipTrigger>
+ <TooltipContent>
+ <div className="space-y-1">
+ <p className="font-medium">적용 범위:</p>
+ {scopeHelpers.getApplicableScopeLabels(template).map(label => (
+ <p key={label} className="text-xs">• {label}</p>
+ ))}
+ </div>
+ </TooltipContent>
+ </Tooltip>
+ </TooltipProvider>
+ </div>
+ );
+ },
+ size: 250,
+ enableResizing: true,
+ },
+ {
+ accessorKey: "templateCode",
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="계약문서 번호" />,
+ cell: ({ row }) => {
+ const template = row.original;
+ return (
+ <span className="font-medium">{template.templateCode}</span>
+ );
+ },
+ size: 120,
+ enableResizing: true,
+ },
+ {
+ accessorKey: "revision",
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="Rev." />,
+ cell: ({ row }) => {
+ const template = row.original;
+ return (
+ <span className="text-xs text-muted-foreground">v{template.revision}</span>
+ );
+ },
+ size: 60,
+ enableResizing: true,
+ },
+ {
+ accessorKey: "legalReviewRequired",
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="법무검토" />,
+ cell: ({ row }) => {
+ const required = row.getValue("legalReviewRequired") as boolean;
+ return (
+ <Badge variant={required ? "destructive" : "secondary"}>
+ {required ? "필요" : "불필요"}
+ </Badge>
+ );
+ },
+ size: 100,
+ enableResizing: true,
+ },
+ ];
- // ----------------------------------------------------------------
- // 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,
- })
- }
- })
+ // 적용 범위 그룹
+ const scopeColumns: ColumnDef<BasicContractTemplate>[] = [
+ {
+ accessorKey: "shipBuildingApplicable",
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="조선해양" />,
+ cell: ({ row }) => {
+ const applicable = row.getValue("shipBuildingApplicable") as boolean;
+ return (
+ <div className="flex justify-center">
+ {applicable ? (
+ <CheckCircle className="h-4 w-4 text-green-500" />
+ ) : (
+ <XCircle className="h-4 w-4 text-gray-300" />
+ )}
+ </div>
+ );
+ },
+ size: 80,
+ enableResizing: true,
+ },
+ {
+ accessorKey: "windApplicable",
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="풍력" />,
+ cell: ({ row }) => {
+ const applicable = row.getValue("windApplicable") as boolean;
+ return (
+ <div className="flex justify-center">
+ {applicable ? (
+ <CheckCircle className="h-4 w-4 text-green-500" />
+ ) : (
+ <XCircle className="h-4 w-4 text-gray-300" />
+ )}
+ </div>
+ );
+ },
+ size: 60,
+ enableResizing: true,
+ },
+ {
+ accessorKey: "pcApplicable",
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="PC" />,
+ cell: ({ row }) => {
+ const applicable = row.getValue("pcApplicable") as boolean;
+ return (
+ <div className="flex justify-center">
+ {applicable ? (
+ <CheckCircle className="h-4 w-4 text-green-500" />
+ ) : (
+ <XCircle className="h-4 w-4 text-gray-300" />
+ )}
+ </div>
+ );
+ },
+ size: 50,
+ enableResizing: true,
+ },
+ {
+ accessorKey: "nbApplicable",
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="NB" />,
+ cell: ({ row }) => {
+ const applicable = row.getValue("nbApplicable") as boolean;
+ return (
+ <div className="flex justify-center">
+ {applicable ? (
+ <CheckCircle className="h-4 w-4 text-green-500" />
+ ) : (
+ <XCircle className="h-4 w-4 text-gray-300" />
+ )}
+ </div>
+ );
+ },
+ size: 50,
+ enableResizing: true,
+ },
+ {
+ accessorKey: "rcApplicable",
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="RC" />,
+ cell: ({ row }) => {
+ const applicable = row.getValue("rcApplicable") as boolean;
+ return (
+ <div className="flex justify-center">
+ {applicable ? (
+ <CheckCircle className="h-4 w-4 text-green-500" />
+ ) : (
+ <XCircle className="h-4 w-4 text-gray-300" />
+ )}
+ </div>
+ );
+ },
+ size: 50,
+ enableResizing: true,
+ },
+ {
+ accessorKey: "gyApplicable",
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="GY" />,
+ cell: ({ row }) => {
+ const applicable = row.getValue("gyApplicable") as boolean;
+ return (
+ <div className="flex justify-center">
+ {applicable ? (
+ <CheckCircle className="h-4 w-4 text-green-500" />
+ ) : (
+ <XCircle className="h-4 w-4 text-gray-300" />
+ )}
+ </div>
+ );
+ },
+ size: 50,
+ enableResizing: true,
+ },
+ {
+ accessorKey: "sysApplicable",
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="S&Sys" />,
+ cell: ({ row }) => {
+ const applicable = row.getValue("sysApplicable") as boolean;
+ return (
+ <div className="flex justify-center">
+ {applicable ? (
+ <CheckCircle className="h-4 w-4 text-green-500" />
+ ) : (
+ <XCircle className="h-4 w-4 text-gray-300" />
+ )}
+ </div>
+ );
+ },
+ size: 60,
+ enableResizing: true,
+ },
+ {
+ accessorKey: "infraApplicable",
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="Infra" />,
+ cell: ({ row }) => {
+ const applicable = row.getValue("infraApplicable") as boolean;
+ return (
+ <div className="flex justify-center">
+ {applicable ? (
+ <CheckCircle className="h-4 w-4 text-green-500" />
+ ) : (
+ <XCircle className="h-4 w-4 text-gray-300" />
+ )}
+ </div>
+ );
+ },
+ size: 60,
+ enableResizing: true,
+ },
+ ];
+
+ // 파일 정보 그룹
+ const fileInfoColumns: ColumnDef<BasicContractTemplate>[] = [
+ {
+ accessorKey: "fileName",
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="파일명" />,
+ cell: ({ row }) => {
+ const fileName = row.getValue("fileName") as string;
+ return (
+ <div className="min-w-0 max-w-full">
+ <span className="block truncate" title={fileName}>
+ {fileName}
+ </span>
+ </div>
+ );
+ },
+ size: 200,
+ enableResizing: true,
+ },
+ ];
+
+ // 감사 정보 그룹
+ const auditColumns: ColumnDef<BasicContractTemplate>[] = [
+ {
+ accessorKey: "createdAt",
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="생성일" />,
+ cell: ({ row }) => {
+ const date = row.getValue("createdAt") as Date;
+ return date ? formatDateTime(date, "KR") : "-";
+ },
+ size: 120,
+ enableResizing: true,
+ },
+ {
+ accessorKey: "updatedAt",
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="수정일" />,
+ cell: ({ row }) => {
+ const date = row.getValue("updatedAt") as Date;
+ return date ? formatDateTime(date, "KR") : "-";
+ },
+ size: 120,
+ enableResizing: true,
+ },
+ {
+ accessorKey: "disposedAt",
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="폐기일" />,
+ cell: ({ row }) => {
+ const date = row.getValue("disposedAt") as Date;
+ return date ? formatDateTime(date, "KR") : "-";
+ },
+ size: 120,
+ enableResizing: true,
+ },
+ {
+ accessorKey: "restoredAt",
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="복구일" />,
+ cell: ({ row }) => {
+ const date = row.getValue("restoredAt") as Date;
+ return date ? formatDateTime(date, "KR") : "-";
+ },
+ size: 120,
+ enableResizing: true,
+ },
+ ];
+
+ // 중첩 컬럼 그룹 생성
+ const nestedColumns: ColumnDef<BasicContractTemplate>[] = [
+ {
+ id: "기본 정보",
+ header: "기본 정보",
+ columns: basicInfoColumns,
+ },
+ {
+ id: "적용 범위",
+ header: "적용 범위",
+ columns: scopeColumns,
+ },
+ {
+ id: "파일 정보",
+ header: "파일 정보",
+ columns: fileInfoColumns,
+ },
+ {
+ id: "감사 정보",
+ header: "감사 정보",
+ columns: auditColumns,
+ },
+ ]
// ----------------------------------------------------------------
// 5) 최종 컬럼 배열: select, download, nestedColumns, actions
// ----------------------------------------------------------------
return [
selectColumn,
- downloadColumn, // 다운로드 컬럼 추가
+ downloadColumn,
...nestedColumns,
actionsColumn,
]
|
