import { useMemo, useState } from 'react' import { Check, ChevronsUpDown } from 'lucide-react' import { useTranslation } from 'react-i18next' import { cn } from '@/lib/utils' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from '@/components/ui/command' import { Popover, PopoverContent, PopoverTrigger, } from '@/components/ui/popover' export type ApiKeyGroupOption = { value: string label: string desc?: string ratio?: number | string } type ApiKeyGroupComboboxProps = { options: ApiKeyGroupOption[] value?: string onValueChange: (value: string) => void placeholder?: string disabled?: boolean } function formatGroupRatio(ratio: ApiKeyGroupOption['ratio'], ratioLabel: string) { if (ratio === undefined || ratio === null || ratio === '') return null return `${ratio}x ${ratioLabel}` } function getRatioBadgeClassName(ratio: ApiKeyGroupOption['ratio']) { if (typeof ratio !== 'number') { return 'border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-900/60 dark:bg-emerald-950/40 dark:text-emerald-300' } if (ratio > 5) { return 'border-rose-200 bg-rose-50 text-rose-700 dark:border-rose-900/60 dark:bg-rose-950/40 dark:text-rose-300' } if (ratio > 3) { return 'border-orange-200 bg-orange-50 text-orange-700 dark:border-orange-900/60 dark:bg-orange-950/40 dark:text-orange-300' } if (ratio > 1) { return 'border-blue-200 bg-blue-50 text-blue-700 dark:border-blue-900/60 dark:bg-blue-950/40 dark:text-blue-300' } return 'border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-900/60 dark:bg-emerald-950/40 dark:text-emerald-300' } function GroupRatioBadge({ ratio }: { ratio: ApiKeyGroupOption['ratio'] }) { const { t } = useTranslation() const label = formatGroupRatio(ratio, t('Ratio')) if (!label) return null return ( {label} ) } export function ApiKeyGroupCombobox({ options, value, onValueChange, placeholder, disabled, }: ApiKeyGroupComboboxProps) { const { t } = useTranslation() const [open, setOpen] = useState(false) const [searchValue, setSearchValue] = useState('') const selectedOption = options.find((option) => option.value === value) const filteredOptions = useMemo(() => { const search = searchValue.trim().toLowerCase() if (!search) return options return options.filter((option) => { const ratioText = String(option.ratio ?? '').toLowerCase() return ( option.value.toLowerCase().includes(search) || option.label.toLowerCase().includes(search) || option.desc?.toLowerCase().includes(search) || ratioText.includes(search) ) }) }, [options, searchValue]) const handleSelect = (selectedValue: string) => { onValueChange(selectedValue) setOpen(false) setSearchValue('') } return ( event.stopPropagation()} onTouchMove={(event) => event.stopPropagation()} onPointerDown={(event) => event.stopPropagation()} > {t('No group found.')} {filteredOptions.map((option) => ( {option.value} {option.desc && ( {option.desc} )} ))} ) }