"use client" import * as React from "react" import { Check, ChevronsUpDown, Loader } from "lucide-react" import { Button } from "@/components/ui/button" import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem } from "@/components/ui/command" import { Popover, PopoverTrigger, PopoverContent } from "@/components/ui/popover" import { cn } from "@/lib/utils" import { useDebounce } from "@/hooks/use-debounce" import { getBudgetaryRfqs, type BudgetaryRfq } from "../service" import { RfqType } from "../validations" // ParentRfq 타입 정의 (서비스의 BudgetaryRfq와 호환되어야 함) interface ParentRfq { id: number; rfqCode: string; description: string | null; rfqType: RfqType; projectId: number | null; projectCode: string | null; projectName: string | null; } interface ParentRfqSelectorProps { selectedRfqId?: number; onRfqSelect: (rfq: ParentRfq | null) => void; rfqType: RfqType; // 현재 생성 중인 RFQ 타입 parentRfqTypes: RfqType[]; // 선택 가능한 부모 RFQ 타입 목록 placeholder?: string; } export function ParentRfqSelector({ selectedRfqId, onRfqSelect, rfqType, parentRfqTypes, placeholder = "부모 RFQ 선택..." }: ParentRfqSelectorProps) { const [searchTerm, setSearchTerm] = React.useState(""); const debouncedSearchTerm = useDebounce(searchTerm, 300); const [open, setOpen] = React.useState(false); const [loading, setLoading] = React.useState(false); const [parentRfqs, setParentRfqs] = React.useState([]); const [selectedRfq, setSelectedRfq] = React.useState(null); const [page, setPage] = React.useState(1); const [hasMore, setHasMore] = React.useState(true); const [totalCount, setTotalCount] = React.useState(0); const listRef = React.useRef(null); // 타입별로 적절한 검색 placeholder 생성 const getSearchPlaceholder = () => { if (rfqType === RfqType.PURCHASE) { return "BUDGETARY/PURCHASE_BUDGETARY RFQ 검색..."; } else if (rfqType === RfqType.PURCHASE_BUDGETARY) { return "BUDGETARY RFQ 검색..."; } return "RFQ 코드/설명/프로젝트 검색..."; }; // 초기 선택된 RFQ가 있을 경우 로드 React.useEffect(() => { if (selectedRfqId && open) { const loadSelectedRfq = async () => { try { // 단일 RFQ를 id로 조회하는 API 호출 const result = await getBudgetaryRfqs({ limit: 1, rfqId: selectedRfqId }); if ('rfqs' in result && result.rfqs && result.rfqs.length > 0) { setSelectedRfq(result.rfqs[0] as unknown as ParentRfq); } } catch (error) { console.error("선택된 RFQ 로드 오류:", error); } }; if (!selectedRfq || selectedRfq.id !== selectedRfqId) { loadSelectedRfq(); } } }, [selectedRfqId, open, selectedRfq]); // 검색어 변경 시 데이터 리셋 및 재로드 React.useEffect(() => { if (open) { setPage(1); setHasMore(true); setParentRfqs([]); loadParentRfqs(1, true); } }, [debouncedSearchTerm, open, parentRfqTypes]); // 데이터 로드 함수 const loadParentRfqs = async (pageToLoad: number, reset = false) => { if (!open || parentRfqTypes.length === 0) return; setLoading(true); try { const limit = 20; // 한 번에 로드할 항목 수 const result = await getBudgetaryRfqs({ search: debouncedSearchTerm, limit, offset: (pageToLoad - 1) * limit, rfqTypes: parentRfqTypes // 현재 RFQ 타입에 맞는 부모 RFQ 타입들로 필터링 }); if ('rfqs' in result && result.rfqs) { if (reset) { setParentRfqs(result.rfqs as unknown as ParentRfq[]); } else { setParentRfqs(prev => [...prev, ...(result.rfqs as unknown as ParentRfq[])]); } setTotalCount(result.totalCount); setHasMore(result.rfqs.length === limit && (pageToLoad * limit) < result.totalCount); setPage(pageToLoad); } } catch (error) { console.error("부모 RFQ 로드 오류:", error); } finally { setLoading(false); } }; // 무한 스크롤 처리 const handleScroll = () => { if (listRef.current) { const { scrollTop, scrollHeight, clientHeight } = listRef.current; // 스크롤이 90% 이상 내려갔을 때 다음 페이지 로드 if (scrollTop + clientHeight >= scrollHeight * 0.9 && !loading && hasMore) { loadParentRfqs(page + 1); } } }; // RFQ를 프로젝트별로 그룹화하는 함수 const groupRfqsByProject = (rfqs: ParentRfq[]) => { const groups: Record = {}; // 'No Project' 그룹 기본 생성 groups['no-project'] = { projectId: null, projectCode: null, projectName: null, rfqs: [] }; // 프로젝트별로 RFQ 그룹화 rfqs.forEach(rfq => { const key = rfq.projectId ? `project-${rfq.projectId}` : 'no-project'; if (!groups[key] && rfq.projectId) { groups[key] = { projectId: rfq.projectId, projectCode: rfq.projectCode, projectName: rfq.projectName, rfqs: [] }; } groups[key].rfqs.push(rfq); }); // 필터링된 결과가 있는 그룹만 남기기 return Object.values(groups).filter(group => group.rfqs.length > 0); }; // 그룹화된 RFQ 목록 const groupedRfqs = React.useMemo(() => { return groupRfqsByProject(parentRfqs); }, [parentRfqs]); // RFQ 선택 처리 const handleRfqSelect = (rfq: ParentRfq | null) => { setSelectedRfq(rfq); onRfqSelect(rfq); setOpen(false); }; // RFQ 타입에 따른 표시 형식 const getRfqTypeLabel = (type: RfqType) => { switch(type) { case RfqType.BUDGETARY: return "BUDGETARY"; case RfqType.PURCHASE_BUDGETARY: return "PURCHASE_BUDGETARY"; case RfqType.PURCHASE: return "PURCHASE"; default: return type; } }; return ( 검색 결과가 없습니다 handleRfqSelect(null)} > 선택 안함 {groupedRfqs.map((group, index) => ( {group.rfqs.map((rfq) => ( handleRfqSelect(rfq)} >
{rfq.rfqCode || ""} {getRfqTypeLabel(rfq.rfqType)}
{rfq.description && ( {rfq.description} )}
))}
))} {loading && (
)} {!loading && !hasMore && parentRfqs.length > 0 && (
총 {totalCount}개 중 {parentRfqs.length}개 표시됨
)}
); }