summaryrefslogtreecommitdiff
path: root/components/PackageSelector.tsx
blob: 9ad2727cad327fe0127accff9e30a8e089399028 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
"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 { getPackagesByProject, type PackageItem } from "@/lib/items/service"

interface PackageSelectorProps {
  projectNo?: string | null;
  selectedPackage?: PackageItem | null;
  onPackageSelect: (packageItem: PackageItem) => void;
  placeholder?: string;
  disabled?: boolean;
}

export function PackageSelector({ 
  projectNo,
  selectedPackage, 
  onPackageSelect, 
  placeholder = "패키지 선택...",
  disabled = false
}: PackageSelectorProps) {
  const [open, setOpen] = React.useState(false)
  const [searchTerm, setSearchTerm] = React.useState("")
  const [packages, setPackages] = React.useState<PackageItem[]>([])
  const [isLoading, setIsLoading] = React.useState(false)

  // 프로젝트가 변경되면 패키지 목록 로드
  React.useEffect(() => {
    if (projectNo) {
      loadPackages(projectNo);
    } else {
      setPackages([]);
    }
  }, [projectNo]);

  const loadPackages = async (projNo: string) => {
    setIsLoading(true);
    try {
      // 서버 액션 호출
      const packageList = await getPackagesByProject(projNo);
      setPackages(packageList);
    } catch (error) {
      console.error("패키지 목록 로드 오류:", error);
      setPackages([]);
    } finally {
      setIsLoading(false);
    }
  };

  // 클라이언트 측에서 검색어로 필터링
  const filteredPackages = React.useMemo(() => {
    if (!searchTerm.trim()) return packages;
    
    const lowerSearch = searchTerm.toLowerCase();
    return packages.filter(
      pkg => 
        pkg.packageCode.toLowerCase().includes(lowerSearch) || 
        (pkg.description && pkg.description.toLowerCase().includes(lowerSearch))
    );
  }, [packages, searchTerm]);

  // 패키지 선택 처리
  const handleSelectPackage = (pkg: PackageItem) => {
    onPackageSelect(pkg);
    setOpen(false);
  };

  return (
    <Popover open={open} onOpenChange={setOpen}>
      <PopoverTrigger asChild>
        <Button
          variant="outline"
          role="combobox"
          aria-expanded={open}
          className="w-full justify-between"
          disabled={disabled || !projectNo}
        >
          {selectedPackage
            ? `${selectedPackage.packageCode} - ${selectedPackage.description}`
            : (projectNo ? 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]" onWheel={(e) => {
            e.stopPropagation();
            const target = e.currentTarget;
            target.scrollTop += e.deltaY;
          }}>
            <CommandEmpty>검색 결과가 없습니다</CommandEmpty>
            {isLoading ? (
              <div className="py-6 text-center text-sm">로딩 중...</div>
            ) : (
              <CommandGroup>
                {filteredPackages.map((pkg) => (
                  <CommandItem
                    key={pkg.packageCode}
                    value={`${pkg.packageCode} ${pkg.description}`}
                    onSelect={() => handleSelectPackage(pkg)}
                  >
                    <Check
                      className={cn(
                        "mr-2 h-4 w-4",
                        selectedPackage?.packageCode === pkg.packageCode
                          ? "opacity-100"
                          : "opacity-0"
                      )}
                    />
                    <span className="font-medium">{pkg.packageCode}</span>
                    <span className="ml-2 text-gray-500 truncate">- {pkg.description}</span>
                  </CommandItem>
                ))}
              </CommandGroup>
            )}
          </CommandList>
        </Command>
      </PopoverContent>
    </Popover>
  );
}