"use client" import { useEffect, useTransition, useState, useRef } from "react" import { useRouter, useParams } from "next/navigation" import { z } from "zod" import { useForm } from "react-hook-form" import { zodResolver } from "@hookform/resolvers/zod" import { CalendarIcon, ChevronRight, Search, X } from "lucide-react" import { customAlphabet } from "nanoid" import { parseAsStringEnum, useQueryState } from "nuqs" import { Button } from "@/components/ui/button" import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form" import { Input } from "@/components/ui/input" import { Badge } from "@/components/ui/badge" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { cn } from "@/lib/utils" import { useTranslation } from '@/i18n/client' import { getFiltersStateParser } from "@/lib/parsers" import { DateRangePicker } from "@/components/date-range-picker" // nanoid 생성기 const generateId = customAlphabet("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 6) // PQ 필터 스키마 정의 const pqFilterSchema = z.object({ requesterName: z.string().optional(), pqNumber: z.string().optional(), vendorName: z.string().optional(), status: z.string().optional(), evaluationResult: z.string().optional(), createdAtRange: z.object({ from: z.date().optional(), to: z.date().optional(), }).optional(), }) // PQ 상태 옵션 정의 const pqStatusOptions = [ { value: "REQUESTED", label: "요청됨" }, { value: "IN_PROGRESS", label: "진행 중" }, { value: "SUBMITTED", label: "제출됨" }, { value: "APPROVED", label: "승인됨" }, { value: "REJECTED", label: "거부됨" }, ] // 평가 결과 옵션 정의 const evaluationResultOptions = [ { value: "APPROVED", label: "승인" }, { value: "SUPPLEMENT", label: "보완" }, { value: "REJECTED", label: "불가" }, ] type PQFilterFormValues = z.infer interface PQFilterSheetProps { isOpen: boolean; onClose: () => void; onSearch?: () => void; isLoading?: boolean; } export function PQFilterSheet({ isOpen, onClose, onSearch, isLoading = false }: PQFilterSheetProps) { const router = useRouter() const params = useParams(); const lng = params ? (params.lng as string) : 'ko'; const { t } = useTranslation(lng); const [isPending, startTransition] = useTransition() // 초기화 상태 추가 - 폼 초기화 중에는 상태 변경을 방지 const [isInitializing, setIsInitializing] = useState(false) // 마지막으로 적용된 필터를 추적하기 위한 ref const lastAppliedFilters = useRef("") // nuqs로 URL 상태 관리 - 파라미터명을 'pqBasicFilters'로 변경 const [filters, setFilters] = useQueryState( "basicFilters", getFiltersStateParser().withDefault([]) ) // joinOperator 설정 const [joinOperator, setJoinOperator] = useQueryState( "basicJoinOperator", parseAsStringEnum(["and", "or"]).withDefault("and") ) // 현재 URL의 페이지 파라미터도 가져옴 const [page, setPage] = useQueryState("page", { defaultValue: "1" }) // 폼 상태 초기화 const form = useForm({ resolver: zodResolver(pqFilterSchema), defaultValues: { requesterName: "", pqNumber: "", vendorName: "", status: "", evaluationResult: "", createdAtRange: { from: undefined, to: undefined, }, }, }) // URL 필터에서 초기 폼 상태 설정 useEffect(() => { // 현재 필터를 문자열로 직렬화 const currentFiltersString = JSON.stringify(filters); // 패널이 열렸고, 필터가 있고, 마지막에 적용된 필터와 다를 때만 업데이트 if (isOpen && filters && filters.length > 0 && currentFiltersString !== lastAppliedFilters.current) { setIsInitializing(true); const formValues = { ...form.getValues() }; let formUpdated = false; filters.forEach(filter => { if (filter.id === "createdAt" && Array.isArray(filter.value) && filter.value.length > 0) { formValues.createdAtRange = { from: filter.value[0] ? new Date(filter.value[0]) : undefined, to: filter.value[1] ? new Date(filter.value[1]) : undefined, }; formUpdated = true; } else if (filter.id in formValues) { // @ts-ignore - 동적 필드 접근 formValues[filter.id] = filter.value; formUpdated = true; } }); // 폼 값이 변경된 경우에만 reset으로 한 번에 업데이트 if (formUpdated) { form.reset(formValues); lastAppliedFilters.current = currentFiltersString; } setIsInitializing(false); } }, [filters, isOpen]) // 현재 적용된 필터 카운트 const getActiveFilterCount = () => { return filters?.length || 0 } // 폼 제출 핸들러 - 수동 URL 업데이트 버전 async function onSubmit(data: PQFilterFormValues) { // 초기화 중이면 제출 방지 if (isInitializing) return; startTransition(async () => { try { // 필터 배열 생성 const newFilters = [] if (data.requesterName?.trim()) { newFilters.push({ id: "requesterName", value: data.requesterName.trim(), type: "text", operator: "iLike", rowId: generateId() }) } if (data.pqNumber?.trim()) { newFilters.push({ id: "pqNumber", value: data.pqNumber.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.status?.trim()) { newFilters.push({ id: "status", value: data.status.trim(), type: "select", operator: "eq", rowId: generateId() }) } if (data.evaluationResult?.trim()) { newFilters.push({ id: "evaluationResult", value: data.evaluationResult.trim(), type: "select", operator: "eq", rowId: generateId() }) } // 생성일 범위 추가 if (data.createdAtRange?.from) { newFilters.push({ id: "createdAt", value: [ data.createdAtRange.from.toISOString().split('T')[0], data.createdAtRange.to ? data.createdAtRange.to.toISOString().split('T')[0] : undefined ].filter(Boolean), type: "date", operator: "isBetween", rowId: generateId() }) } // 수동으로 URL 업데이트 (nuqs 대신) const currentUrl = new URL(window.location.href); const params = new URLSearchParams(currentUrl.search); // 기존 필터 관련 파라미터 제거 params.delete('basicFilters'); params.delete('pqBasicFilters'); params.delete('basicJoinOperator'); params.delete('pqBasicJoinOperator'); params.delete('page'); // 새로운 필터 추가 if (newFilters.length > 0) { params.set('basicFilters', JSON.stringify(newFilters)); params.set('basicJoinOperator', joinOperator); } // 페이지를 1로 설정 params.set('page', '1'); const newUrl = `${currentUrl.pathname}?${params.toString()}`; console.log("New URL:", newUrl); // 페이지 완전 새로고침으로 서버 렌더링 강제 window.location.href = newUrl; // 마지막 적용된 필터 업데이트 lastAppliedFilters.current = JSON.stringify(newFilters); // 필터 업데이트 후 조회 핸들러 호출 (제공된 경우) if (onSearch) { console.log("Calling onSearch..."); onSearch(); } console.log("=== PQ Filter Submit Complete ==="); } catch (error) { console.error("PQ 필터 적용 오류:", error); } }) } // 필터 초기화 핸들러 // 필터 초기화 핸들러 async function handleReset() { try { setIsInitializing(true); form.reset({ requesterName: "", pqNumber: "", vendorName: "", status: "", evaluationResult: "", createdAtRange: { from: undefined, to: undefined }, }); console.log("=== PQ Filter Reset Debug ==="); console.log("Current URL before reset:", window.location.href); // 수동으로 URL 초기화 const currentUrl = new URL(window.location.href); const params = new URLSearchParams(currentUrl.search); // 필터 관련 파라미터 제거 params.delete('basicFilters'); params.delete('pqBasicFilters'); params.delete('basicJoinOperator'); params.delete('pqBasicJoinOperator'); params.set('page', '1'); const newUrl = `${currentUrl.pathname}?${params.toString()}`; console.log("Reset URL:", newUrl); // 페이지 완전 새로고침 window.location.href = newUrl; // 마지막 적용된 필터 초기화 lastAppliedFilters.current = ""; console.log("PQ 필터 초기화 완료"); setIsInitializing(false); } catch (error) { console.error("PQ 필터 초기화 오류:", error); setIsInitializing(false); } } // Don't render if not open (for side panel use) if (!isOpen) { return null; } return (
{/* Filter Panel Header */}

PQ 검색 필터

{getActiveFilterCount() > 0 && ( {getActiveFilterCount()}개 필터 적용됨 )}
{/* Join Operator Selection */}
{/* Scrollable content area */}
{/* 요청자명 */} ( 요청자명
{field.value && ( )}
)} /> {/* PQ 번호 */} ( PQ 번호
{field.value && ( )}
)} /> {/* 협력업체명 */} ( 협력업체명
{field.value && ( )}
)} /> {/* PQ 상태 */} ( PQ 상태 )} /> {/* 평가 결과 */} ( 평가 결과 )} /> {/* PQ 생성일 */} ( PQ 생성일
{(field.value?.from || field.value?.to) && ( )}
)} />
{/* Fixed buttons at bottom */}
) }