"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 { 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 { getFiltersStateParser } from "@/lib/parsers" // nanoid 생성기 const generateId = customAlphabet("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 6) // RFQ 필터 스키마 정의 const rfqFilterSchema = z.object({ rfqCode: z.string().optional(), projectCode: z.string().optional(), picName: z.string().optional(), packageNo: z.string().optional(), packageName: z.string().optional(), status: z.string().optional(), }) // RFQ 상태 옵션 정의 const rfqStatusOptions = [ { value: "DRAFT", label: "초안" }, { value: "Doc. Received", label: "문서접수" }, { value: "PIC Assigned", label: "담당자배정" }, { value: "Doc. Confirmed", label: "문서확인" }, { value: "Init. RFQ Sent", label: "초기RFQ발송" }, { value: "Init. RFQ Answered", label: "초기RFQ회신" }, { value: "TBE started", label: "TBE시작" }, { value: "TBE finished", label: "TBE완료" }, { value: "Final RFQ Sent", label: "최종RFQ발송" }, { value: "Quotation Received", label: "견적접수" }, { value: "Vendor Selected", label: "업체선정" }, ] type RFQFilterFormValues = z.infer interface RFQFilterSheetProps { isOpen: boolean; onClose: () => void; onSearch?: () => void; isLoading?: boolean; } export function RFQFilterSheet({ isOpen, onClose, onSearch, isLoading = false }: RFQFilterSheetProps) { const router = useRouter() const params = useParams(); const lng = params ? (params.lng as string) : 'ko'; const [isPending, startTransition] = useTransition() // 초기화 상태 추가 - 폼 초기화 중에는 상태 변경을 방지 const [isInitializing, setIsInitializing] = useState(false) // 마지막으로 적용된 필터를 추적하기 위한 ref const lastAppliedFilters = useRef("") // nuqs로 URL 상태 관리 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(rfqFilterSchema), defaultValues: { rfqCode: "", projectCode: "", picName: "", packageNo: "", packageName: "", status: "", }, }) // 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 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 } // 폼 제출 핸들러 async function onSubmit(data: RFQFilterFormValues) { // 초기화 중이면 제출 방지 if (isInitializing) return; startTransition(async () => { try { // 필터 배열 생성 const newFilters = [] if (data.rfqCode?.trim()) { newFilters.push({ id: "rfqCode", value: data.rfqCode.trim(), type: "text", operator: "iLike", rowId: generateId() }) } if (data.projectCode?.trim()) { newFilters.push({ id: "projectCode", value: data.projectCode.trim(), type: "text", operator: "iLike", rowId: generateId() }) } if (data.picName?.trim()) { newFilters.push({ id: "picName", value: data.picName.trim(), type: "text", operator: "iLike", rowId: generateId() }) } if (data.packageNo?.trim()) { newFilters.push({ id: "packageNo", value: data.packageNo.trim(), type: "text", operator: "iLike", rowId: generateId() }) } if (data.packageName?.trim()) { newFilters.push({ id: "packageName", value: data.packageName.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() }) } // 수동으로 URL 업데이트 (nuqs 대신) const currentUrl = new URL(window.location.href); const params = new URLSearchParams(currentUrl.search); // 기존 필터 관련 파라미터 제거 params.delete('basicFilters'); params.delete('rfqBasicFilters'); params.delete('basicJoinOperator'); params.delete('rfqBasicJoinOperator'); 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 RFQ Filter URL:", newUrl); // 페이지 완전 새로고침으로 서버 렌더링 강제 window.location.href = newUrl; // 마지막 적용된 필터 업데이트 lastAppliedFilters.current = JSON.stringify(newFilters); // 필터 업데이트 후 조회 핸들러 호출 (제공된 경우) if (onSearch) { console.log("Calling RFQ onSearch..."); onSearch(); } console.log("=== RFQ Filter Submit Complete ==="); } catch (error) { console.error("RFQ 필터 적용 오류:", error); } }) } // 필터 초기화 핸들러 async function handleReset() { try { setIsInitializing(true); form.reset({ rfqCode: "", projectCode: "", picName: "", packageNo: "", packageName: "", status: "", }); console.log("=== RFQ 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('rfqBasicFilters'); params.delete('basicJoinOperator'); params.delete('rfqBasicJoinOperator'); params.set('page', '1'); const newUrl = `${currentUrl.pathname}?${params.toString()}`; console.log("Reset URL:", newUrl); // 페이지 완전 새로고침 window.location.href = newUrl; // 마지막 적용된 필터 초기화 lastAppliedFilters.current = ""; console.log("RFQ 필터 초기화 완료"); setIsInitializing(false); } catch (error) { console.error("RFQ 필터 초기화 오류:", error); setIsInitializing(false); } } // Don't render if not open (for side panel use) if (!isOpen) { return null; } return (
{/* Filter Panel Header */}

RFQ 검색 필터

{getActiveFilterCount() > 0 && ( {getActiveFilterCount()}개 필터 적용됨 )}
{/* Join Operator Selection */}
{/* Scrollable content area */}
{/* RFQ 코드 */} ( RFQ 코드
{field.value && ( )}
)} /> {/* 프로젝트 코드 */} ( 프로젝트 코드
{field.value && ( )}
)} /> {/* 담당자명 */} ( 담당자명
{field.value && ( )}
)} /> {/* 패키지 번호 */} ( 패키지 번호
{field.value && ( )}
)} /> {/* 패키지명 */} ( 패키지명
{field.value && ( )}
)} /> {/* RFQ 상태 */} ( RFQ 상태 )} />
{/* Fixed buttons at bottom */}
) }