From b75b1cd920efd61923f7b2dbc4c49987b7b0c4e1 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Thu, 20 Nov 2025 10:25:41 +0000 Subject: (최겸) 구매 입찰 수정 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/bidding/ProjectSelectorBid.tsx | 183 ++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 components/bidding/ProjectSelectorBid.tsx (limited to 'components/bidding/ProjectSelectorBid.tsx') diff --git a/components/bidding/ProjectSelectorBid.tsx b/components/bidding/ProjectSelectorBid.tsx new file mode 100644 index 00000000..de9e435e --- /dev/null +++ b/components/bidding/ProjectSelectorBid.tsx @@ -0,0 +1,183 @@ +"use client" + +import * as React from "react" +import { Check, ChevronsUpDown, X } from "lucide-react" +import { Button } from "@/components/ui/button" +import { Popover, PopoverTrigger, PopoverContent } from "@/components/ui/popover" +import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem } from "@/components/ui/command" +import { cn } from "@/lib/utils" +import { getProjects, type Project } from "@/lib/rfqs/service" + +interface ProjectSelectorProps { + selectedProjectId?: number | null; + onProjectSelect: (project: Project) => void; + placeholder?: string; + filterType?: string; // 옵션으로 필터 타입 지정 가능 +} + +export function ProjectSelector({ + selectedProjectId, + onProjectSelect, + placeholder = "프로젝트 선택...", + filterType +}: ProjectSelectorProps) { + const [open, setOpen] = React.useState(false) + const [searchTerm, setSearchTerm] = React.useState("") + const [projects, setProjects] = React.useState([]) + const [isLoading, setIsLoading] = React.useState(false) + const [selectedProject, setSelectedProject] = React.useState(null) + + // 모든 프로젝트 데이터 로드 후 plant 타입만 필터링 + React.useEffect(() => { + async function loadAllProjects() { + setIsLoading(true); + try { + const allProjects = await getProjects(); + + // filterType이 지정된 경우 해당 타입만 필터링 + const filteredByType = filterType + ? allProjects.filter(p => p.type === filterType) + : allProjects; + + console.log(`Loaded ${filteredByType.length} ${filterType || 'all'} projects`); + setProjects(filteredByType); + + // 초기 선택된 프로젝트가 있으면 설정 + if (selectedProjectId) { + const selected = filteredByType.find(p => p.id === selectedProjectId); + if (selected) { + setSelectedProject(selected); + } + } + } catch (error) { + console.error("프로젝트 목록 로드 오류:", error); + } finally { + setIsLoading(false); + } + } + + loadAllProjects(); + }, [selectedProjectId, filterType]); + + // 클라이언트 측에서 검색어로 필터링 + const filteredProjects = React.useMemo(() => { + if (!searchTerm.trim()) return projects; + + const lowerSearch = searchTerm.toLowerCase(); + return projects.filter( + project => + project.projectCode.toLowerCase().includes(lowerSearch) || + project.projectName.toLowerCase().includes(lowerSearch) + ); + }, [projects, searchTerm]); + + // 프로젝트 선택 처리 + const handleSelectProject = (project: Project) => { + // 이미 선택된 프로젝트를 다시 선택하면 선택 해제 + if (selectedProject?.id === project.id) { + setSelectedProject(null); + onProjectSelect(null as any); // 선택 해제를 위해 null 전달 + setOpen(false); + return; + } + + setSelectedProject(project); + onProjectSelect(project); + setOpen(false); + }; + + // 프로젝트 선택 해제 + const handleClearSelection = (e: React.MouseEvent) => { + e.stopPropagation(); // Popover가 열리지 않도록 방지 + setSelectedProject(null); + onProjectSelect(null as any); // 선택 해제를 위해 null 전달 + }; + + return ( + + + + + + + ) : ( +
+ {placeholder} + +
+ )} + +
+ + + + { + e.stopPropagation(); + const target = e.currentTarget; + target.scrollTop += e.deltaY; + }} + > + {isLoading ? ( +
로딩 중...
+ ) : filteredProjects.length === 0 ? ( + + {searchTerm + ? "검색 결과가 없습니다" + : `${filterType || '해당 타입의'} 프로젝트가 없습니다`} + + ) : ( + + {filteredProjects.map((project) => ( + handleSelectProject(project)} + > + + {project.projectCode} + - {project.projectName} + {selectedProject?.id === project.id && ( + (선택됨) + )} + + ))} + + )} +
+
+
+
+ ); +} \ No newline at end of file -- cgit v1.2.3