diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-04-28 02:13:30 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-04-28 02:13:30 +0000 |
| commit | ef4c533ebacc2cdc97e518f30e9a9350004fcdfb (patch) | |
| tree | 345251a3ed0f4429716fa5edaa31024d8f4cb560 /components/BidProjectSelector.tsx | |
| parent | 9ceed79cf32c896f8a998399bf1b296506b2cd4a (diff) | |
~20250428 작업사항
Diffstat (limited to 'components/BidProjectSelector.tsx')
| -rw-r--r-- | components/BidProjectSelector.tsx | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/components/BidProjectSelector.tsx b/components/BidProjectSelector.tsx new file mode 100644 index 00000000..8e229b10 --- /dev/null +++ b/components/BidProjectSelector.tsx @@ -0,0 +1,124 @@ +"use client" + +import * as React from "react" +import { Check, ChevronsUpDown } 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 { getBidProjects, type Project } from "@/lib/rfqs/service" + +interface ProjectSelectorProps { + selectedProjectId?: number | null; + onProjectSelect: (project: Project) => void; + placeholder?: string; +} + +export function EstimateProjectSelector ({ + selectedProjectId, + onProjectSelect, + placeholder = "프로젝트 선택..." +}: ProjectSelectorProps) { + const [open, setOpen] = React.useState(false) + const [searchTerm, setSearchTerm] = React.useState("") + const [projects, setProjects] = React.useState<Project[]>([]) + const [isLoading, setIsLoading] = React.useState(false) + const [selectedProject, setSelectedProject] = React.useState<Project | null>(null) + + // 모든 프로젝트 데이터 로드 (한 번만) + React.useEffect(() => { + async function loadAllProjects() { + setIsLoading(true); + try { + const allProjects = await getBidProjects(); + setProjects(allProjects); + + // 초기 선택된 프로젝트가 있으면 설정 + if (selectedProjectId) { + const selected = allProjects.find(p => p.id === selectedProjectId); + if (selected) { + setSelectedProject(selected); + } + } + } catch (error) { + console.error("프로젝트 목록 로드 오류:", error); + } finally { + setIsLoading(false); + } + } + + loadAllProjects(); + }, [selectedProjectId]); + + // 클라이언트 측에서 검색어로 필터링 + 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) => { + setSelectedProject(project); + onProjectSelect(project); + setOpen(false); + }; + + return ( + <Popover open={open} onOpenChange={setOpen}> + <PopoverTrigger asChild> + <Button + variant="outline" + role="combobox" + aria-expanded={open} + className="w-full justify-between" + > + {selectedProject + ? `${selectedProject.projectCode} - ${selectedProject.projectName}` + : placeholder} + <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" /> + </Button> + </PopoverTrigger> + <PopoverContent className="w-[400px] p-0"> + <Command> + <CommandInput + placeholder="프로젝트 코드/이름 검색..." + onValueChange={setSearchTerm} + /> + <CommandList className="max-h-[300px]"> + <CommandEmpty>검색 결과가 없습니다</CommandEmpty> + {isLoading ? ( + <div className="py-6 text-center text-sm">로딩 중...</div> + ) : ( + <CommandGroup> + {filteredProjects.map((project) => ( + <CommandItem + key={project.id} + value={`${project.projectCode} ${project.projectName}`} + onSelect={() => handleSelectProject(project)} + > + <Check + className={cn( + "mr-2 h-4 w-4", + selectedProject?.id === project.id + ? "opacity-100" + : "opacity-0" + )} + /> + <span className="font-medium">{project.projectCode}</span> + <span className="ml-2 text-gray-500 truncate">- {project.projectName}</span> + </CommandItem> + ))} + </CommandGroup> + )} + </CommandList> + </Command> + </PopoverContent> + </Popover> + ); +}
\ No newline at end of file |
