"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 { DateRangePicker } from "@/components/date-range-picker" 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" // nanoid 생성기 const generateId = customAlphabet("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 6) // 필터 스키마 정의 (TechSales RFQ에 맞게 수정) const filterSchema = z.object({ rfqCode: z.string().optional(), materialCode: z.string().optional(), itemName: z.string().optional(), pspid: z.string().optional(), projNm: z.string().optional(), ptypeNm: z.string().optional(), createdByName: z.string().optional(), status: z.string().optional(), dateRange: z.object({ from: z.date().optional(), to: z.date().optional(), }).optional(), }) // 상태 옵션 정의 (TechSales RFQ 상태에 맞게 수정) 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: "Closed", label: "Closed" }, ] 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: { rfqCode: "", materialCode: "", itemName: "", pspid: "", projNm: "", ptypeNm: "", createdByName: "", 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) { // eslint-disable-next-line @typescript-eslint/no-explicit-any (formValues as any)[filter.id] = filter.value; formUpdated = true; } }); // 폼 값이 변경된 경우에만 reset으로 한 번에 업데이트 if (formUpdated) { form.reset(formValues); lastAppliedFilters.current = currentFiltersString; } setIsInitializing(false); } }, [filters, isOpen, form]) // form 의존성 추가 // 현재 적용된 필터 카운트 const getActiveFilterCount = () => { return filters?.length || 0 } // 조회 버튼 클릭 핸들러 const handleSearch = () => { // 필터 패널 닫기 로직이 있다면 여기에 추가 if (onSearch) { onSearch(); } } // 폼 제출 핸들러 - 개선된 버전 async function onSubmit(data: FilterFormValues) { // 초기화 중이면 제출 방지 if (isInitializing) return; startTransition(async () => { try { // 필터 배열 생성 const newFilters = [] if (data.rfqCode?.trim()) { newFilters.push({ id: "rfqCode", value: data.rfqCode.trim(), type: "text" as const, operator: "iLike" as const, rowId: generateId() }) } if (data.materialCode?.trim()) { newFilters.push({ id: "materialCode", value: data.materialCode.trim(), type: "text" as const, operator: "iLike" as const, rowId: generateId() }) } if (data.itemName?.trim()) { newFilters.push({ id: "itemName", value: data.itemName.trim(), type: "text" as const, operator: "iLike" as const, rowId: generateId() }) } if (data.pspid?.trim()) { newFilters.push({ id: "pspid", value: data.pspid.trim(), type: "text" as const, operator: "iLike" as const, rowId: generateId() }) } if (data.projNm?.trim()) { newFilters.push({ id: "projNm", value: data.projNm.trim(), type: "text" as const, operator: "iLike" as const, rowId: generateId() }) } if (data.ptypeNm?.trim()) { newFilters.push({ id: "ptypeNm", value: data.ptypeNm.trim(), type: "text" as const, operator: "iLike" as const, rowId: generateId() }) } if (data.createdByName?.trim()) { newFilters.push({ id: "createdByName", value: data.createdByName.trim(), type: "text" as const, operator: "iLike" as const, rowId: generateId() }) } if (data.status?.trim()) { newFilters.push({ id: "status", value: data.status.trim(), type: "select" as const, operator: "eq" as const, 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) as string[], type: "date" as const, operator: "isBetween" as const, rowId: generateId() }) } console.log("기본 필터 적용:", newFilters); // 마지막 적용된 필터 업데이트 lastAppliedFilters.current = JSON.stringify(newFilters); // 먼저 필터를 설정 await setFilters(newFilters.length > 0 ? newFilters : null); // 그 다음 페이지를 1로 설정 await setPage("1"); // 필터 업데이트 후 조회 핸들러 호출 (제공된 경우) handleSearch(); // 페이지 새로고침으로 서버 데이터 다시 가져오기 setTimeout(() => { window.location.reload(); }, 100); } catch (error) { console.error("필터 적용 오류:", error); } }) } // 필터 초기화 핸들러 - 개선된 버전 async function handleReset() { try { setIsInitializing(true); form.reset({ rfqCode: "", materialCode: "", itemName: "", pspid: "", projNm: "", ptypeNm: "", createdByName: "", status: "", dateRange: { from: undefined, to: undefined }, }); // 필터와 조인 연산자를 초기화 await setFilters(null); await setJoinOperator("and"); await setPage("1"); // 마지막 적용된 필터 초기화 lastAppliedFilters.current = ""; console.log("필터 초기화 완료"); setIsInitializing(false); // 페이지 새로고침으로 서버 데이터 다시 가져오기 setTimeout(() => { window.location.reload(); }, 100); } catch (error) { console.error("필터 초기화 오류:", error); setIsInitializing(false); } } // Don't render if not open (for side panel use) if (!isOpen) { return null; } return (
{/* Filter Panel Header - 보더 제거, 배경 색상 적용 */}

검색 필터

{/* Join Operator Selection - 보더 제거, 배경 색상 적용 */}
{/* Scrollable content area - 헤더와 버튼 사이에서 스크롤 */}
{/* RFQ NO. */} ( {t("RFQ NO.")}
{field.value && ( )}
)} /> {/* 자재그룹 */} ( {t("자재그룹")}
{field.value && ( )}
)} /> {/* 자재명 */} ( {t("자재명")}
{field.value && ( )}
)} /> {/* 프로젝트 ID */} ( {t("프로젝트 ID")}
{field.value && ( )}
)} /> {/* 프로젝트명 */} ( {t("프로젝트명")}
{field.value && ( )}
)} /> {/* 선종명 */} ( {t("선종명")}
{field.value && ( )}
)} /> {/* 요청자 */} ( {t("요청자")}
{field.value && ( )}
)} /> {/* Status */} ( {t("Status")} )} /> {/* RFQ 전송일 */} ( {t("RFQ 전송일")}
{(field.value?.from || field.value?.to) && ( )}
)} />
{/* Fixed buttons at bottom - 보더 제거, 배경 색상 적용 */}
) }