"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) // 필터 스키마 정의 (RFQ 관련 항목 유지) const filterSchema = z.object({ picCode: z.string().optional(), projectCode: z.string().optional(), rfqCode: z.string().optional(), itemCode: z.string().optional(), majorItemMaterialCode: z.string().optional(), status: z.string().optional(), dateRange: z.object({ from: z.date().optional(), to: z.date().optional(), }).optional(), }) // 상태 옵션 정의 const statusOptions = [ { value: "RFQ Created", label: "RFQ Created" }, { value: "RFQ Vendor Assignned", label: "RFQ Vendor Assignned" }, { value: "RFQ Sent", label: "RFQ Sent" }, { value: "Quotation Analysis", label: "Quotation Analysis" }, { value: "PO Transfer", label: "PO Transfer" }, { value: "PO Create", label: "PO Create" }, ] type FilterFormValues = z.infer interface RFQFilterSheetProps { isOpen: boolean; onClose: () => void; onSearch?: () => void; isLoading?: boolean; } // Updated component for inline use (not a sheet anymore) export function RFQFilterSheet({ isOpen, onClose, onSearch, isLoading = false }: RFQFilterSheetProps) { 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 상태 관리 - 파라미터명을 'basicFilters'로 변경 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(filterSchema), defaultValues: { picCode: "", projectCode: "", rfqCode: "", itemCode: "", majorItemMaterialCode: "", status: "", dateRange: { 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 === "rfqSendDate" && Array.isArray(filter.value) && filter.value.length > 0) { formValues.dateRange = { 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]) // form 의존성 제거 // 현재 적용된 필터 카운트 const getActiveFilterCount = () => { return filters?.length || 0 } // 폼 제출 핸들러 - PQ 방식으로 수정 (수동 URL 업데이트 버전) async function onSubmit(data: FilterFormValues) { // 초기화 중이면 제출 방지 if (isInitializing) return; startTransition(async () => { try { // 필터 배열 생성 const newFilters = [] if (data.picCode?.trim()) { newFilters.push({ id: "picCode", value: data.picCode.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.rfqCode?.trim()) { newFilters.push({ id: "rfqCode", value: data.rfqCode.trim(), type: "text", operator: "iLike", rowId: generateId() }) } if (data.itemCode?.trim()) { newFilters.push({ id: "itemCode", value: data.itemCode.trim(), type: "text", operator: "iLike", rowId: generateId() }) } if (data.majorItemMaterialCode?.trim()) { newFilters.push({ id: "majorItemMaterialCode", value: data.majorItemMaterialCode.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() }) } // Add date range to params if it exists if (data.dateRange?.from) { newFilters.push({ id: "rfqSendDate", value: [ data.dateRange.from.toISOString().split('T')[0], data.dateRange.to ? data.dateRange.to.toISOString().split('T')[0] : undefined ].filter(Boolean), type: "date", operator: "isBetween", rowId: generateId() }) } console.log("=== RFQ Filter Submit Debug ==="); console.log("Generated filters:", newFilters); console.log("Join operator:", joinOperator); // 🔑 PQ 방식: 수동으로 URL 업데이트 (nuqs 대신) const currentUrl = new URL(window.location.href); const params = new URLSearchParams(currentUrl.search); // 기존 필터 관련 파라미터 제거 params.delete('basicFilters'); params.delete('basicJoinOperator'); 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); // 🔑 PQ 방식: 페이지 완전 새로고침으로 서버 렌더링 강제 window.location.href = newUrl; // 마지막 적용된 필터 업데이트 lastAppliedFilters.current = JSON.stringify(newFilters); // 필터 업데이트 후 조회 핸들러 호출 (제공된 경우) if (onSearch) { console.log("Calling onSearch..."); onSearch(); } console.log("=== RFQ Filter Submit Complete ==="); } catch (error) { console.error("RFQ 필터 적용 오류:", error); } }) } // 필터 초기화 핸들러 - PQ 방식으로 수정 async function handleReset() { try { setIsInitializing(true); form.reset({ picCode: "", projectCode: "", rfqCode: "", itemCode: "", majorItemMaterialCode: "", status: "", dateRange: { from: undefined, to: undefined }, }); console.log("=== RFQ Filter Reset Debug ==="); console.log("Current URL before reset:", window.location.href); // 🔑 PQ 방식: 수동으로 URL 초기화 const currentUrl = new URL(window.location.href); const params = new URLSearchParams(currentUrl.search); // 필터 관련 파라미터 제거 params.delete('basicFilters'); params.delete('basicJoinOperator'); params.set('page', '1'); const newUrl = `${currentUrl.pathname}?${params.toString()}`; console.log("Reset URL:", newUrl); // 🔑 PQ 방식: 페이지 완전 새로고침 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 */}

검색 필터

{getActiveFilterCount() > 0 && ( {getActiveFilterCount()}개 필터 적용됨 )}
{/* Join Operator Selection */}
{/* Scrollable content area - 헤더와 버튼 사이에서 스크롤 */}
{/* 발주 담당 */} ( {t("발주담당")}
{field.value && ( )}
)} /> {/* 프로젝트 코드 */} ( {t("프로젝트 코드")}
{field.value && ( )}
)} /> {/* RFQ NO. */} ( {t("RFQ NO.")}
{field.value && ( )}
)} /> {/* 자재그룹 */} ( {t("자재그룹")}
{field.value && ( )}
)} /> {/* 자재코드 */} ( {t("자재코드")}
{field.value && ( )}
)} /> {/* Status */} ( {t("Status")} )} /> {/* RFQ 전송일 */} ( {t("RFQ 전송일")}
{(field.value?.from || field.value?.to) && ( )}
)} />
{/* Fixed buttons at bottom */}
) }