summaryrefslogtreecommitdiff
path: root/lib/evaluation-target-list/table/evaluation-targets-columns.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/evaluation-target-list/table/evaluation-targets-columns.tsx')
-rw-r--r--lib/evaluation-target-list/table/evaluation-targets-columns.tsx351
1 files changed, 125 insertions, 226 deletions
diff --git a/lib/evaluation-target-list/table/evaluation-targets-columns.tsx b/lib/evaluation-target-list/table/evaluation-targets-columns.tsx
index 93807ef9..e2163cad 100644
--- a/lib/evaluation-target-list/table/evaluation-targets-columns.tsx
+++ b/lib/evaluation-target-list/table/evaluation-targets-columns.tsx
@@ -4,16 +4,17 @@ import { type ColumnDef } from "@tanstack/react-table";
import { Checkbox } from "@/components/ui/checkbox";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
-import { Pencil, Eye, MessageSquare, Check, X } from "lucide-react";
+import { Pencil, Check, X } from "lucide-react";
import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header";
import { EvaluationTargetWithDepartments } from "@/db/schema";
-import { EditEvaluationTargetSheet } from "./update-evaluation-target";
+import type { DataTableRowAction } from "@/types/table";
+import { formatDate } from "@/lib/utils";
interface GetColumnsProps {
setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<EvaluationTargetWithDepartments> | null>>;
}
-// 상태별 색상 매핑
+// ✅ 모든 헬퍼 함수들을 컴포넌트 외부로 이동 (매번 재생성 방지)
const getStatusBadgeVariant = (status: string) => {
switch (status) {
case "PENDING":
@@ -27,7 +28,15 @@ const getStatusBadgeVariant = (status: string) => {
}
};
-// 의견 일치 여부 배지
+const getStatusText = (status: string) => {
+ const statusMap = {
+ PENDING: "검토 중",
+ CONFIRMED: "확정",
+ EXCLUDED: "제외"
+ };
+ return statusMap[status] || status;
+};
+
const getConsensusBadge = (consensusStatus: boolean | null) => {
if (consensusStatus === null) {
return <Badge variant="outline">검토 중</Badge>;
@@ -38,16 +47,14 @@ const getConsensusBadge = (consensusStatus: boolean | null) => {
return <Badge variant="destructive">의견 불일치</Badge>;
};
-// 구분 배지
const getDivisionBadge = (division: string) => {
return (
- <Badge variant={division === "PLANT" ? "default" : "secondary"}>
- {division === "PLANT" ? "해양" : "조선"}
+ <Badge variant={division === "OCEAN" ? "default" : "secondary"}>
+ {division === "OCEAN" ? "해양" : "조선"}
</Badge>
);
};
-// 자재구분 배지
const getMaterialTypeBadge = (materialType: string) => {
const typeMap = {
EQUIPMENT: "기자재",
@@ -57,7 +64,6 @@ const getMaterialTypeBadge = (materialType: string) => {
return <Badge variant="outline">{typeMap[materialType] || materialType}</Badge>;
};
-// 내외자 배지
const getDomesticForeignBadge = (domesticForeign: string) => {
return (
<Badge variant={domesticForeign === "DOMESTIC" ? "default" : "secondary"}>
@@ -66,24 +72,27 @@ const getDomesticForeignBadge = (domesticForeign: string) => {
);
};
-// 평가 상태 배지
-const getApprovalBadge = (isApproved: boolean | null) => {
- if (isApproved === null) {
- return <Badge variant="outline" className="text-xs">대기중</Badge>;
- }
- if (isApproved === true) {
- return <Badge variant="default" className="bg-green-600 text-xs">승인</Badge>;
+// ✅ 평가 대상 여부 표시 함수
+const getEvaluationTargetBadge = (isTarget: boolean | null) => {
+ if (isTarget === null) {
+ return <Badge variant="outline">미정</Badge>;
}
- return <Badge variant="destructive" className="text-xs">거부</Badge>;
+ return isTarget ? (
+ <Badge variant="default" className="bg-blue-600">
+ <Check className="size-3 mr-1" />
+ 평가 대상
+ </Badge>
+ ) : (
+ <Badge variant="secondary">
+ <X className="size-3 mr-1" />
+ 평가 제외
+ </Badge>
+ );
};
-export function getEvaluationTargetsColumns({setRowAction}:GetColumnsProps): ColumnDef<EvaluationTargetWithDepartments>[] {
+export function getEvaluationTargetsColumns({ setRowAction }: GetColumnsProps): ColumnDef<EvaluationTargetWithDepartments>[] {
return [
- // ═══════════════════════════════════════════════════════════════
- // 기본 정보
- // ═══════════════════════════════════════════════════════════════
-
- // Checkbox
+ // ✅ Checkbox
{
id: "select",
header: ({ table }) => (
@@ -107,15 +116,13 @@ export function getEvaluationTargetsColumns({setRowAction}:GetColumnsProps): Col
enableHiding: false,
},
- // ░░░ 평가년도 ░░░
+ // ✅ 기본 정보
{
accessorKey: "evaluationYear",
header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="평가년도" />,
cell: ({ row }) => <span className="font-medium">{row.getValue("evaluationYear")}</span>,
size: 100,
},
-
- // ░░░ 구분 ░░░
{
accessorKey: "division",
header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="구분" />,
@@ -127,24 +134,25 @@ export function getEvaluationTargetsColumns({setRowAction}:GetColumnsProps): Col
header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="상태" />,
cell: ({ row }) => {
const status = row.getValue<string>("status");
- const statusMap = {
- PENDING: "검토 중",
- CONFIRMED: "확정",
- EXCLUDED: "제외"
- };
return (
<Badge variant={getStatusBadgeVariant(status)}>
- {statusMap[status] || status}
+ {getStatusText(status)}
</Badge>
);
},
size: 100,
},
+ {
+ accessorKey: "consensusStatus",
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="의견 일치" />,
+ cell: ({ row }) => getConsensusBadge(row.getValue("consensusStatus")),
+ size: 100,
+ },
- // ░░░ 벤더 코드 ░░░
-
+ // ✅ 벤더 정보 그룹
{
- header: "협력업체 정보",
+ id: "vendorInfo",
+ header: "벤더 정보",
columns: [
{
accessorKey: "vendorCode",
@@ -154,8 +162,6 @@ export function getEvaluationTargetsColumns({setRowAction}:GetColumnsProps): Col
),
size: 120,
},
-
- // ░░░ 벤더명 ░░░
{
accessorKey: "vendorName",
header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="벤더명" />,
@@ -166,267 +172,182 @@ export function getEvaluationTargetsColumns({setRowAction}:GetColumnsProps): Col
),
size: 200,
},
-
- // ░░░ 내외자 ░░░
{
accessorKey: "domesticForeign",
header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="내외자" />,
cell: ({ row }) => getDomesticForeignBadge(row.getValue("domesticForeign")),
size: 80,
},
-
+ {
+ accessorKey: "materialType",
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="자재구분" />,
+ cell: ({ row }) => getMaterialTypeBadge(row.getValue("materialType")),
+ size: 120,
+ },
]
},
- // ░░░ 자재구분 ░░░
- {
- accessorKey: "materialType",
- header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="자재구분" />,
- cell: ({ row }) => getMaterialTypeBadge(row.getValue("materialType")),
- size: 120,
- },
-
- // ░░░ 상태 ░░░
-
-
- // ░░░ 의견 일치 여부 ░░░
- {
- accessorKey: "consensusStatus",
- header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="의견 일치" />,
- cell: ({ row }) => getConsensusBadge(row.getValue("consensusStatus")),
- size: 100,
- },
-
- // ═══════════════════════════════════════════════════════════════
- // 주문 부서 그룹
- // ═══════════════════════════════════════════════════════════════
+ // ✅ 발주 담당자
{
- header: "발주 평가 담당자",
+ id: "orderReviewer",
+ header: "발주 담당자",
columns: [
{
- accessorKey: "orderDepartmentName",
- header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="부서명" />,
- cell: ({ row }) => {
- const departmentName = row.getValue<string>("orderDepartmentName");
- return departmentName ? (
- <div className="truncate max-w-[120px]" title={departmentName}>
- {departmentName}
- </div>
- ) : (
- <span className="text-muted-foreground">-</span>
- );
- },
- size: 120,
- },
- {
accessorKey: "orderReviewerName",
- header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="담당자" />,
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="담당자명" />,
cell: ({ row }) => {
const reviewerName = row.getValue<string>("orderReviewerName");
return reviewerName ? (
- <div className="truncate max-w-[100px]" title={reviewerName}>
+ <div className="truncate max-w-[120px]" title={reviewerName}>
{reviewerName}
</div>
) : (
<span className="text-muted-foreground">-</span>
);
},
- size: 100,
+ size: 120,
},
{
accessorKey: "orderIsApproved",
- header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="평가" />,
- cell: ({ row }) => getApprovalBadge(row.getValue("orderIsApproved")),
- size: 80,
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="평가 대상" />,
+ cell: ({ row }) => {
+ const isApproved = row.getValue<boolean>("orderIsApproved");
+ return getEvaluationTargetBadge(isApproved);
+ },
+ size: 120,
},
- ],
+ ]
},
- // ═══════════════════════════════════════════════════════════════
- // 조달 부서 그룹
- // ═══════════════════════════════════════════════════════════════
+ // ✅ 조달 담당자
{
- header: "조달 평가 담당자",
+ id: "procurementReviewer",
+ header: "조달 담당자",
columns: [
{
- accessorKey: "procurementDepartmentName",
- header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="부서명" />,
- cell: ({ row }) => {
- const departmentName = row.getValue<string>("procurementDepartmentName");
- return departmentName ? (
- <div className="truncate max-w-[120px]" title={departmentName}>
- {departmentName}
- </div>
- ) : (
- <span className="text-muted-foreground">-</span>
- );
- },
- size: 120,
- },
- {
accessorKey: "procurementReviewerName",
- header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="담당자" />,
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="담당자명" />,
cell: ({ row }) => {
const reviewerName = row.getValue<string>("procurementReviewerName");
return reviewerName ? (
- <div className="truncate max-w-[100px]" title={reviewerName}>
+ <div className="truncate max-w-[120px]" title={reviewerName}>
{reviewerName}
</div>
) : (
<span className="text-muted-foreground">-</span>
);
},
- size: 100,
+ size: 120,
},
{
accessorKey: "procurementIsApproved",
- header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="평가" />,
- cell: ({ row }) => getApprovalBadge(row.getValue("procurementIsApproved")),
- size: 80,
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="평가 대상" />,
+ cell: ({ row }) => {
+ const isApproved = row.getValue<boolean>("procurementIsApproved");
+ return getEvaluationTargetBadge(isApproved);
+ },
+ size: 120,
},
- ],
+ ]
},
- // ═══════════════════════════════════════════════════════════════
- // 품질 부서 그룹
- // ═══════════════════════════════════════════════════════════════
+ // ✅ 품질 담당자
{
- header: "품질 평가 담당자",
+ id: "qualityReviewer",
+ header: "품질 담당자",
columns: [
{
- accessorKey: "qualityDepartmentName",
- header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="부서명" />,
- cell: ({ row }) => {
- const departmentName = row.getValue<string>("qualityDepartmentName");
- return departmentName ? (
- <div className="truncate max-w-[120px]" title={departmentName}>
- {departmentName}
- </div>
- ) : (
- <span className="text-muted-foreground">-</span>
- );
- },
- size: 120,
- },
- {
accessorKey: "qualityReviewerName",
- header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="담당자" />,
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="담당자명" />,
cell: ({ row }) => {
const reviewerName = row.getValue<string>("qualityReviewerName");
return reviewerName ? (
- <div className="truncate max-w-[100px]" title={reviewerName}>
+ <div className="truncate max-w-[120px]" title={reviewerName}>
{reviewerName}
</div>
) : (
<span className="text-muted-foreground">-</span>
);
},
- size: 100,
+ size: 120,
},
{
accessorKey: "qualityIsApproved",
- header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="평가" />,
- cell: ({ row }) => getApprovalBadge(row.getValue("qualityIsApproved")),
- size: 80,
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="평가 대상" />,
+ cell: ({ row }) => {
+ const isApproved = row.getValue<boolean>("qualityIsApproved");
+ return getEvaluationTargetBadge(isApproved);
+ },
+ size: 120,
},
- ],
+ ]
},
- // ═══════════════════════════════════════════════════════════════
- // 설계 부서 그룹
- // ═══════════════════════════════════════════════════════════════
+ // ✅ 설계 담당자
{
- header: "설계 평가 담당자",
+ id: "designReviewer",
+ header: "설계 담당자",
columns: [
{
- accessorKey: "designDepartmentName",
- header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="부서명" />,
- cell: ({ row }) => {
- const departmentName = row.getValue<string>("designDepartmentName");
- return departmentName ? (
- <div className="truncate max-w-[120px]" title={departmentName}>
- {departmentName}
- </div>
- ) : (
- <span className="text-muted-foreground">-</span>
- );
- },
- size: 120,
- },
- {
accessorKey: "designReviewerName",
- header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="담당자" />,
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="담당자명" />,
cell: ({ row }) => {
const reviewerName = row.getValue<string>("designReviewerName");
return reviewerName ? (
- <div className="truncate max-w-[100px]" title={reviewerName}>
+ <div className="truncate max-w-[120px]" title={reviewerName}>
{reviewerName}
</div>
) : (
<span className="text-muted-foreground">-</span>
);
},
- size: 100,
+ size: 120,
},
{
accessorKey: "designIsApproved",
- header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="평가" />,
- cell: ({ row }) => getApprovalBadge(row.getValue("designIsApproved")),
- size: 80,
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="평가 대상" />,
+ cell: ({ row }) => {
+ const isApproved = row.getValue<boolean>("designIsApproved");
+ return getEvaluationTargetBadge(isApproved);
+ },
+ size: 120,
},
- ],
+ ]
},
- // ═══════════════════════════════════════════════════════════════
- // CS 부서 그룹
- // ═══════════════════════════════════════════════════════════════
+ // ✅ CS 담당자
{
- header: "CS 평가 담당자",
+ id: "csReviewer",
+ header: "CS 담당자",
columns: [
{
- accessorKey: "csDepartmentName",
- header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="부서명" />,
- cell: ({ row }) => {
- const departmentName = row.getValue<string>("csDepartmentName");
- return departmentName ? (
- <div className="truncate max-w-[120px]" title={departmentName}>
- {departmentName}
- </div>
- ) : (
- <span className="text-muted-foreground">-</span>
- );
- },
- size: 120,
- },
- {
accessorKey: "csReviewerName",
- header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="담당자" />,
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="담당자명" />,
cell: ({ row }) => {
const reviewerName = row.getValue<string>("csReviewerName");
return reviewerName ? (
- <div className="truncate max-w-[100px]" title={reviewerName}>
+ <div className="truncate max-w-[120px]" title={reviewerName}>
{reviewerName}
</div>
) : (
<span className="text-muted-foreground">-</span>
);
},
- size: 100,
+ size: 120,
},
{
accessorKey: "csIsApproved",
- header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="평가" />,
- cell: ({ row }) => getApprovalBadge(row.getValue("csIsApproved")),
- size: 80,
+ header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="평가 대상" />,
+ cell: ({ row }) => {
+ const isApproved = row.getValue<boolean>("csIsApproved");
+ return getEvaluationTargetBadge(isApproved);
+ },
+ size: 120,
},
- ],
+ ]
},
- // ═══════════════════════════════════════════════════════════════
- // 관리 정보
- // ═══════════════════════════════════════════════════════════════
-
- // ░░░ 관리자 의견 ░░░
+ // ✅ 의견 및 결과
{
accessorKey: "adminComment",
header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="관리자 의견" />,
@@ -442,8 +363,6 @@ export function getEvaluationTargetsColumns({setRowAction}:GetColumnsProps): Col
},
size: 150,
},
-
- // ░░░ 종합 의견 ░░░
{
accessorKey: "consolidatedComment",
header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="종합 의견" />,
@@ -459,69 +378,49 @@ export function getEvaluationTargetsColumns({setRowAction}:GetColumnsProps): Col
},
size: 150,
},
-
- // ░░░ 확정일 ░░░
{
accessorKey: "confirmedAt",
header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="확정일" />,
cell: ({ row }) => {
const confirmedAt = row.getValue<Date>("confirmedAt");
- return confirmedAt ? (
- <span className="text-sm">
- {new Intl.DateTimeFormat("ko-KR", {
- year: "numeric",
- month: "2-digit",
- day: "2-digit",
- }).format(new Date(confirmedAt))}
- </span>
- ) : (
- <span className="text-muted-foreground">-</span>
- );
+ return <span className="text-sm">{formatDate(confirmedAt, "KR")}</span>;
},
size: 100,
},
-
- // ░░░ 생성일 ░░░
{
accessorKey: "createdAt",
header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="생성일" />,
cell: ({ row }) => {
const createdAt = row.getValue<Date>("createdAt");
- return createdAt ? (
- <span className="text-sm">
- {new Intl.DateTimeFormat("ko-KR", {
- year: "numeric",
- month: "2-digit",
- day: "2-digit",
- }).format(new Date(createdAt))}
- </span>
- ) : (
- <span className="text-muted-foreground">-</span>
- );
+ return <span className="text-sm">{formatDate(createdAt, "KR")}</span>;
},
size: 100,
},
- // ░░░ Actions ░░░
+ // ✅ Actions - 가장 안전하게 처리
{
id: "actions",
enableHiding: false,
size: 40,
minSize: 40,
cell: ({ row }) => {
- return (
+ // ✅ 함수를 직접 정의해서 매번 새로 생성되지 않도록 처리
+ const handleEdit = () => {
+ setRowAction({ row, type: "update" });
+ };
+
+ return (
<div className="flex items-center gap-1">
<Button
variant="ghost"
size="icon"
className="size-8"
- onClick={() => setRowAction({ row, type: "update" })}
+ onClick={handleEdit}
aria-label="수정"
title="수정"
>
<Pencil className="size-4" />
</Button>
-
</div>
);
},