From e9897d416b3e7327bbd4d4aef887eee37751ae82 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Fri, 27 Jun 2025 01:16:20 +0000 Subject: (대표님) 20250627 오전 10시 작업사항 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/evaluation-target-list/service.ts | 22 +++-- .../table/evaluation-target-table.tsx | 109 ++++++++++++++++++--- .../table/evaluation-targets-columns.tsx | 4 +- .../table/evaluation-targets-toolbar-actions.tsx | 4 +- 4 files changed, 109 insertions(+), 30 deletions(-) (limited to 'lib/evaluation-target-list') diff --git a/lib/evaluation-target-list/service.ts b/lib/evaluation-target-list/service.ts index 0da50fa2..bb47fca4 100644 --- a/lib/evaluation-target-list/service.ts +++ b/lib/evaluation-target-list/service.ts @@ -19,8 +19,13 @@ import { EvaluationTargetWithDepartments, evaluationTargetsWithDepartments, periodicEvaluations, - reviewerEvaluations + reviewerEvaluations, + evaluationSubmissions, + generalEvaluations, + esgEvaluationItems } from "@/db/schema"; + + import { GetEvaluationTargetsSchema } from "./validation"; import { PgTransaction } from "drizzle-orm/pg-core"; import { getServerSession } from "next-auth/next" @@ -62,8 +67,6 @@ export async function countEvaluationTargetsFromView( } // ============= 메인 서버 액션도 함께 수정 ============= - - export async function getEvaluationTargets(input: GetEvaluationTargetsSchema) { try { const offset = (input.page - 1) * input.perPage; @@ -590,18 +593,18 @@ export async function updateEvaluationTarget(input: UpdateEvaluationTargetInput) const allRejected = approvals.every(approval => approval === false) const hasConsensus = allApproved || allRejected - let newStatus: "PENDING" | "CONFIRMED" | "EXCLUDED" = "PENDING" - if (hasConsensus) { - newStatus = allApproved ? "CONFIRMED" : "EXCLUDED" - } + // let newStatus: "PENDING" | "CONFIRMED" | "EXCLUDED" = "PENDING" + // if (hasConsensus) { + // newStatus = allApproved ? "CONFIRMED" : "EXCLUDED" + // } - console.log("Auto-updating status:", { hasConsensus, newStatus, approvals }) + // console.log("Auto-updating status:", { hasConsensus, newStatus, approvals }) + console.log("Auto-updating status:", { hasConsensus, approvals }) await tx .update(evaluationTargets) .set({ consensusStatus: hasConsensus, - status: newStatus, confirmedAt: hasConsensus ? new Date() : null, confirmedBy: hasConsensus ? Number(session.user.id) : null, updatedAt: new Date() @@ -1128,7 +1131,6 @@ export async function requestEvaluationReview(targetIds: number[], message?: str await Promise.all(emailPromises) - revalidatePath("/evaluation-targets") return { success: true, message: `${reviewerEmails.size}명의 담당자에게 의견 요청 이메일이 발송되었습니다.`, diff --git a/lib/evaluation-target-list/table/evaluation-target-table.tsx b/lib/evaluation-target-list/table/evaluation-target-table.tsx index 87be3589..b140df0e 100644 --- a/lib/evaluation-target-list/table/evaluation-target-table.tsx +++ b/lib/evaluation-target-list/table/evaluation-target-table.tsx @@ -7,7 +7,7 @@ import * as React from "react"; import { useSearchParams } from "next/navigation"; import { Button } from "@/components/ui/button"; -import { PanelLeftClose, PanelLeftOpen } from "lucide-react"; +import { HelpCircle, PanelLeftClose, PanelLeftOpen } from "lucide-react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Skeleton } from "@/components/ui/skeleton"; @@ -28,6 +28,74 @@ import { EvaluationTargetsTableToolbarActions } from "./evaluation-targets-toolb import { EvaluationTargetFilterSheet } from "./evaluation-targets-filter-sheet"; import { EvaluationTargetWithDepartments } from "@/db/schema"; import { EditEvaluationTargetSheet } from "./update-evaluation-target"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; + +/* -------------------------------------------------------------------------- */ +/* Process Guide Popover */ +/* -------------------------------------------------------------------------- */ +function ProcessGuidePopover() { + return ( + + + + + +
+
+

평가 대상 확정 프로세스

+

+ 발주실적을 기반으로 평가 대상을 확정하는 절차입니다. +

+
+
+
+
+ 1 +
+
+

발주실적 기반 자동 추출

+

전년도 10월 ~ 해당년도 9월 발주실적에서 업체 목록을 자동으로 생성합니다.

+
+
+
+
+ 2 +
+
+

담당자 지정

+

각 평가 대상별로 5개 부서(발주/조달/품질/설계/CS)의 담당자를 지정합니다.

+
+
+
+
+ 3 +
+
+

검토 및 의견 수렴

+

모든 담당자가 평가 대상 적합성을 검토하고 의견을 제출합니다.

+
+
+
+
+ 4 +
+
+

최종 확정

+

모든 담당자 의견이 일치하면 평가 대상으로 최종 확정됩니다.

+
+
+
+
+
+
+ ) +} /* -------------------------------------------------------------------------- */ /* Stats Card */ @@ -130,7 +198,7 @@ function EvaluationTargetsStats({ evaluationYear }: { evaluationYear: number }) 확정 - 완료 + 완료
{confirmed.toLocaleString()}
@@ -204,10 +272,17 @@ export function EvaluationTargetsTable({ promises, evaluationYear, className }: const tableData = promiseData; /* ---------------------- 검색 파라미터 안전 처리 ---------------------- */ - const getSearchParam = React.useCallback((key: string, defaultValue?: string): string => { - return searchParams?.get(key) ?? defaultValue ?? ""; - }, [searchParams]); - + const searchString = React.useMemo( + () => searchParams.toString(), // query가 바뀔 때만 새로 계산 + [searchParams] + ); + + const getSearchParam = React.useCallback( + (key: string, def = "") => + new URLSearchParams(searchString).get(key) ?? def, + [searchString] + ); + // 제네릭 함수는 useCallback 밖에서 정의 const parseSearchParamHelper = React.useCallback((key: string, defaultValue: any): any => { try { @@ -226,7 +301,7 @@ const parseSearchParam = (key: string, defaultValue: T): T => { const initialSettings = React.useMemo(() => ({ page: parseInt(getSearchParam("page", "1")), perPage: parseInt(getSearchParam("perPage", "10")), - sort: parseSearchParam("sort", [{ id: "createdAt", desc: true }]), + sort: getSearchParam('sort') ? JSON.parse(getSearchParam('sort')!) : [{ id: "createdAt", desc: true }], filters: parseSearchParam("filters", []), joinOperator: (getSearchParam("joinOperator") as "and" | "or") || "and", basicFilters: parseSearchParam("basicFilters", []), @@ -237,7 +312,7 @@ const parseSearchParam = (key: string, defaultValue: T): T => { pinnedColumns: { left: [], right: ["actions"] }, groupBy: [], expandedRows: [], - }), [getSearchParam, parseSearchParamHelper]); + }), [getSearchParam]); /* --------------------- 프리셋 훅 ------------------------------ */ const { @@ -267,9 +342,6 @@ const parseSearchParam = (key: string, defaultValue: T): T => { // { accessorKey: "division", header: "구분" } // ]; -window.addEventListener('beforeunload', () => { - console.trace('[beforeunload] 문서가 통째로 사라지려 합니다!'); -}); /* 기본 필터 */ const filterFields: DataTableFilterField[] = [ @@ -297,11 +369,16 @@ window.addEventListener('beforeunload', () => { /* current settings */ const currentSettings = React.useMemo(() => getCurrentSettings(), [getCurrentSettings]); - const initialState = React.useMemo(() => ({ - sorting: initialSettings.sort.filter((s: any) => columns.some((c: any) => ("accessorKey" in c ? c.accessorKey : c.id) === s.id)), - columnVisibility: currentSettings.columnVisibility, - columnPinning: currentSettings.pinnedColumns, - }), [columns, currentSettings, initialSettings.sort]); + const initialState = React.useMemo(() => { + return { + sorting: initialSettings.sort.filter(sortItem => { + const columnExists = columns.some(col => col.accessorKey === sortItem.id) + return columnExists + }) as any, + columnVisibility: currentSettings.columnVisibility, + columnPinning: currentSettings.pinnedColumns, + } + }, [currentSettings, initialSettings.sort, columns]) /* ----------------------- useDataTable ------------------------ */ const { table } = useDataTable({ diff --git a/lib/evaluation-target-list/table/evaluation-targets-columns.tsx b/lib/evaluation-target-list/table/evaluation-targets-columns.tsx index e2163cad..b6631f14 100644 --- a/lib/evaluation-target-list/table/evaluation-targets-columns.tsx +++ b/lib/evaluation-target-list/table/evaluation-targets-columns.tsx @@ -42,7 +42,7 @@ const getConsensusBadge = (consensusStatus: boolean | null) => { return 검토 중; } if (consensusStatus === true) { - return 의견 일치; + return 의견 일치; } return 의견 불일치; }; @@ -383,7 +383,7 @@ export function getEvaluationTargetsColumns({ setRowAction }: GetColumnsProps): header: ({ column }) => , cell: ({ row }) => { const confirmedAt = row.getValue("confirmedAt"); - return {formatDate(confirmedAt, "KR")}; + return { confirmedAt ? formatDate(confirmedAt, "KR") :'-'}; }, size: 100, }, 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 7ea2e0ec..82b7c97c 100644 --- a/lib/evaluation-target-list/table/evaluation-targets-toolbar-actions.tsx +++ b/lib/evaluation-target-list/table/evaluation-targets-toolbar-actions.tsx @@ -160,9 +160,9 @@ export function EvaluationTargetsTableToolbarActions({ {/* 확정 버튼 */} {selectedStats.canConfirm && (