summaryrefslogtreecommitdiff
path: root/lib/basic-contract/status/basic-contract-columns.tsx
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-08-27 12:06:26 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-08-27 12:06:26 +0000
commit7548e2ad6948f1c6aa102fcac408bc6c9c0f9796 (patch)
tree8e66703ec821888ad51dcc242a508813a027bf71 /lib/basic-contract/status/basic-contract-columns.tsx
parent7eac558470ef179dad626a8e82db5784fe86a556 (diff)
(대표님, 최겸) 기본계약, 입찰, 파일라우트, 계약서명라우트, 인포메이션, 메뉴설정, PQ(메일템플릿 관련)
Diffstat (limited to 'lib/basic-contract/status/basic-contract-columns.tsx')
-rw-r--r--lib/basic-contract/status/basic-contract-columns.tsx363
1 files changed, 240 insertions, 123 deletions
diff --git a/lib/basic-contract/status/basic-contract-columns.tsx b/lib/basic-contract/status/basic-contract-columns.tsx
index cc9d9bff..8ae8fa1e 100644
--- a/lib/basic-contract/status/basic-contract-columns.tsx
+++ b/lib/basic-contract/status/basic-contract-columns.tsx
@@ -8,26 +8,34 @@ import { formatDateTime } from "@/lib/utils"
import { Badge } from "@/components/ui/badge"
import { Checkbox } from "@/components/ui/checkbox"
import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header"
-import {
- FileActionsDropdown,
- FileNameLink
-} from "@/components/ui/file-actions"
+import { Button } from "@/components/ui/button"
+import { MoreHorizontal, Eye, FileText, Calendar } from "lucide-react"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu"
+import { useRouter } from "next/navigation"
+import { BasicContractTemplateStatsView } from "@/db/schema"
+
+type NextRouter = ReturnType<typeof useRouter>;
-import { basicContractColumnsConfig } from "@/config/basicContractColumnsConfig"
-import { BasicContractView } from "@/db/schema"
interface GetColumnsProps {
- setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<BasicContractView> | null>>
+ setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<BasicContractTemplateStatsView> | null>>
+ router: NextRouter;
+
}
/**
- * 공용 파일 다운로드 유틸리티를 사용하는 간소화된 컬럼 정의
+ * BasicContractTemplateStatsView용 컬럼 정의
*/
-export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<BasicContractView>[] {
+export function getColumns({ setRowAction, router }: GetColumnsProps): ColumnDef<BasicContractTemplateStatsView>[] {
// ----------------------------------------------------------------
// 1) select 컬럼 (체크박스)
// ----------------------------------------------------------------
- const selectColumn: ColumnDef<BasicContractView> = {
+ const selectColumn: ColumnDef<BasicContractTemplateStatsView> = {
id: "select",
header: ({ table }) => (
<Checkbox
@@ -54,143 +62,252 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<BasicCo
}
// ----------------------------------------------------------------
- // 2) 파일 다운로드 컬럼 (공용 컴포넌트 사용)
+ // 2) Actions 컬럼
// ----------------------------------------------------------------
- const downloadColumn: ColumnDef<BasicContractView> = {
- id: "download",
- header: "",
+ const actionsColumn: ColumnDef<BasicContractTemplateStatsView> = {
+ id: "actions",
+ header: "작업",
cell: ({ row }) => {
- const template = row.original;
-
- if (!template.filePath || !template.fileName) {
- return null;
- }
+ const template = row.original
+ const detailUrl = `/evcp/basic-contract/${template.templateId}`;
return (
- <FileActionsDropdown
- filePath={template.filePath}
- fileName={template.fileName}
- variant="ghost"
- size="icon"
- />
- );
+ <DropdownMenu>
+ <DropdownMenuTrigger asChild>
+ <Button variant="ghost" className="h-8 w-8 p-0">
+ <span className="sr-only">Open menu</span>
+ <MoreHorizontal className="h-4 w-4" />
+ </Button>
+ </DropdownMenuTrigger>
+ <DropdownMenuContent align="end">
+ <DropdownMenuItem onClick={() => router.push(detailUrl)}>
+ <Eye className="mr-2 h-4 w-4" />
+ 상세 보기
+ </DropdownMenuItem>
+ <DropdownMenuItem onClick={() => setRowAction({ type: "view", row })}>
+ <FileText className="mr-2 h-4 w-4" />
+ 템플릿 보기
+ </DropdownMenuItem>
+ </DropdownMenuContent>
+ </DropdownMenu>
+ )
},
- maxSize: 30,
enableSorting: false,
+ enableHiding: false,
+ maxSize: 80,
}
// ----------------------------------------------------------------
- // 3) 일반 컬럼들을 "그룹"별로 묶어 중첩 columns 생성
+ // 3) 기본 정보 컬럼들
// ----------------------------------------------------------------
- const groupMap: Record<string, ColumnDef<BasicContractView>[]> = {}
-
- basicContractColumnsConfig.forEach((cfg) => {
- const groupName = cfg.group || "_noGroup"
-
- if (!groupMap[groupName]) {
- groupMap[groupName] = []
- }
+ const basicInfoColumns: ColumnDef<BasicContractTemplateStatsView>[] = [
+ {
+ accessorKey: "templateName",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="템플릿명" />
+ ),
+ cell: ({ row }) => {
+ const name = row.getValue("templateName") as string
+ return (
+ <div className="font-medium">
+ {name}
+ </div>
+ )
+ },
+ minSize: 200,
+ },
+ {
+ accessorKey: "revision",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="리비전" />
+ ),
+ cell: ({ row }) => {
+ const revision = row.getValue("revision") as number
+ return (
+ <Badge variant="outline">
+ v{revision}
+ </Badge>
+ )
+ },
+ minSize: 80,
+ },
+ ]
- const childCol: ColumnDef<BasicContractView> = {
- accessorKey: cfg.id,
- enableResizing: true,
+ // ----------------------------------------------------------------
+ // 4) 발송/처리 현황 컬럼들
+ // ----------------------------------------------------------------
+ const processStatusColumns: ColumnDef<BasicContractTemplateStatsView>[] = [
+ {
+ accessorKey: "totalSentCount",
header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title={cfg.label} />
+ <DataTableColumnHeaderSimple column={column} title="발송건수" />
),
- meta: {
- excelHeader: cfg.excelHeader,
- group: cfg.group,
- type: cfg.type,
+ cell: ({ row }) => {
+ const count = row.getValue("totalSentCount") as number
+ return (
+ <div className="text-center font-medium">
+ {count.toLocaleString()}
+ </div>
+ )
},
- cell: ({ row, cell }) => {
- // 날짜 형식 처리
- if (cfg.id === "createdAt" || cfg.id === "updatedAt") {
- const dateVal = cell.getValue() as Date
- return formatDateTime(dateVal, "KR")
- }
+ minSize: 100,
+ },
+ {
+ accessorKey: "overdueCount",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="지연건수" />
+ ),
+ cell: ({ row }) => {
+ const count = row.getValue("overdueCount") as number
+ const total = row.getValue("totalSentCount") as number
+ const isHigh = total > 0 && (count / total) > 0.2
- // Status 컬럼에 Badge 적용 (확장)
- if (cfg.id === "status") {
- const status = row.getValue(cfg.id) as string
-
- let variant: "default" | "secondary" | "destructive" | "outline" = "secondary";
- let label = status;
-
- switch (status) {
- case "ACTIVE":
- variant = "default";
- label = "활성";
- break;
- case "INACTIVE":
- variant = "secondary";
- label = "비활성";
- break;
- case "PENDING":
- variant = "outline";
- label = "대기중";
- break;
- case "COMPLETED":
- variant = "default";
- label = "완료";
- break;
- default:
- variant = "secondary";
- label = status;
- }
-
- return <Badge variant={variant}>{label}</Badge>
- }
-
- // ✅ 파일 이름 컬럼 (공용 컴포넌트 사용)
- if (cfg.id === "fileName") {
- const fileName = cell.getValue() as string;
- const filePath = row.original.filePath;
-
- if (fileName && filePath) {
- return (
- <FileNameLink
- filePath={filePath}
- fileName={fileName}
- maxLength={200}
- showIcon={true}
- />
- );
- }
- return fileName || "";
- }
-
- // 나머지 컬럼은 그대로 값 표시
- return row.getValue(cfg.id) ?? ""
+ return (
+ <div className={`text-center font-medium ${isHigh ? 'text-red-600' : 'text-gray-900'}`}>
+ {count.toLocaleString()}
+ </div>
+ )
},
- minSize: 80,
- }
+ minSize: 100,
+ },
+ {
+ accessorKey: "unsignedCount",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="미서명건수" />
+ ),
+ cell: ({ row }) => {
+ const count = row.getValue("unsignedCount") as number
+ return (
+ <div className="text-center font-medium text-orange-600">
+ {count.toLocaleString()}
+ </div>
+ )
+ },
+ minSize: 120,
+ },
+ ]
- groupMap[groupName].push(childCol)
- })
+ // ----------------------------------------------------------------
+ // 5) 법무검토 현황 컬럼들
+ // ----------------------------------------------------------------
+ const legalReviewColumns: ColumnDef<BasicContractTemplateStatsView>[] = [
+ {
+ accessorKey: "legalRequestCount",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="법무요청" />
+ ),
+ cell: ({ row }) => {
+ const count = row.getValue("legalRequestCount") as number
+ return (
+ <div className="text-center font-medium text-purple-600">
+ {count.toLocaleString()}
+ </div>
+ )
+ },
+ minSize: 100,
+ },
+ {
+ accessorKey: "legalCompletedCount",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="법무완료" />
+ ),
+ cell: ({ row }) => {
+ const count = row.getValue("legalCompletedCount") as number
+ return (
+ <div className="text-center font-medium text-indigo-600">
+ {count.toLocaleString()}
+ </div>
+ )
+ },
+ minSize: 100,
+ },
+ {
+ accessorKey: "contractCompletedCount",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="계약완료" />
+ ),
+ cell: ({ row }) => {
+ const count = row.getValue("contractCompletedCount") as number
+ return (
+ <div className="text-center font-medium text-indigo-600">
+ {count.toLocaleString()}
+ </div>
+ )
+ },
+ minSize: 100,
+ },
+ ]
// ----------------------------------------------------------------
- // 4) groupMap에서 실제 상위 컬럼(그룹)을 만들기
+ // 6) 성과 지표 컬럼들
// ----------------------------------------------------------------
- const nestedColumns: ColumnDef<BasicContractView>[] = []
+ const performanceColumns: ColumnDef<BasicContractTemplateStatsView>[] = [
+ {
+ accessorKey: "avgProcessingDays",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="평균처리일" />
+ ),
+ cell: ({ row }) => {
+ const days = row.getValue("avgProcessingDays") as number | null
+ if (!days) return "-"
+
+ const rounded = Math.round(days * 10) / 10
+ const isGood = rounded <= 5
+ const isWarning = rounded > 5 && rounded <= 10
+
+ let colorClass = "text-gray-900"
+ if (isGood) colorClass = "text-green-600"
+ else if (isWarning) colorClass = "text-orange-600"
+ else colorClass = "text-red-600"
+
+ return (
+ <div className={`text-center font-medium ${colorClass}`}>
+ {rounded}일
+ </div>
+ )
+ },
+ minSize: 100,
+ },
+ ]
- Object.entries(groupMap).forEach(([groupName, colDefs]) => {
- if (groupName === "_noGroup") {
- nestedColumns.push(...colDefs)
- } else {
- nestedColumns.push({
- id: groupName,
- header: groupName,
- columns: colDefs,
- })
- }
- })
+ // ----------------------------------------------------------------
+ // 7) 시간 정보 컬럼들
+ // ----------------------------------------------------------------
+ const timeInfoColumns: ColumnDef<BasicContractTemplateStatsView>[] = [
+ {
+ accessorKey: "templateCreatedAt",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="생성일" />
+ ),
+ cell: ({ row }) => {
+ const date = row.getValue("templateCreatedAt") as Date
+ return formatDateTime(date, "KR")
+ },
+ minSize: 120,
+ },
+ {
+ accessorKey: "lastActivityDate",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="최근활동" />
+ ),
+ cell: ({ row }) => {
+ const date = row.getValue("lastActivityDate") as Date | null
+ return date ? formatDateTime(date, "KR") : "-"
+ },
+ minSize: 120,
+ },
+ ]
// ----------------------------------------------------------------
- // 5) 최종 컬럼 배열
+ // 8) 최종 컬럼 배열 (평면 구조)
// ----------------------------------------------------------------
return [
selectColumn,
- downloadColumn, // ✅ 공용 파일 액션 컴포넌트 사용
- ...nestedColumns,
+ ...basicInfoColumns,
+ ...processStatusColumns,
+ ...legalReviewColumns,
+ ...performanceColumns,
+ ...timeInfoColumns,
+ actionsColumn,
]
-}
+} \ No newline at end of file