From de2ac5a2860bc25180971e7a11f852d9d44675b7 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Wed, 6 Aug 2025 04:23:40 +0000 Subject: (대표님) 정기평가, 법적검토, 정책, 가입관련 처리 및 관련 컴포넌트 추가, 메뉴 변경 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/evaluation/service.ts | 6 +- lib/evaluation/table/evaluation-filter-sheet.tsx | 234 ++++++++++++++++------- lib/evaluation/table/evaluation-table.tsx | 223 ++++++++++++++++----- 3 files changed, 342 insertions(+), 121 deletions(-) (limited to 'lib/evaluation') diff --git a/lib/evaluation/service.ts b/lib/evaluation/service.ts index 879876ed..b958e371 100644 --- a/lib/evaluation/service.ts +++ b/lib/evaluation/service.ts @@ -40,7 +40,7 @@ import { authOptions } from "@/app/api/auth/[...nextauth]/route" import { AttachmentDetail, EvaluationDetailResponse } from "@/types/evaluation-form" import { headers } from 'next/headers'; -export async function getPeriodicEvaluations(input: GetEvaluationTargetsSchema) { +export async function getPeriodicEvaluations(input: GetEvaluationsSchema) { try { const offset = (input.page - 1) * input.perPage; @@ -1300,7 +1300,7 @@ export async function getEvaluationDetails(periodicEvaluationId: number): Promis } -export async function getPeriodicEvaluationsAggregated(input: GetEvaluationTargetsSchema) { +export async function getPeriodicEvaluationsAggregated(input: GetEvaluationsSchema) { try { const offset = (input.page - 1) * input.perPage; @@ -1398,7 +1398,7 @@ export async function getPeriodicEvaluationsAggregated(input: GetEvaluationTarge } // 기존 함수에 집계 옵션을 추가한 통합 함수 -export async function getPeriodicEvaluationsWithAggregation(input: GetEvaluationTargetsSchema) { +export async function getPeriodicEvaluationsWithAggregation(input: GetEvaluationsSchema) { if (input.aggregated) { return getPeriodicEvaluationsAggregated(input); } else { diff --git a/lib/evaluation/table/evaluation-filter-sheet.tsx b/lib/evaluation/table/evaluation-filter-sheet.tsx index b0bf9139..c2dd9734 100644 --- a/lib/evaluation/table/evaluation-filter-sheet.tsx +++ b/lib/evaluation/table/evaluation-filter-sheet.tsx @@ -7,7 +7,6 @@ import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { Search, X } from "lucide-react"; import { customAlphabet } from "nanoid"; -import { parseAsStringEnum, useQueryState } from "nuqs"; import { Button } from "@/components/ui/button"; import { @@ -28,7 +27,6 @@ import { SelectValue, } from "@/components/ui/select"; import { cn } from "@/lib/utils"; -import { getFiltersStateParser } from "@/lib/parsers"; import { EVALUATION_TARGET_FILTER_OPTIONS } from "@/lib/evaluation-target-list/validation"; /***************************************************************************************** @@ -49,7 +47,6 @@ const statusOptions = [ { value: "FINALIZED", label: "결과확정" }, ]; - const documentsSubmittedOptions = [ { value: "true", label: "제출완료" }, { value: "false", label: "미제출" }, @@ -104,14 +101,13 @@ export function PeriodicEvaluationFilterSheet({ isLoading = false, onFiltersApply, }: PeriodicEvaluationFilterSheetProps) { - /** Router (needed only for pathname) */ + /** Router (needed for URL updates) */ const router = useRouter(); /** Track pending state while we update URL */ const [isPending, startTransition] = useTransition(); const [joinOperator, setJoinOperator] = useState<"and" | "or">("and") - /** React‑Hook‑Form */ const form = useForm({ resolver: zodResolver(periodicEvaluationFilterSchema), @@ -131,82 +127,156 @@ export function PeriodicEvaluationFilterSheet({ }, }); - /***************************************************************************************** - * 3️⃣ Submit → build filter array → push to URL (and reset page=1) + * 3️⃣ Submit → build filter array → callback + URL (동기적 처리) *****************************************************************************************/ async function onSubmit(data: PeriodicEvaluationFilterFormValues) { startTransition(() => { try { - const newFilters: any[] = []; - - const pushFilter = ( - id: string, - value: any, - type: "text" | "select" | "number" | "boolean", - operator: "eq" | "iLike" | "gte" | "lte" - ) => { - newFilters.push({ id, value, type, operator, rowId: generateId() }); - }; - - if (data.evaluationYear?.trim()) - pushFilter("evaluationYear", Number(data.evaluationYear), "number", "eq"); - - if (data.division?.trim()) - pushFilter("division", data.division.trim(), "select", "eq"); - - if (data.status?.trim()) - pushFilter("status", data.status.trim(), "select", "eq"); - - if (data.domesticForeign?.trim()) - pushFilter("domesticForeign", data.domesticForeign.trim(), "select", "eq"); - - if (data.materialType?.trim()) - pushFilter("materialType", data.materialType.trim(), "select", "eq"); - - if (data.vendorCode?.trim()) - pushFilter("vendorCode", data.vendorCode.trim(), "text", "iLike"); - - if (data.vendorName?.trim()) - pushFilter("vendorName", data.vendorName.trim(), "text", "iLike"); - - if (data.documentsSubmitted?.trim()) - pushFilter( - "documentsSubmitted", - data.documentsSubmitted.trim() === "true", - "boolean", - "eq" - ); - - if (data.evaluationGrade?.trim()) - pushFilter("evaluationGrade", data.evaluationGrade.trim(), "select", "eq"); - - if (data.finalGrade?.trim()) - pushFilter("finalGrade", data.finalGrade.trim(), "select", "eq"); - - if (data.minTotalScore?.trim()) - pushFilter("totalScore", Number(data.minTotalScore), "number", "gte"); - - if (data.maxTotalScore?.trim()) - pushFilter("totalScore", Number(data.maxTotalScore), "number", "lte"); - - setJoinOperator(joinOperator); - - + const newFilters = [] + + // 필터 생성 로직 + if (data.evaluationYear?.trim()) { + newFilters.push({ + id: "evaluationYear", + value: parseInt(data.evaluationYear.trim()), + type: "number", + operator: "eq", + rowId: generateId() + }) + } + + if (data.division?.trim()) { + newFilters.push({ + id: "division", + value: data.division.trim(), + type: "select", + operator: "eq", + rowId: generateId() + }) + } + + if (data.status?.trim()) { + newFilters.push({ + id: "status", + value: data.status.trim(), + type: "select", + operator: "eq", + rowId: generateId() + }) + } + + if (data.domesticForeign?.trim()) { + newFilters.push({ + id: "domesticForeign", + value: data.domesticForeign.trim(), + type: "select", + operator: "eq", + rowId: generateId() + }) + } + + if (data.materialType?.trim()) { + newFilters.push({ + id: "materialType", + value: data.materialType.trim(), + type: "select", + operator: "eq", + rowId: generateId() + }) + } + + if (data.vendorCode?.trim()) { + newFilters.push({ + id: "vendorCode", + value: data.vendorCode.trim(), + type: "text", + operator: "iLike", + rowId: generateId() + }) + } + + if (data.vendorName?.trim()) { + newFilters.push({ + id: "vendorName", + value: data.vendorName.trim(), + type: "text", + operator: "iLike", + rowId: generateId() + }) + } + + if (data.documentsSubmitted?.trim()) { + newFilters.push({ + id: "documentsSubmitted", + value: data.documentsSubmitted.trim() === "true", + type: "boolean", + operator: "eq", + rowId: generateId() + }) + } + + if (data.evaluationGrade?.trim()) { + newFilters.push({ + id: "evaluationGrade", + value: data.evaluationGrade.trim(), + type: "select", + operator: "eq", + rowId: generateId() + }) + } + + if (data.finalGrade?.trim()) { + newFilters.push({ + id: "finalGrade", + value: data.finalGrade.trim(), + type: "select", + operator: "eq", + rowId: generateId() + }) + } + + if (data.minTotalScore?.trim()) { + newFilters.push({ + id: "totalScore", + value: parseFloat(data.minTotalScore.trim()), + type: "number", + operator: "gte", + rowId: generateId() + }) + } + + if (data.maxTotalScore?.trim()) { + newFilters.push({ + id: "totalScore", + value: parseFloat(data.maxTotalScore.trim()), + type: "number", + operator: "lte", + rowId: generateId() + }) + } + + console.log("=== 생성된 필터들 ===", newFilters); + console.log("=== 조인 연산자 ===", joinOperator); + + + // ✅ 부모 컴포넌트에 필터 전달 (동기적으로 즉시 호출) onFiltersApply(newFilters, joinOperator); - } catch (err) { - // eslint-disable-next-line no-console - console.error("정기평가 필터 적용 오류:", err); + + console.log("=== 필터 적용 완료 ==="); + } catch (error) { + console.error("정기평가 필터 적용 오류:", error); } }); } /***************************************************************************************** - * 4️⃣ Reset → clear form & URL + * 4️⃣ Reset → clear form & URL (동기적 처리) *****************************************************************************************/ - async function handleReset() { + function handleReset() { + // 1. 폼 초기화 form.reset({ - evaluationYear: new Date().getFullYear().toString(), + evaluationYear: "", division: "", status: "", domesticForeign: "", @@ -220,9 +290,26 @@ export function PeriodicEvaluationFilterSheet({ maxTotalScore: "", }); - onFiltersApply([], "and"); + // 2. 조인 연산자 초기화 setJoinOperator("and"); + // 3. URL 파라미터 초기화 (필터를 빈 배열로 설정) + const currentUrl = new URL(window.location.href); + const newSearchParams = new URLSearchParams(currentUrl.search); + + // 필터 관련 파라미터 초기화 + newSearchParams.set("filters", JSON.stringify([])); + newSearchParams.set("joinOperator", "and"); + newSearchParams.set("page", "1"); + newSearchParams.delete("search"); // 검색어 제거 + + // URL 업데이트 + router.replace(`${currentUrl.pathname}?${newSearchParams.toString()}`); + + // 4. 빈 필터 배열 전달 (즉시 UI 업데이트를 위해) + onFiltersApply([], "and"); + + console.log("=== 필터 완전 초기화 완료 ==="); } /***************************************************************************************** @@ -306,7 +393,6 @@ export function PeriodicEvaluationFilterSheet({ )} /> - {/* 구분 */} { e.stopPropagation(); - form.setValue("materialType", ""); + form.setValue("materialType", ""); }} disabled={isPending} > @@ -798,7 +884,7 @@ export function PeriodicEvaluationFilterSheet({ type="button" variant="outline" onClick={handleReset} - disabled={isPending } + disabled={isPending} className="px-4" > 초기화 @@ -806,7 +892,7 @@ export function PeriodicEvaluationFilterSheet({