diff options
Diffstat (limited to 'lib/risk-management/table/user-combo-box.tsx')
| -rw-r--r-- | lib/risk-management/table/user-combo-box.tsx | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/lib/risk-management/table/user-combo-box.tsx b/lib/risk-management/table/user-combo-box.tsx new file mode 100644 index 00000000..e319b538 --- /dev/null +++ b/lib/risk-management/table/user-combo-box.tsx @@ -0,0 +1,127 @@ +'use client'; + +/* IMPORT */ +import { Button } from '@/components/ui/button'; +import { Check, ChevronsUpDown } from 'lucide-react'; +import { cn } from '@/lib/utils'; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, +} from '@/components/ui/command'; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from '@/components/ui/popover'; +import { useMemo, useState } from 'react'; +import { User } from '@/db/schema'; + +// ---------------------------------------------------------------------------------------------------- + +/* TYPES */ +interface UserComboBoxProps { + users: Partial<User>[]; + value: number | null; + onChange: (value: number) => void; + placeholder?: string; + disabled?: boolean; +} + +// ---------------------------------------------------------------------------------------------------- + +/* User Combo Box Component */ +function UserComboBox(props: UserComboBoxProps) { + const { + users, + value, + onChange, + placeholder = '담당자 선택...', + disabled = false, + } = props; + const [open, setOpen] = useState(false); + const [inputValue, setInputValue] = useState(''); + const selectedUser = useMemo(() => { + return users.find(user => user.id === value) + }, [users, value]); + + return ( + <Popover open={open} onOpenChange={setOpen}> + <PopoverTrigger asChild> + <Button + variant="outline" + role="combobox" + aria-expanded={open} + className={cn( + "w-full justify-between", + !value && "text-muted-foreground" + )} + disabled={disabled} + > + {selectedUser ? ( + <span className="flex items-center"> + <span className="font-medium">{selectedUser.name}</span> + {selectedUser.deptName && ( + <span className="ml-2 text-xs text-muted-foreground"> + ({selectedUser.deptName}) + </span> + )} + </span> + ) : ( + placeholder + )} + <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" /> + </Button> + </PopoverTrigger> + <PopoverContent className="w-[300px] p-0"> + <Command> + <CommandInput + placeholder="담당자 검색..." + value={inputValue} + onValueChange={setInputValue} + /> + <CommandEmpty>검색 결과가 존재하지 않아요.</CommandEmpty> + <CommandGroup className="max-h-[200px] overflow-y-auto"> + {users.map((user) => ( + <CommandItem + key={user.id} + value={user.name} + onSelect={() => { + onChange(user.id!) + setOpen(false) + }} + > + <Check + className={cn( + 'mr-2 h-4 w-4', + value === user.id ? 'opacity-100' : 'opacity-0', + )} + /> + <div className="flex flex-col truncate"> + <div className="flex items-center"> + <span className="font-medium">{user.name}</span> + {user.deptName && ( + <span className="ml-2 text-xs text-muted-foreground"> + ({user.deptName}) + </span> + )} + </div> + <span className="text-xs text-muted-foreground truncate"> + {user.email} + </span> + </div> + </CommandItem> + ))} + </CommandGroup> + </Command> + </PopoverContent> + </Popover> + ) +} + +// ---------------------------------------------------------------------------------------------------- + +/* EXPORT */ +export default UserComboBox; |
