diff options
| author | joonhoekim <26rote@gmail.com> | 2025-11-27 17:53:34 +0900 |
|---|---|---|
| committer | joonhoekim <26rote@gmail.com> | 2025-11-27 17:53:34 +0900 |
| commit | 5870b73785715d1585531e655c06d8c068eb64ac (patch) | |
| tree | 1d19e1482f5210cc56e778158b51e810f9717c46 /components/vendor-data-plant/project-swicher.tsx | |
| parent | 95984e67b8d57fbe1431fcfedf3bb682f28416b3 (diff) | |
(김준회) Revert "(대표님) EDP 작업사항"
태그 가져오기 실패 등 에러로 인한 Revert 처리
Diffstat (limited to 'components/vendor-data-plant/project-swicher.tsx')
| -rw-r--r-- | components/vendor-data-plant/project-swicher.tsx | 163 |
1 files changed, 104 insertions, 59 deletions
diff --git a/components/vendor-data-plant/project-swicher.tsx b/components/vendor-data-plant/project-swicher.tsx index 9b8f9bea..d3123709 100644 --- a/components/vendor-data-plant/project-swicher.tsx +++ b/components/vendor-data-plant/project-swicher.tsx @@ -1,7 +1,6 @@ "use client" import * as React from "react" -import { Check, ChevronsUpDown } from "lucide-react" import { cn } from "@/lib/utils" import { Button } from "@/components/ui/button" import { @@ -17,103 +16,149 @@ import { PopoverContent, PopoverTrigger, } from "@/components/ui/popover" +import { Check, ChevronsUpDown, Loader2 } from "lucide-react" -interface PackageData { - packageCode: string - packageName: string | null +interface ContractInfo { + contractId: number + contractName: string } -interface ProjectData { +interface ProjectInfo { projectId: number projectCode: string projectName: string - projectType: string - packages: PackageData[] + contracts: ContractInfo[] } interface ProjectSwitcherProps { isCollapsed: boolean - projects: ProjectData[] - selectedProjectId: number - selectedPackageCode: string | null - onSelectPackage: (projectId: number, packageCode: string) => void + projects: ProjectInfo[] + + // 상위가 관리하는 "현재 선택된 contractId" + selectedContractId: number | null + + // 콜백: 사용자가 "어떤 contract"를 골랐는지 + // => 우리가 projectId도 찾아서 상위 state를 같이 갱신해야 함 + onSelectContract: (projectId: number, contractId: number) => void + + // 로딩 상태 (선택사항) + isLoading?: boolean } export function ProjectSwitcher({ isCollapsed, projects, - selectedProjectId, - selectedPackageCode, - onSelectPackage, + selectedContractId, + onSelectContract, + isLoading = false, }: ProjectSwitcherProps) { - const [open, setOpen] = React.useState(false) + const [popoverOpen, setPopoverOpen] = React.useState(false) + const [searchTerm, setSearchTerm] = React.useState("") - // 현재 선택된 프로젝트와 패키지 정보 - const selectedProject = projects.find(p => p.projectId === selectedProjectId) - const selectedPackage = selectedProject?.packages.find( - pkg => pkg.packageCode === selectedPackageCode - ) + // 현재 선택된 contract 객체 찾기 + const selectedContract = React.useMemo(() => { + if (!selectedContractId) return null + for (const proj of projects) { + const found = proj.contracts.find((c) => c.contractId === selectedContractId) + if (found) { + return { ...found, projectId: proj.projectId, projectName: proj.projectName } + } + } + return null + }, [projects, selectedContractId]) + + // Trigger label => 계약 이름 or placeholder + const triggerLabel = selectedContract?.contractName ?? "Select a contract" + // 검색어에 따른 필터링된 프로젝트/계약 목록 + const filteredProjects = React.useMemo(() => { + if (!searchTerm) return projects + + return projects.map(project => ({ + ...project, + contracts: project.contracts.filter(contract => + contract.contractName.toLowerCase().includes(searchTerm.toLowerCase()) || + project.projectName.toLowerCase().includes(searchTerm.toLowerCase()) + ) + })).filter(project => project.contracts.length > 0) + }, [projects, searchTerm]) - console.log(projects,"projects") + // 계약 선택 핸들러 + function handleSelectContract(projectId: number, contractId: number) { + onSelectContract(projectId, contractId) + setPopoverOpen(false) + setSearchTerm("") // 검색어 초기화 + } - const displayText = selectedPackage - ? `${selectedProject?.projectCode} - ${selectedPackage.packageCode}` - : selectedProject?.projectCode || "Select Package" + // 총 계약 수 계산 (빈 상태 표시용) + const totalContracts = filteredProjects.reduce((sum, project) => sum + project.contracts.length, 0) return ( - <Popover open={open} onOpenChange={setOpen}> + <Popover open={popoverOpen} onOpenChange={setPopoverOpen}> <PopoverTrigger asChild> <Button + type="button" variant="outline" - role="combobox" - aria-expanded={open} - aria-label="Select a package" - className={cn("w-full justify-between", isCollapsed && "w-[50px]")} + className={cn( + "justify-between relative", + isCollapsed ? "h-9 w-9 shrink-0 items-center justify-center p-0" : "w-full h-9" + )} + disabled={isLoading} + aria-label="Select Contract" > - {isCollapsed ? ( - <ChevronsUpDown className="h-4 w-4" /> + {isLoading ? ( + <> + <span className={cn(isCollapsed && "hidden")}>Loading...</span> + <Loader2 className={cn("h-4 w-4 animate-spin", !isCollapsed && "ml-2")} /> + </> ) : ( <> - <span className="truncate">{displayText}</span> - <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" /> + <span className={cn("truncate flex-grow text-left", isCollapsed && "hidden")}> + {triggerLabel} + </span> + <ChevronsUpDown className={cn("h-4 w-4 opacity-50 flex-shrink-0", isCollapsed && "hidden")} /> </> )} </Button> </PopoverTrigger> - <PopoverContent className="w-[300px] p-0"> + + <PopoverContent className="w-[320px] p-0" align="start"> <Command> - <CommandInput placeholder="Search package..." /> - <CommandList> - <CommandEmpty>No package found.</CommandEmpty> - {projects.map((project) => ( - <CommandGroup key={project.projectId} heading={project.projectName}> - {project.packages.map((pkg) => ( + <CommandInput + placeholder="Search contracts..." + value={searchTerm} + onValueChange={setSearchTerm} + /> + + <CommandList + className="max-h-[320px]" + onWheel={(e) => { + e.stopPropagation() // 이벤트 전파 차단 + const target = e.currentTarget + target.scrollTop += e.deltaY // 직접 스크롤 처리 + }} + > + <CommandEmpty> + {totalContracts === 0 ? "No contracts found." : "No search results."} + </CommandEmpty> + + {filteredProjects.map((project) => ( + <CommandGroup key={project.projectCode} heading={project.projectName}> + {project.contracts.map((contract) => ( <CommandItem - key={`${project.projectId}-${pkg.packageCode}`} - onSelect={() => { - onSelectPackage(project.projectId, pkg.packageCode) - setOpen(false) - }} - className="text-sm" + key={contract.contractId} + onSelect={() => handleSelectContract(project.projectId, contract.contractId)} + value={`${project.projectName} ${contract.contractName}`} + className="truncate" + title={contract.contractName} > + <span className="truncate">{contract.contractName}</span> <Check className={cn( - "mr-2 h-4 w-4", - selectedProjectId === project.projectId && - selectedPackageCode === pkg.packageCode - ? "opacity-100" - : "opacity-0" + "ml-auto h-4 w-4 flex-shrink-0", + selectedContractId === contract.contractId ? "opacity-100" : "opacity-0" )} /> - <div className="flex flex-col"> - <span className="font-medium">{pkg.packageCode}</span> - {pkg.packageName && ( - <span className="text-xs text-muted-foreground"> - {pkg.packageName} - </span> - )} - </div> </CommandItem> ))} </CommandGroup> |
