"use client" import { useEffect, useTransition, useState, useRef } from "react" 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" import { Checkbox } from "@/components/ui/checkbox" // nanoid 생성기 const generateId = customAlphabet("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 6) // 필터 스키마 정의 (기술영업 벤더에 맞게 수정) const filterSchema = z.object({ vendorCode: z.string().optional(), vendorName: z.string().optional(), country: z.string().optional(), status: z.string().optional(), techVendorType: z.array(z.string()).optional(), workTypes: z.array(z.string()).optional(), }) // 상태 옵션 정의 const statusOptions = [ { value: "ACTIVE", label: "활성 상태" }, { value: "INACTIVE", label: "비활성 상태" }, { value: "BLACKLISTED", label: "거래 금지" }, { value: "PENDING_INVITE", label: "초대 대기" }, { value: "INVITED", label: "초대 완료" }, { value: "QUOTE_COMPARISON", label: "견적 비교" }, ] // 벤더 타입 옵션 const vendorTypeOptions = [ { value: "조선", label: "조선" }, { value: "해양TOP", label: "해양TOP" }, { value: "해양HULL", label: "해양HULL" }, ] // 공종 옵션 const workTypeOptions = [ // 조선 workTypes { value: "기장", label: "기장" }, { value: "전장", label: "전장" }, { value: "선실", label: "선실" }, { value: "배관", label: "배관" }, { value: "철의", label: "철의" }, { value: "선체", label: "선체" }, // 해양TOP workTypes { value: "TM", label: "TM" }, { value: "TS", label: "TS" }, { value: "TE", label: "TE" }, { value: "TP", label: "TP" }, { value: "TA", label: "TA" }, // 해양HULL workTypes { value: "HA", label: "HA" }, { value: "HE", label: "HE" }, { value: "HH", label: "HH" }, { value: "HM", label: "HM" }, { value: "NC", label: "NC" }, { value: "HO", label: "HO" }, { value: "HP", label: "HP" }, ] type FilterFormValues = z.infer interface TechVendorsFilterSheetProps { isOpen: boolean; onClose: () => void; onSearch?: () => void; isLoading?: boolean; } export function TechVendorsFilterSheet({ isOpen, onSearch, isLoading = false }: TechVendorsFilterSheetProps) { const [isPending, startTransition] = useTransition() // 초기화 상태 추가 - 폼 초기화 중에는 상태 변경을 방지 const [isInitializing, setIsInitializing] = useState(false) // 마지막으로 적용된 필터를 추적하기 위한 ref const lastAppliedFilters = useRef("") // nuqs로 URL 상태 관리 - 파라미터명을 'filters'로 변경하여 searchParamsCache와 일치 const [filters] = useQueryState( "filters", getFiltersStateParser().withDefault([]) ) // joinOperator 설정 const [joinOperator, setJoinOperator] = useQueryState( "joinOperator", parseAsStringEnum(["and", "or"]).withDefault("and") ) // 폼 상태 초기화 const form = useForm({ resolver: zodResolver(filterSchema), defaultValues: { vendorCode: "", vendorName: "", country: "", status: "", techVendorType: [], workTypes: [], }, }) // 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 === "techVendorType" && Array.isArray(filter.value)) { formValues.techVendorType = filter.value; formUpdated = true; } else if (filter.id === "workTypes" && Array.isArray(filter.value)) { formValues.workTypes = filter.value; 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; } }); if (formUpdated) { form.reset(formValues); lastAppliedFilters.current = currentFiltersString; } setIsInitializing(false); } }, [filters, isOpen, form]) // 현재 적용된 필터 카운트 const getActiveFilterCount = () => { return filters?.length || 0 } // 폼 제출 핸들러 async function onSubmit(data: FilterFormValues) { if (isInitializing) return; startTransition(async () => { try { const newFilters = [] 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.country?.trim()) { newFilters.push({ id: "country", value: data.country.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.techVendorType && data.techVendorType.length > 0) { newFilters.push({ id: "techVendorType", value: data.techVendorType, type: "multi-select", operator: "eq", rowId: generateId() }) } if (data.workTypes && data.workTypes.length > 0) { newFilters.push({ id: "workTypes", value: data.workTypes, type: "multi-select", operator: "eq", rowId: generateId() }) } // URL 수동 업데이트 const currentUrl = new URL(window.location.href); const params = new URLSearchParams(currentUrl.search); // 기존 필터 관련 파라미터 제거 params.delete('filters'); params.delete('joinOperator'); params.delete('page'); // 새로운 필터 추가 if (newFilters.length > 0) { params.set('filters', JSON.stringify(newFilters)); params.set('joinOperator', joinOperator); } // 페이지를 1로 설정 params.set('page', '1'); const newUrl = `${currentUrl.pathname}?${params.toString()}`; // 페이지 완전 새로고침 window.location.href = newUrl; // 마지막 적용된 필터 업데이트 lastAppliedFilters.current = JSON.stringify(newFilters); if (onSearch) { onSearch(); } } catch (error) { console.error("벤더 필터 적용 오류:", error); } }) } // 필터 초기화 핸들러 async function handleReset() { try { setIsInitializing(true); form.reset({ vendorCode: "", vendorName: "", country: "", status: "", techVendorType: [], workTypes: [], }); const currentUrl = new URL(window.location.href); const params = new URLSearchParams(currentUrl.search); params.delete('filters'); params.delete('joinOperator'); params.set('page', '1'); const newUrl = `${currentUrl.pathname}?${params.toString()}`; window.location.href = newUrl; lastAppliedFilters.current = ""; setIsInitializing(false); } catch (error) { console.error("벤더 필터 초기화 오류:", error); setIsInitializing(false); } } if (!isOpen) { return null; } return (
{/* Filter Panel Header */}

벤더 검색 필터

{getActiveFilterCount() > 0 && ( {getActiveFilterCount()}개 필터 적용됨 )}
{/* Join Operator Selection */}
{/* Scrollable content area */}
{/* 벤더코드 */} ( 벤더코드
{field.value && ( )}
)} /> {/* 벤더명 */} ( 벤더명
{field.value && ( )}
)} /> {/* 국가 */} ( 국가
{field.value && ( )}
)} /> {/* 상태 */} ( 상태 )} /> {/* 벤더 타입 */} ( 벤더 타입
{vendorTypeOptions.map((option) => (
{ const updatedValue = checked ? [...(field.value || []), option.value] : (field.value || []).filter((value) => value !== option.value); field.onChange(updatedValue); }} disabled={isInitializing} />
))}
)} /> {/* 공종 */} ( 공종
{workTypeOptions.map((option) => (
{ const updatedValue = checked ? [...(field.value || []), option.value] : (field.value || []).filter((value) => value !== option.value); field.onChange(updatedValue); }} disabled={isInitializing} />
))}
)} />
{/* Action buttons */}
) }