From c657ef972feeafff16ab0e07cb4771f7dd141ba0 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Thu, 10 Jul 2025 09:55:45 +0000 Subject: (대표님) 20250710 작업사항 - 평가 첨부, 로그인, SEDP 변경 요구사항 반영 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../table/evaluation-target-table.tsx | 2 + .../table/evaluation-targets-columns.tsx | 66 ++++++-- .../table/evaluation-targets-toolbar-actions.tsx | 181 ++++++++++++++------- .../table/update-evaluation-target.tsx | 10 +- 4 files changed, 189 insertions(+), 70 deletions(-) (limited to 'lib/evaluation-target-list/table') diff --git a/lib/evaluation-target-list/table/evaluation-target-table.tsx b/lib/evaluation-target-list/table/evaluation-target-table.tsx index b140df0e..5560d3ff 100644 --- a/lib/evaluation-target-list/table/evaluation-target-table.tsx +++ b/lib/evaluation-target-list/table/evaluation-target-table.tsx @@ -271,6 +271,8 @@ export function EvaluationTargetsTable({ promises, evaluationYear, className }: const [promiseData] = React.use(promises); const tableData = promiseData; + console.log(tableData) + /* ---------------------- 검색 파라미터 안전 처리 ---------------------- */ const searchString = React.useMemo( () => searchParams.toString(), // query가 바뀔 때만 새로 계산 diff --git a/lib/evaluation-target-list/table/evaluation-targets-columns.tsx b/lib/evaluation-target-list/table/evaluation-targets-columns.tsx index 60f1af39..c3aa9d71 100644 --- a/lib/evaluation-target-list/table/evaluation-targets-columns.tsx +++ b/lib/evaluation-target-list/table/evaluation-targets-columns.tsx @@ -16,7 +16,7 @@ interface GetColumnsProps { } // ✅ 모든 헬퍼 함수들을 컴포넌트 외부로 이동 -const getStatusBadgeVariant = (status: string) => { +export const getStatusBadgeVariant = (status: string) => { switch (status) { case "PENDING": return "secondary"; case "CONFIRMED": return "default"; @@ -43,14 +43,14 @@ const getDivisionBadge = (division: string) => { ); }; -const getMaterialTypeBadge = (materialType: string) => { +export const getMaterialTypeBadge = (materialType: string) => { return {vendortypeMap[materialType] || materialType}; }; const getDomesticForeignBadge = (domesticForeign: string) => { return ( - {domesticForeign === "DOMESTIC" ? "내자" : "외자"} + {domesticForeign === "DOMESTIC" ? "D" : "F"} ); }; @@ -72,6 +72,16 @@ const getEvaluationTargetBadge = (isTarget: boolean | null) => { ); }; +const getStatusLabel = (status: string) => { + const statusMap = { + PENDING: "미확정", + EXCLUDED: "제외", + CONFIRMED: "확정" + }; + return statusMap[status] || status; +}; + + // ✅ 모든 cell 렌더러 함수들을 미리 정의 (매번 새로 생성 방지) const renderEvaluationYear = ({ row }: any) => ( {row.getValue("evaluationYear")} @@ -83,7 +93,7 @@ const renderStatus = ({ row }: any) => { const status = row.getValue("status"); return ( - {status} + {getStatusLabel(status)} ); }; @@ -213,12 +223,7 @@ function createStaticColumns(setRowAction: GetColumnsProps['setRowAction']): Col cell: renderStatus, size: 100, }, - { - accessorKey: "consensusStatus", - header: createHeaderRenderer("의견 일치"), - cell: renderConsensusStatus, - size: 100, - }, + // 벤더 정보 { @@ -252,6 +257,47 @@ function createStaticColumns(setRowAction: GetColumnsProps['setRowAction']): Col ] }, + { + accessorKey: "consensusStatus", + header: createHeaderRenderer("의견 일치"), + cell: renderConsensusStatus, + size: 100, + }, + + { + id: "claim", + header: "L/D, Claim", + columns:[ + { + accessorKey: "ldClaimCount", + header: ({ column }) => , + cell: ({ row }) => ( + {row.original.ldClaimCount} + ), + size: 80, + }, + + { + accessorKey: "ldClaimAmount", + header: ({ column }) => , + cell: ({ row }) => ( + {(Number(row.original.ldClaimAmount).toLocaleString())} + ), + size: 80, + }, + { + accessorKey: "ldClaimCurrency", + header: ({ column }) => , + + cell: ({ row }) => ( + {row.original.ldClaimCurrency} + ), + size: 80, + }, + ] + + }, + // 발주 담당자 { id: "orderReviewer", diff --git a/lib/evaluation-target-list/table/evaluation-targets-toolbar-actions.tsx b/lib/evaluation-target-list/table/evaluation-targets-toolbar-actions.tsx index 8bc5254c..d1c7e500 100644 --- a/lib/evaluation-target-list/table/evaluation-targets-toolbar-actions.tsx +++ b/lib/evaluation-target-list/table/evaluation-targets-toolbar-actions.tsx @@ -14,6 +14,7 @@ import { } from "lucide-react" import { toast } from "sonner" import { useRouter } from "next/navigation" +import { useSession } from "next-auth/react" import { Button } from "@/components/ui/button" import { @@ -31,6 +32,8 @@ import { } from "./evaluation-target-action-dialogs" import { EvaluationTargetWithDepartments } from "@/db/schema" import { exportTableToExcel } from "@/lib/export" +import { autoGenerateEvaluationTargets } from "../service" // 서버 액션 import +import { useAuthRole } from "@/hooks/use-auth-role" interface EvaluationTargetsTableToolbarActionsProps { table: Table @@ -47,6 +50,16 @@ export function EvaluationTargetsTableToolbarActions({ const [excludeDialogOpen, setExcludeDialogOpen] = React.useState(false) const [reviewDialogOpen, setReviewDialogOpen] = React.useState(false) const router = useRouter() + const { data: session } = useSession() + + // 권한 체크 + const { hasRole, isLoading: roleLoading } = useAuthRole() + const canManageEvaluations = hasRole('정기평가') || hasRole('admin') + + // 사용자 ID 가져오기 + const userId = React.useMemo(() => { + return session?.user?.id ? Number(session.user.id) : 1; + }, [session]); // 선택된 행들 const selectedRows = table.getFilteredSelectedRowModel().rows @@ -141,16 +154,36 @@ export function EvaluationTargetsTableToolbarActions({ const handleAutoGenerate = React.useCallback(async () => { setIsLoading(true) try { - // TODO: 발주실적에서 자동 추출 API 호출 - toast.success("평가 대상이 자동으로 생성되었습니다.") - router.refresh() + // 현재 년도를 기준으로 평가 대상 자동 생성 + const currentYear = new Date().getFullYear() + const result = await autoGenerateEvaluationTargets(currentYear, userId) + + if (result.success) { + if (result.generatedCount === 0) { + toast.info(result.message, { + description: result.skippedCount + ? `이미 존재하는 평가 대상: ${result.skippedCount}개` + : undefined + }) + } else { + toast.success(result.message, { + description: result.details + ? `해양: ${result.details.shipTargets}개, 조선: ${result.details.plantTargets}개 생성${result.details.duplicateSkipped > 0 ? `, 중복 건너뜀: ${result.details.duplicateSkipped}개` : ''}` + : undefined + }) + } + onRefresh?.() + router.refresh() + } else { + toast.error(result.error || "자동 생성 중 오류가 발생했습니다.") + } } catch (error) { console.error('Error auto generating targets:', error) toast.error("자동 생성 중 오류가 발생했습니다.") } finally { setIsLoading(false) } - }, [router]) + }, [router, onRefresh, userId]) // ---------------------------------------------------------------- // 신규 평가 대상 생성 (수동) @@ -178,33 +211,54 @@ export function EvaluationTargetsTableToolbarActions({ }) }, [table]) + // 권한이 없거나 로딩 중인 경우 내보내기 버튼만 표시 + if (roleLoading) { + return ( +
+
+ +
+
+ ) + } + return ( <>
- {/* 신규 생성 드롭다운 */} - - - - - - - - 자동 생성 (발주실적 기반) - - - - 수동 생성 - - - + {/* 신규 생성 드롭다운 - 정기평가 권한이 있는 경우만 표시 */} + {canManageEvaluations && ( + + + + + + + + 자동 생성 (발주실적 기반) + + + + 수동 생성 + + + + )} {/* 유틸리티 버튼들 */}
@@ -219,8 +273,8 @@ export function EvaluationTargetsTableToolbarActions({
- {/* 선택된 항목 액션 버튼들 */} - {hasSelection && ( + {/* 선택된 항목 액션 버튼들 - 정기평가 권한이 있는 경우만 표시 */} + {canManageEvaluations && hasSelection && (
{/* 확정 버튼 */} {selectedStats.canConfirm && ( @@ -271,37 +325,52 @@ export function EvaluationTargetsTableToolbarActions({ )}
)} + + {/* 권한이 없는 경우 안내 메시지 (선택사항) */} + {!canManageEvaluations && hasSelection && ( +
+
+ 평가 관리 권한이 필요합니다 +
+
+ )}
- {/* 수동 생성 다이얼로그 */} - + {/* 다이얼로그들 - 권한이 있는 경우만 렌더링 */} + {canManageEvaluations && ( + <> + {/* 수동 생성 다이얼로그 */} + - {/* 확정 컨펌 다이얼로그 */} - + {/* 확정 컨펌 다이얼로그 */} + - {/* 제외 컨펌 다이얼로그 */} - + {/* 제외 컨펌 다이얼로그 */} + - {/* 의견 요청 다이얼로그 */} - + {/* 의견 요청 다이얼로그 */} + + + )} ) } \ No newline at end of file diff --git a/lib/evaluation-target-list/table/update-evaluation-target.tsx b/lib/evaluation-target-list/table/update-evaluation-target.tsx index 9f9b7af4..8ea63a1a 100644 --- a/lib/evaluation-target-list/table/update-evaluation-target.tsx +++ b/lib/evaluation-target-list/table/update-evaluation-target.tsx @@ -58,6 +58,8 @@ import { type UpdateEvaluationTargetInput, } from "../service" import { EvaluationTargetWithDepartments } from "@/db/schema" +import { getMaterialTypeBadge } from "./evaluation-targets-columns" +import { getStatusLabel } from "../validation" // 편집 가능한 필드들에 대한 스키마 const editEvaluationTargetSchema = z.object({ @@ -123,10 +125,10 @@ export function EditEvaluationTargetSheet({ } const userEmail = session.user.email - const userRole = session.user.role + const userRole = session.user?.roles // 평가관리자는 모든 권한 - if (userRole === "평가관리자") { + if (userRole?.some(role => role.includes('정기평가'))|| userRole?.some(role => role.toLocaleLowerCase().includes('admin'))) { return { level: "admin", editableApprovals: [ @@ -372,10 +374,10 @@ export function EditEvaluationTargetSheet({ 벤더명: {evaluationTarget.vendorName}
- 자재구분: {evaluationTarget.materialType} + 자재구분: {getMaterialTypeBadge(evaluationTarget.materialType)}
- 상태: {evaluationTarget.status} + 상태: {getStatusLabel(evaluationTarget.status)}
-- cgit v1.2.3