summaryrefslogtreecommitdiff
path: root/components/pq/project-select.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'components/pq/project-select.tsx')
-rw-r--r--components/pq/project-select.tsx173
1 files changed, 173 insertions, 0 deletions
diff --git a/components/pq/project-select.tsx b/components/pq/project-select.tsx
new file mode 100644
index 00000000..0d6e6445
--- /dev/null
+++ b/components/pq/project-select.tsx
@@ -0,0 +1,173 @@
+"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, CommandSeparator } 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 | null) => void;
+ placeholder?: string;
+ showClearOption?: boolean;
+ clearOptionText?: string;
+}
+
+export function ProjectSelector({
+ selectedProjectId,
+ onProjectSelect,
+ placeholder = "프로젝트 선택...",
+ showClearOption = true,
+ clearOptionText = "일반 PQ 보기"
+}: 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 getProjects();
+ 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);
+ };
+
+ // 선택 해제 처리
+ const handleClearSelection = () => {
+ setSelectedProject(null);
+ onProjectSelect(null);
+ setOpen(false);
+ };
+
+ return (
+ <div className="space-y-1">
+ {/* 선택된 프로젝트 정보 표시 (선택된 경우에만) */}
+ {selectedProject && (
+ <div className="flex items-center justify-between px-2">
+ <div className="flex flex-col">
+ <div className="text-sm font-medium">{selectedProject.projectCode}</div>
+ <div className="text-xs text-muted-foreground truncate max-w-[300px]">
+ {selectedProject.projectName}
+ </div>
+ </div>
+ <Button
+ variant="ghost"
+ size="sm"
+ className="h-7 w-7 p-0 text-muted-foreground hover:text-destructive"
+ onClick={handleClearSelection}
+ >
+ <X className="h-4 w-4" />
+ <span className="sr-only">선택 해제</span>
+ </Button>
+ </div>
+ )}
+
+ {/* 셀렉터 컴포넌트 */}
+ <Popover open={open} onOpenChange={setOpen}>
+ <PopoverTrigger asChild>
+ <Button
+ variant="outline"
+ role="combobox"
+ aria-expanded={open}
+ className="w-full justify-between"
+ >
+ {selectedProject ? "프로젝트 변경..." : 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>
+
+ {showClearOption && selectedProject && (
+ <>
+ <CommandGroup>
+ <CommandItem
+ onSelect={handleClearSelection}
+ className="text-blue-600 font-medium"
+ >
+ {clearOptionText}
+ </CommandItem>
+ </CommandGroup>
+ <CommandSeparator />
+ </>
+ )}
+
+ {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>
+ </div>
+ );
+} \ No newline at end of file