fix: migrate select to Base UI items API (#4655)

This commit is contained in:
yyhhyyyyyy 2026-05-06 21:38:58 +08:00 committed by GitHub
parent 38a3314b9b
commit d98f0e8ac3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 1187 additions and 485 deletions

View File

@ -11,6 +11,7 @@ import { Button } from '@/components/ui/button'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -45,6 +46,12 @@ export function DataTablePagination<TData>({
</div> </div>
<div className='flex items-center gap-2 @max-2xl/content:flex-row-reverse'> <div className='flex items-center gap-2 @max-2xl/content:flex-row-reverse'>
<Select <Select
items={[
...[10, 20, 30, 40, 50, 100].map((pageSize) => ({
value: `${pageSize}`,
label: pageSize,
})),
]}
value={`${table.getState().pagination.pageSize}`} value={`${table.getState().pagination.pageSize}`}
onValueChange={(value) => { onValueChange={(value) => {
table.setPageSize(Number(value)) table.setPageSize(Number(value))
@ -53,12 +60,14 @@ export function DataTablePagination<TData>({
<SelectTrigger className='h-8 w-[64px] sm:w-[70px]'> <SelectTrigger className='h-8 w-[64px] sm:w-[70px]'>
<SelectValue placeholder={table.getState().pagination.pageSize} /> <SelectValue placeholder={table.getState().pagination.pageSize} />
</SelectTrigger> </SelectTrigger>
<SelectContent side='top'> <SelectContent side='top' alignItemWithTrigger={false}>
{[10, 20, 30, 40, 50, 100].map((pageSize) => ( <SelectGroup>
<SelectItem key={pageSize} value={`${pageSize}`}> {[10, 20, 30, 40, 50, 100].map((pageSize) => (
{pageSize} <SelectItem key={pageSize} value={`${pageSize}`}>
</SelectItem> {pageSize}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<p className='hidden text-sm font-medium sm:block'> <p className='hidden text-sm font-medium sm:block'>

View File

@ -93,7 +93,7 @@ function SelectContent({
data-slot='select-content' data-slot='select-content'
data-align-trigger={alignItemWithTrigger} data-align-trigger={alignItemWithTrigger}
className={cn( className={cn(
'dark bg-popover text-popover-foreground ring-foreground/10 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg shadow-md ring-1 duration-100 data-[align-trigger=true]:animate-none', 'bg-popover text-popover-foreground ring-foreground/10 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg shadow-md ring-1 duration-100 data-[align-trigger=true]:animate-none',
className className
)} )}
{...props} {...props}

View File

@ -25,6 +25,7 @@ import { Label } from '@/components/ui/label'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -431,21 +432,29 @@ export function ChannelTestDialog({
<div className='grid gap-2'> <div className='grid gap-2'>
<Label htmlFor='endpoint-type'>{t('Endpoint Type')}</Label> <Label htmlFor='endpoint-type'>{t('Endpoint Type')}</Label>
<Select <Select
items={[
...endpointTypeOptions.map((option) => {
const itemValue = option.value
return { value: itemValue, label: t(option.label) }
}),
]}
value={endpointType} value={endpointType}
onValueChange={(v) => v !== null && setEndpointType(v)} onValueChange={(v) => v !== null && setEndpointType(v)}
> >
<SelectTrigger id='endpoint-type'> <SelectTrigger id='endpoint-type'>
<SelectValue placeholder={t('Auto detect (default)')} /> <SelectValue placeholder={t('Auto detect (default)')} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{endpointTypeOptions.map((option) => { <SelectGroup>
const itemValue = option.value {endpointTypeOptions.map((option) => {
return ( const itemValue = option.value
<SelectItem key={itemValue} value={itemValue}> return (
{t(option.label)} <SelectItem key={itemValue} value={itemValue}>
</SelectItem> {t(option.label)}
) </SelectItem>
})} )
})}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<p className='text-muted-foreground text-xs'> <p className='text-muted-foreground text-xs'>

View File

@ -18,6 +18,7 @@ import { ScrollArea } from '@/components/ui/scroll-area'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -276,6 +277,12 @@ export function EditTagDialog({ open, onOpenChange }: EditTagDialogProps) {
<div className='flex gap-2'> <div className='flex gap-2'>
<Select<string> <Select<string>
items={[
...availableModels.map((model) => ({
value: model,
label: model,
})),
]}
onValueChange={(value) => { onValueChange={(value) => {
if (value === null) return if (value === null) return
if (!selectedModels.includes(value)) { if (!selectedModels.includes(value)) {
@ -288,14 +295,16 @@ export function EditTagDialog({ open, onOpenChange }: EditTagDialogProps) {
placeholder={t('Add from available models...')} placeholder={t('Add from available models...')}
/> />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<ScrollArea className='h-60'> <SelectGroup>
{availableModels.map((model) => ( <ScrollArea className='h-60'>
<SelectItem key={model} value={model}> {availableModels.map((model) => (
{model} <SelectItem key={model} value={model}>
</SelectItem> {model}
))} </SelectItem>
</ScrollArea> ))}
</ScrollArea>
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>

View File

@ -14,6 +14,7 @@ import {
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -257,18 +258,26 @@ export function MultiKeyManageDialog({
{/* Toolbar */} {/* Toolbar */}
<div className='flex shrink-0 items-center justify-between'> <div className='flex shrink-0 items-center justify-between'>
<Select <Select
items={[
...MULTI_KEY_FILTER_OPTIONS.map((option) => ({
value: option.value,
label: t(option.label),
})),
]}
value={statusFilter === null ? 'all' : statusFilter.toString()} value={statusFilter === null ? 'all' : statusFilter.toString()}
onValueChange={(v) => v !== null && handleStatusFilterChange(v)} onValueChange={(v) => v !== null && handleStatusFilterChange(v)}
> >
<SelectTrigger className='w-40'> <SelectTrigger className='w-40'>
<SelectValue placeholder={t('All Status')} /> <SelectValue placeholder={t('All Status')} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{MULTI_KEY_FILTER_OPTIONS.map((option) => ( <SelectGroup>
<SelectItem key={option.value} value={option.value}> {MULTI_KEY_FILTER_OPTIONS.map((option) => (
{t(option.label)} <SelectItem key={option.value} value={option.value}>
</SelectItem> {t(option.label)}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>

View File

@ -38,6 +38,7 @@ import { ScrollArea } from '@/components/ui/scroll-area'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -1722,6 +1723,12 @@ export function ParamOverrideEditorDialog(
{t('Template')} {t('Template')}
</span> </span>
<Select <Select
items={[
...templatePresetOptions.map((o) => ({
value: o.value,
label: t(o.label),
})),
]}
value={templatePresetKey} value={templatePresetKey}
onValueChange={(v) => onValueChange={(v) =>
setTemplatePresetKey(v || 'operations_default') setTemplatePresetKey(v || 'operations_default')
@ -1730,12 +1737,14 @@ export function ParamOverrideEditorDialog(
<SelectTrigger className='h-8 w-[220px]'> <SelectTrigger className='h-8 w-[220px]'>
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{templatePresetOptions.map((o) => ( <SelectGroup>
<SelectItem key={o.value} value={o.value}> {templatePresetOptions.map((o) => (
{t(o.label)} <SelectItem key={o.value} value={o.value}>
</SelectItem> {t(o.label)}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<Button <Button
@ -2140,6 +2149,12 @@ function RuleEditor(ruleEditorProps: RuleEditorProps) {
<div className='space-y-1.5'> <div className='space-y-1.5'>
<label className='text-xs font-medium'>{t('Operation Type')}</label> <label className='text-xs font-medium'>{t('Operation Type')}</label>
<Select <Select
items={[
...OPERATION_MODE_OPTIONS.map((o) => ({
value: o.value,
label: t(o.label),
})),
]}
value={mode} value={mode}
onValueChange={(nextMode) => onValueChange={(nextMode) =>
nextMode !== null && nextMode !== null &&
@ -2151,12 +2166,14 @@ function RuleEditor(ruleEditorProps: RuleEditorProps) {
<SelectTrigger className='h-9'> <SelectTrigger className='h-9'>
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{OPERATION_MODE_OPTIONS.map((o) => ( <SelectGroup>
<SelectItem key={o.value} value={o.value}> {OPERATION_MODE_OPTIONS.map((o) => (
{t(o.label)} <SelectItem key={o.value} value={o.value}>
</SelectItem> {t(o.label)}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
@ -2339,6 +2356,10 @@ function RuleEditor(ruleEditorProps: RuleEditorProps) {
<div className='flex items-center gap-2'> <div className='flex items-center gap-2'>
<span className='text-sm font-medium'>{t('Conditions')}</span> <span className='text-sm font-medium'>{t('Conditions')}</span>
<Select <Select
items={[
{ value: 'OR', label: t('Match Any (OR)') },
{ value: 'AND', label: t('Match All (AND)') },
]}
value={operation.logic || 'OR'} value={operation.logic || 'OR'}
onValueChange={(v) => onValueChange={(v) =>
v !== null && v !== null &&
@ -2350,9 +2371,11 @@ function RuleEditor(ruleEditorProps: RuleEditorProps) {
<SelectTrigger className='h-7 w-[120px] text-xs'> <SelectTrigger className='h-7 w-[120px] text-xs'>
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value='OR'>{t('Match Any (OR)')}</SelectItem> <SelectGroup>
<SelectItem value='AND'>{t('Match All (AND)')}</SelectItem> <SelectItem value='OR'>{t('Match Any (OR)')}</SelectItem>
<SelectItem value='AND'>{t('Match All (AND)')}</SelectItem>
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
@ -2515,6 +2538,12 @@ function ConditionEditor(conditionEditorProps: ConditionEditorProps) {
{t('Match Mode')} {t('Match Mode')}
</label> </label>
<Select <Select
items={[
...CONDITION_MODE_OPTIONS.map((o) => ({
value: o.value,
label: t(o.label),
})),
]}
value={condition.mode} value={condition.mode}
onValueChange={(v) => onValueChange={(v) =>
v !== null && v !== null &&
@ -2528,12 +2557,14 @@ function ConditionEditor(conditionEditorProps: ConditionEditorProps) {
<SelectTrigger className='h-8 text-xs'> <SelectTrigger className='h-8 text-xs'>
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{CONDITION_MODE_OPTIONS.map((o) => ( <SelectGroup>
<SelectItem key={o.value} value={o.value}> {CONDITION_MODE_OPTIONS.map((o) => (
{t(o.label)} <SelectItem key={o.value} value={o.value}>
</SelectItem> {t(o.label)}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
@ -2889,6 +2920,10 @@ function PruneObjectsEditor(pruneObjectsEditorProps: PruneObjectsEditorProps) {
<div className='space-y-1'> <div className='space-y-1'>
<label className='text-xs font-medium'>{t('Logic')}</label> <label className='text-xs font-medium'>{t('Logic')}</label>
<Select <Select
items={[
{ value: 'AND', label: t('All Must Match (AND)') },
{ value: 'OR', label: t('Any Match (OR)') },
]}
value={draft.logic} value={draft.logic}
onValueChange={(v) => onValueChange={(v) =>
pruneObjectsEditorProps.updateDraft( pruneObjectsEditorProps.updateDraft(
@ -2900,11 +2935,13 @@ function PruneObjectsEditor(pruneObjectsEditorProps: PruneObjectsEditorProps) {
<SelectTrigger className='h-8 text-xs'> <SelectTrigger className='h-8 text-xs'>
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value='AND'> <SelectGroup>
{t('All Must Match (AND)')} <SelectItem value='AND'>
</SelectItem> {t('All Must Match (AND)')}
<SelectItem value='OR'>{t('Any Match (OR)')}</SelectItem> </SelectItem>
<SelectItem value='OR'>{t('Any Match (OR)')}</SelectItem>
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
@ -3021,6 +3058,12 @@ function PruneObjectsEditor(pruneObjectsEditorProps: PruneObjectsEditorProps) {
{t('Match Mode')} {t('Match Mode')}
</label> </label>
<Select <Select
items={[
...CONDITION_MODE_OPTIONS.map((o) => ({
value: o.value,
label: t(o.label),
})),
]}
value={rule.mode} value={rule.mode}
onValueChange={(v) => onValueChange={(v) =>
v !== null && v !== null &&
@ -3034,12 +3077,14 @@ function PruneObjectsEditor(pruneObjectsEditorProps: PruneObjectsEditorProps) {
<SelectTrigger className='h-7 text-xs'> <SelectTrigger className='h-7 text-xs'>
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{CONDITION_MODE_OPTIONS.map((o) => ( <SelectGroup>
<SelectItem key={o.value} value={o.value}> {CONDITION_MODE_OPTIONS.map((o) => (
{t(o.label)} <SelectItem key={o.value} value={o.value}>
</SelectItem> {t(o.label)}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
@ -3126,6 +3171,12 @@ function SyncFieldsEditor(syncFieldsEditorProps: SyncFieldsEditorProps) {
</label> </label>
<div className='flex gap-2'> <div className='flex gap-2'>
<Select <Select
items={[
...SYNC_TARGET_TYPE_OPTIONS.map((o) => ({
value: o.value,
label: t(o.label),
})),
]}
value={syncFieldsEditorProps.syncFromTarget.type || 'json'} value={syncFieldsEditorProps.syncFromTarget.type || 'json'}
onValueChange={(v) => onValueChange={(v) =>
v !== null && v !== null &&
@ -3143,12 +3194,14 @@ function SyncFieldsEditor(syncFieldsEditorProps: SyncFieldsEditorProps) {
<SelectTrigger className='h-8 w-[110px] text-xs'> <SelectTrigger className='h-8 w-[110px] text-xs'>
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{SYNC_TARGET_TYPE_OPTIONS.map((o) => ( <SelectGroup>
<SelectItem key={o.value} value={o.value}> {SYNC_TARGET_TYPE_OPTIONS.map((o) => (
{t(o.label)} <SelectItem key={o.value} value={o.value}>
</SelectItem> {t(o.label)}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<Input <Input
@ -3175,6 +3228,12 @@ function SyncFieldsEditor(syncFieldsEditorProps: SyncFieldsEditorProps) {
</label> </label>
<div className='flex gap-2'> <div className='flex gap-2'>
<Select <Select
items={[
...SYNC_TARGET_TYPE_OPTIONS.map((o) => ({
value: o.value,
label: t(o.label),
})),
]}
value={syncFieldsEditorProps.syncToTarget.type || 'json'} value={syncFieldsEditorProps.syncToTarget.type || 'json'}
onValueChange={(v) => onValueChange={(v) =>
v !== null && v !== null &&
@ -3192,12 +3251,14 @@ function SyncFieldsEditor(syncFieldsEditorProps: SyncFieldsEditorProps) {
<SelectTrigger className='h-8 w-[110px] text-xs'> <SelectTrigger className='h-8 w-[110px] text-xs'>
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{SYNC_TARGET_TYPE_OPTIONS.map((o) => ( <SelectGroup>
<SelectItem key={o.value} value={o.value}> {SYNC_TARGET_TYPE_OPTIONS.map((o) => (
{t(o.label)} <SelectItem key={o.value} value={o.value}>
</SelectItem> {t(o.label)}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<Input <Input

View File

@ -59,6 +59,7 @@ import { Input } from '@/components/ui/input'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -1376,6 +1377,13 @@ export function ChannelMutateDrawer({
<FormItem> <FormItem>
<FormLabel>{t('AWS Key Format')}</FormLabel> <FormLabel>{t('AWS Key Format')}</FormLabel>
<Select <Select
items={[
{
value: 'ak_sk',
label: t('AccessKey / SecretAccessKey'),
},
{ value: 'api_key', label: t('API Key') },
]}
onValueChange={field.onChange} onValueChange={field.onChange}
value={field.value} value={field.value}
> >
@ -1386,13 +1394,15 @@ export function ChannelMutateDrawer({
/> />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value='ak_sk'> <SelectGroup>
{t('AccessKey / SecretAccessKey')} <SelectItem value='ak_sk'>
</SelectItem> {t('AccessKey / SecretAccessKey')}
<SelectItem value='api_key'> </SelectItem>
{t('API Key')} <SelectItem value='api_key'>
</SelectItem> {t('API Key')}
</SelectItem>
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<FormDescription> <FormDescription>
@ -1534,6 +1544,10 @@ export function ChannelMutateDrawer({
<FormItem> <FormItem>
<FormLabel>{t('Vertex AI Key Format')}</FormLabel> <FormLabel>{t('Vertex AI Key Format')}</FormLabel>
<Select <Select
items={[
{ value: 'json', label: t('JSON') },
{ value: 'api_key', label: t('API Key') },
]}
onValueChange={field.onChange} onValueChange={field.onChange}
value={field.value} value={field.value}
> >
@ -1542,11 +1556,15 @@ export function ChannelMutateDrawer({
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value='json'>{t('JSON')}</SelectItem> <SelectGroup>
<SelectItem value='api_key'> <SelectItem value='json'>
{t('API Key')} {t('JSON')}
</SelectItem> </SelectItem>
<SelectItem value='api_key'>
{t('API Key')}
</SelectItem>
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<FormDescription> <FormDescription>
@ -1672,6 +1690,22 @@ export function ChannelMutateDrawer({
{t('API Base URL *')} {t('API Base URL *')}
</FormLabel> </FormLabel>
<Select <Select
items={[
{
value: 'https://ark.cn-beijing.volces.com',
label: t('https://ark.cn-beijing.volces.com'),
},
{
value: 'https://ark.ap-southeast.bytepluses.com',
label: t(
'https://ark.ap-southeast.bytepluses.com'
),
},
{
value: 'doubao-coding-plan',
label: t('Doubao Coding Plan'),
},
]}
onValueChange={field.onChange} onValueChange={field.onChange}
value={ value={
field.value || 'https://ark.cn-beijing.volces.com' field.value || 'https://ark.cn-beijing.volces.com'
@ -1682,16 +1716,18 @@ export function ChannelMutateDrawer({
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value='https://ark.cn-beijing.volces.com'> <SelectGroup>
{t('https://ark.cn-beijing.volces.com')} <SelectItem value='https://ark.cn-beijing.volces.com'>
</SelectItem> {t('https://ark.cn-beijing.volces.com')}
<SelectItem value='https://ark.ap-southeast.bytepluses.com'> </SelectItem>
{t('https://ark.ap-southeast.bytepluses.com')} <SelectItem value='https://ark.ap-southeast.bytepluses.com'>
</SelectItem> {t('https://ark.ap-southeast.bytepluses.com')}
<SelectItem value='doubao-coding-plan'> </SelectItem>
{t('Doubao Coding Plan')} <SelectItem value='doubao-coding-plan'>
</SelectItem> {t('Doubao Coding Plan')}
</SelectItem>
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<FormDescription> <FormDescription>
@ -1790,6 +1826,12 @@ export function ChannelMutateDrawer({
<FormItem> <FormItem>
<FormLabel>{t('Add Mode')}</FormLabel> <FormLabel>{t('Add Mode')}</FormLabel>
<Select <Select
items={[
...ADD_MODE_OPTIONS.map((option) => ({
value: option.value,
label: t(option.label),
})),
]}
onValueChange={field.onChange} onValueChange={field.onChange}
value={field.value} value={field.value}
> >
@ -1798,15 +1840,17 @@ export function ChannelMutateDrawer({
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{ADD_MODE_OPTIONS.map((option) => ( <SelectGroup>
<SelectItem {ADD_MODE_OPTIONS.map((option) => (
key={option.value} <SelectItem
value={option.value} key={option.value}
> value={option.value}
{t(option.label)} >
</SelectItem> {t(option.label)}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<FormDescription> <FormDescription>
@ -2027,6 +2071,16 @@ export function ChannelMutateDrawer({
<FormItem> <FormItem>
<FormLabel>{t('Key Update Mode')}</FormLabel> <FormLabel>{t('Key Update Mode')}</FormLabel>
<Select <Select
items={[
{
value: 'append',
label: t('Append to existing keys'),
},
{
value: 'replace',
label: t('Replace all existing keys'),
},
]}
onValueChange={field.onChange} onValueChange={field.onChange}
value={field.value} value={field.value}
> >
@ -2035,13 +2089,15 @@ export function ChannelMutateDrawer({
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value='append'> <SelectGroup>
{t('Append to existing keys')} <SelectItem value='append'>
</SelectItem> {t('Append to existing keys')}
<SelectItem value='replace'> </SelectItem>
{t('Replace all existing keys')} <SelectItem value='replace'>
</SelectItem> {t('Replace all existing keys')}
</SelectItem>
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<FormDescription> <FormDescription>
@ -2067,6 +2123,10 @@ export function ChannelMutateDrawer({
<FormItem> <FormItem>
<FormLabel>{t('Multi-Key Strategy')}</FormLabel> <FormLabel>{t('Multi-Key Strategy')}</FormLabel>
<Select <Select
items={[
{ value: 'random', label: t('Random') },
{ value: 'polling', label: t('Polling') },
]}
onValueChange={field.onChange} onValueChange={field.onChange}
value={field.value} value={field.value}
> >
@ -2075,13 +2135,15 @@ export function ChannelMutateDrawer({
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value='random'> <SelectGroup>
{t('Random')} <SelectItem value='random'>
</SelectItem> {t('Random')}
<SelectItem value='polling'> </SelectItem>
{t('Polling')} <SelectItem value='polling'>
</SelectItem> {t('Polling')}
</SelectItem>
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<FormDescription> <FormDescription>

View File

@ -16,6 +16,7 @@ import { Label } from '@/components/ui/label'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -73,6 +74,12 @@ export function ModelsChartPreferences(props: ModelsChartPreferencesProps) {
<div className='grid gap-2'> <div className='grid gap-2'>
<Label htmlFor='default-time-range'>{t('Default range')}</Label> <Label htmlFor='default-time-range'>{t('Default range')}</Label>
<Select <Select
items={[
...TIME_RANGE_PRESETS.map((option) => ({
value: String(option.days),
label: t(option.label),
})),
]}
value={String(draft.defaultTimeRangeDays)} value={String(draft.defaultTimeRangeDays)}
onValueChange={(value) => onValueChange={(value) =>
setDraft((prev) => ({ setDraft((prev) => ({
@ -84,12 +91,14 @@ export function ModelsChartPreferences(props: ModelsChartPreferencesProps) {
<SelectTrigger id='default-time-range'> <SelectTrigger id='default-time-range'>
<SelectValue placeholder={t('Select default range')} /> <SelectValue placeholder={t('Select default range')} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{TIME_RANGE_PRESETS.map((option) => ( <SelectGroup>
<SelectItem key={option.days} value={String(option.days)}> {TIME_RANGE_PRESETS.map((option) => (
{t(option.label)} <SelectItem key={option.days} value={String(option.days)}>
</SelectItem> {t(option.label)}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
@ -99,6 +108,12 @@ export function ModelsChartPreferences(props: ModelsChartPreferencesProps) {
{t('Default time granularity')} {t('Default time granularity')}
</Label> </Label>
<Select <Select
items={[
...TIME_GRANULARITY_OPTIONS.map((option) => ({
value: option.value,
label: t(option.label),
})),
]}
value={draft.defaultTimeGranularity} value={draft.defaultTimeGranularity}
onValueChange={(value) => onValueChange={(value) =>
setDraft((prev) => ({ setDraft((prev) => ({
@ -110,12 +125,14 @@ export function ModelsChartPreferences(props: ModelsChartPreferencesProps) {
<SelectTrigger id='default-time-granularity'> <SelectTrigger id='default-time-granularity'>
<SelectValue placeholder={t('Select time granularity')} /> <SelectValue placeholder={t('Select time granularity')} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{TIME_GRANULARITY_OPTIONS.map((option) => ( <SelectGroup>
<SelectItem key={option.value} value={option.value}> {TIME_GRANULARITY_OPTIONS.map((option) => (
{t(option.label)} <SelectItem key={option.value} value={option.value}>
</SelectItem> {t(option.label)}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
@ -125,6 +142,12 @@ export function ModelsChartPreferences(props: ModelsChartPreferencesProps) {
{t('Default consumption chart')} {t('Default consumption chart')}
</Label> </Label>
<Select <Select
items={[
...CONSUMPTION_DISTRIBUTION_CHART_OPTIONS.map((option) => ({
value: option.value,
label: t(option.labelKey),
})),
]}
value={draft.consumptionDistributionChart} value={draft.consumptionDistributionChart}
onValueChange={(value) => onValueChange={(value) =>
setDraft((prev) => ({ setDraft((prev) => ({
@ -137,12 +160,14 @@ export function ModelsChartPreferences(props: ModelsChartPreferencesProps) {
<SelectTrigger id='consumption-distribution-chart'> <SelectTrigger id='consumption-distribution-chart'>
<SelectValue placeholder={t('Select default chart')} /> <SelectValue placeholder={t('Select default chart')} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{CONSUMPTION_DISTRIBUTION_CHART_OPTIONS.map((option) => ( <SelectGroup>
<SelectItem key={option.value} value={option.value}> {CONSUMPTION_DISTRIBUTION_CHART_OPTIONS.map((option) => (
{t(option.labelKey)} <SelectItem key={option.value} value={option.value}>
</SelectItem> {t(option.labelKey)}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
@ -152,6 +177,12 @@ export function ModelsChartPreferences(props: ModelsChartPreferencesProps) {
{t('Default model call chart')} {t('Default model call chart')}
</Label> </Label>
<Select <Select
items={[
...MODEL_ANALYTICS_CHART_OPTIONS.map((option) => ({
value: option.value,
label: t(option.labelKey),
})),
]}
value={draft.modelAnalyticsChart} value={draft.modelAnalyticsChart}
onValueChange={(value) => onValueChange={(value) =>
setDraft((prev) => ({ setDraft((prev) => ({
@ -163,12 +194,14 @@ export function ModelsChartPreferences(props: ModelsChartPreferencesProps) {
<SelectTrigger id='model-analytics-chart'> <SelectTrigger id='model-analytics-chart'>
<SelectValue placeholder={t('Select default chart')} /> <SelectValue placeholder={t('Select default chart')} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{MODEL_ANALYTICS_CHART_OPTIONS.map((option) => ( <SelectGroup>
<SelectItem key={option.value} value={option.value}> {MODEL_ANALYTICS_CHART_OPTIONS.map((option) => (
{t(option.labelKey)} <SelectItem key={option.value} value={option.value}>
</SelectItem> {t(option.labelKey)}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>

View File

@ -20,6 +20,7 @@ import { ScrollArea } from '@/components/ui/scroll-area'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -197,6 +198,12 @@ export function ModelsFilter(props: ModelsFilterProps) {
<div className='grid gap-2'> <div className='grid gap-2'>
<Label htmlFor='time_granularity'>{t('Time Granularity')}</Label> <Label htmlFor='time_granularity'>{t('Time Granularity')}</Label>
<Select <Select
items={[
...TIME_GRANULARITY_OPTIONS.map((option) => ({
value: option.value,
label: t(option.label),
})),
]}
value={filters.time_granularity} value={filters.time_granularity}
onValueChange={(value) => onValueChange={(value) =>
handleChange('time_granularity', value as TimeGranularity) handleChange('time_granularity', value as TimeGranularity)
@ -205,12 +212,14 @@ export function ModelsFilter(props: ModelsFilterProps) {
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder={t('Select time granularity')} /> <SelectValue placeholder={t('Select time granularity')} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{TIME_GRANULARITY_OPTIONS.map((option) => ( <SelectGroup>
<SelectItem key={option.value} value={option.value}> {TIME_GRANULARITY_OPTIONS.map((option) => (
{t(option.label)} <SelectItem key={option.value} value={option.value}>
</SelectItem> {t(option.label)}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>

View File

@ -18,6 +18,7 @@ import { Input } from '@/components/ui/input'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -432,6 +433,12 @@ export function CreateDeploymentDrawer({
<FormItem> <FormItem>
<FormLabel>{t('Hardware type')}</FormLabel> <FormLabel>{t('Hardware type')}</FormLabel>
<Select <Select
items={[
...hardwareOptions.map((opt) => ({
value: opt.value,
label: opt.label,
})),
]}
value={field.value} value={field.value}
onValueChange={(v) => field.onChange(v)} onValueChange={(v) => field.onChange(v)}
disabled={isLoadingHardware} disabled={isLoadingHardware}
@ -441,12 +448,14 @@ export function CreateDeploymentDrawer({
<SelectValue placeholder={t('Select')} /> <SelectValue placeholder={t('Select')} />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{hardwareOptions.map((opt) => ( <SelectGroup>
<SelectItem key={opt.value} value={opt.value}> {hardwareOptions.map((opt) => (
{opt.label} <SelectItem key={opt.value} value={opt.value}>
</SelectItem> {opt.label}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<FormMessage /> <FormMessage />
@ -593,6 +602,10 @@ export function CreateDeploymentDrawer({
<FormItem> <FormItem>
<FormLabel>{t('Billing currency')}</FormLabel> <FormLabel>{t('Billing currency')}</FormLabel>
<Select <Select
items={[
{ value: 'usdc', label: 'USDC' },
{ value: 'iocoin', label: 'IOCOIN' },
]}
value={field.value || 'usdc'} value={field.value || 'usdc'}
onValueChange={(v) => field.onChange(v)} onValueChange={(v) => field.onChange(v)}
> >
@ -601,9 +614,11 @@ export function CreateDeploymentDrawer({
<SelectValue placeholder={t('Select')} /> <SelectValue placeholder={t('Select')} />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value='usdc'>USDC</SelectItem> <SelectGroup>
<SelectItem value='iocoin'>IOCOIN</SelectItem> <SelectItem value='usdc'>USDC</SelectItem>
<SelectItem value='iocoin'>IOCOIN</SelectItem>
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</FormItem> </FormItem>

View File

@ -36,6 +36,7 @@ import {
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -550,6 +551,12 @@ export function UpstreamConflictDialog({
{t('Rows per page')} {t('Rows per page')}
</span> </span>
<Select <Select
items={[
...[5, 10, 20, 50].map((size) => ({
value: String(size),
label: size,
})),
]}
value={String(pageSize)} value={String(pageSize)}
onValueChange={(value) => { onValueChange={(value) => {
setPageSize(Number(value)) setPageSize(Number(value))
@ -559,12 +566,14 @@ export function UpstreamConflictDialog({
<SelectTrigger className='h-8 w-[70px] text-xs sm:h-8 sm:w-[72px]'> <SelectTrigger className='h-8 w-[70px] text-xs sm:h-8 sm:w-[72px]'>
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{[5, 10, 20, 50].map((size) => ( <SelectGroup>
<SelectItem key={size} value={String(size)}> {[5, 10, 20, 50].map((size) => (
{size} <SelectItem key={size} value={String(size)}>
</SelectItem> {size}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>

View File

@ -12,6 +12,7 @@ import {
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -175,6 +176,27 @@ export function ViewLogsDialog({
{t('Container')} {t('Container')}
</div> </div>
<Select <Select
items={[
...containers.flatMap((c) => {
const id = c?.container_id
if (typeof id !== 'string' || !id) return []
const status =
typeof c?.status === 'string' && c.status
? ` (${c.status})`
: ''
return [
{
value: id,
label: (
<>
{id}
{status}
</>
),
},
]
}),
]}
value={containerId} value={containerId}
onValueChange={(v) => v !== null && setContainerId(v)} onValueChange={(v) => v !== null && setContainerId(v)}
disabled={isLoadingContainers || containers.length === 0} disabled={isLoadingContainers || containers.length === 0}
@ -190,27 +212,34 @@ export function ViewLogsDialog({
} }
/> />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{containers.map((c) => { <SelectGroup>
const id = c?.container_id {containers.map((c) => {
if (typeof id !== 'string' || !id) return null const id = c?.container_id
const status = if (typeof id !== 'string' || !id) return null
typeof c?.status === 'string' && c.status const status =
? ` (${c.status})` typeof c?.status === 'string' && c.status
: '' ? ` (${c.status})`
return ( : ''
<SelectItem key={id} value={id}> return (
{id} <SelectItem key={id} value={id}>
{status} {id}
</SelectItem> {status}
) </SelectItem>
})} )
})}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
<div className='space-y-1'> <div className='space-y-1'>
<div className='text-muted-foreground text-xs'>{t('Stream')}</div> <div className='text-muted-foreground text-xs'>{t('Stream')}</div>
<Select <Select
items={[
{ value: 'stdout', label: 'stdout' },
{ value: 'stderr', label: 'stderr' },
{ value: 'all', label: 'all' },
]}
value={stream} value={stream}
onValueChange={(v) => { onValueChange={(v) => {
if (v === 'stderr' || v === 'all' || v === 'stdout') { if (v === 'stderr' || v === 'all' || v === 'stdout') {
@ -223,10 +252,12 @@ export function ViewLogsDialog({
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder={t('Select')} /> <SelectValue placeholder={t('Select')} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value='stdout'>stdout</SelectItem> <SelectGroup>
<SelectItem value='stderr'>stderr</SelectItem> <SelectItem value='stdout'>stdout</SelectItem>
<SelectItem value='all'>all</SelectItem> <SelectItem value='stderr'>stderr</SelectItem>
<SelectItem value='all'>all</SelectItem>
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>

View File

@ -27,6 +27,7 @@ import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -701,6 +702,12 @@ export function ModelMutateDrawer({
<FormItem> <FormItem>
<FormLabel>{t('Vendor')}</FormLabel> <FormLabel>{t('Vendor')}</FormLabel>
<Select <Select
items={[
...vendors.map((vendor) => ({
value: String(vendor.id),
label: vendor.name,
})),
]}
onValueChange={(value) => onValueChange={(value) =>
field.onChange(value ? parseInt(value) : undefined) field.onChange(value ? parseInt(value) : undefined)
} }
@ -711,12 +718,17 @@ export function ModelMutateDrawer({
<SelectValue placeholder={t('Select vendor')} /> <SelectValue placeholder={t('Select vendor')} />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{vendors.map((vendor) => ( <SelectGroup>
<SelectItem key={vendor.id} value={String(vendor.id)}> {vendors.map((vendor) => (
{vendor.name} <SelectItem
</SelectItem> key={vendor.id}
))} value={String(vendor.id)}
>
{vendor.name}
</SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<FormMessage /> <FormMessage />
@ -801,6 +813,12 @@ export function ModelMutateDrawer({
<div className='flex items-center justify-between'> <div className='flex items-center justify-between'>
<h3 className='text-sm font-semibold'>{t('Endpoints')}</h3> <h3 className='text-sm font-semibold'>{t('Endpoints')}</h3>
<Select<string> <Select<string>
items={[
...Object.keys(ENDPOINT_TEMPLATES).map((key) => ({
value: key,
label: key,
})),
]}
onValueChange={(v) => onValueChange={(v) =>
v !== null && handleFillEndpointTemplate(v) v !== null && handleFillEndpointTemplate(v)
} }
@ -808,12 +826,14 @@ export function ModelMutateDrawer({
<SelectTrigger size='sm' className='w-[200px]'> <SelectTrigger size='sm' className='w-[200px]'>
<SelectValue placeholder={t('Load template...')} /> <SelectValue placeholder={t('Load template...')} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{Object.keys(ENDPOINT_TEMPLATES).map((key) => ( <SelectGroup>
<SelectItem key={key} value={key}> {Object.keys(ENDPOINT_TEMPLATES).map((key) => (
{key} <SelectItem key={key} value={key}>
</SelectItem> {key}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>

View File

@ -19,6 +19,7 @@ import { Input } from '@/components/ui/input'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -252,6 +253,22 @@ export function PrefillGroupFormDrawer({
<FormItem> <FormItem>
<FormLabel>Group Type</FormLabel> <FormLabel>Group Type</FormLabel>
<Select <Select
items={[
...PREFILL_GROUP_TYPES.map((type) => ({
value: type.value,
label: (
<div className='flex flex-col text-left'>
<span className='font-medium'>{type.label}</span>
<span
data-prefill-description
className='text-muted-foreground text-xs'
>
{type.description}
</span>
</div>
),
})),
]}
value={field.value} value={field.value}
onValueChange={(value) => onValueChange={(value) =>
value !== null && value !== null &&
@ -263,20 +280,24 @@ export function PrefillGroupFormDrawer({
<SelectValue placeholder={t('Select a group type')} /> <SelectValue placeholder={t('Select a group type')} />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{PREFILL_GROUP_TYPES.map((type) => ( <SelectGroup>
<SelectItem key={type.value} value={type.value}> {PREFILL_GROUP_TYPES.map((type) => (
<div className='flex flex-col text-left'> <SelectItem key={type.value} value={type.value}>
<span className='font-medium'>{type.label}</span> <div className='flex flex-col text-left'>
<span <span className='font-medium'>
data-prefill-description {type.label}
className='text-muted-foreground text-xs' </span>
> <span
{type.description} data-prefill-description
</span> className='text-muted-foreground text-xs'
</div> >
</SelectItem> {type.description}
))} </span>
</div>
</SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<FormDescription> <FormDescription>

View File

@ -6,6 +6,7 @@ import { useAuthStore } from '@/stores/auth-store'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -112,6 +113,12 @@ export function LanguagePreferencesCard(props: LanguagePreferencesCardProps) {
</div> </div>
<div className='flex items-center gap-2 sm:min-w-48'> <div className='flex items-center gap-2 sm:min-w-48'>
<Select <Select
items={[
...LANGUAGE_OPTIONS.map((language) => ({
value: language.value,
label: language.label,
})),
]}
value={currentLanguage} value={currentLanguage}
onValueChange={handleLanguageChange} onValueChange={handleLanguageChange}
disabled={saving} disabled={saving}
@ -119,12 +126,14 @@ export function LanguagePreferencesCard(props: LanguagePreferencesCardProps) {
<SelectTrigger className='w-full sm:w-48'> <SelectTrigger className='w-full sm:w-48'>
<SelectValue placeholder={t('Select language')} /> <SelectValue placeholder={t('Select language')} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{LANGUAGE_OPTIONS.map((language) => ( <SelectGroup>
<SelectItem key={language.value} value={language.value}> {LANGUAGE_OPTIONS.map((language) => (
{language.label} <SelectItem key={language.value} value={language.value}>
</SelectItem> {language.label}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
{saving && ( {saving && (

View File

@ -13,6 +13,7 @@ import {
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -270,6 +271,12 @@ export function SubscriptionPurchaseDialog(props: Props) {
{hasEpay && ( {hasEpay && (
<div className='grid grid-cols-[minmax(0,1fr)_auto] gap-2'> <div className='grid grid-cols-[minmax(0,1fr)_auto] gap-2'>
<Select <Select
items={[
...(props.epayMethods || []).map((m) => ({
value: m.type,
label: m.name || m.type,
})),
]}
value={selectedEpayMethod} value={selectedEpayMethod}
onValueChange={(v) => onValueChange={(v) =>
v !== null && setSelectedEpayMethod(v) v !== null && setSelectedEpayMethod(v)
@ -279,12 +286,14 @@ export function SubscriptionPurchaseDialog(props: Props) {
<SelectTrigger className='flex-1'> <SelectTrigger className='flex-1'>
<SelectValue>{selectedEpayMethodLabel}</SelectValue> <SelectValue>{selectedEpayMethodLabel}</SelectValue>
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{(props.epayMethods || []).map((m) => ( <SelectGroup>
<SelectItem key={m.type} value={m.type}> {(props.epayMethods || []).map((m) => (
{m.name || m.type} <SelectItem key={m.type} value={m.type}>
</SelectItem> {m.name || m.type}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<Button <Button

View File

@ -6,6 +6,7 @@ import { Button } from '@/components/ui/button'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -183,19 +184,32 @@ export function UserSubscriptionsDialog(props: Props) {
<div className='mt-4 space-y-4'> <div className='mt-4 space-y-4'>
<div className='flex gap-2'> <div className='flex gap-2'>
<Select <Select
items={[
...plans.map((p) => ({
value: String(p.plan.id),
label: (
<>
{p.plan.title}($
{Number(p.plan.price_amount || 0).toFixed(2)})
</>
),
})),
]}
value={selectedPlanId} value={selectedPlanId}
onValueChange={(v) => v !== null && setSelectedPlanId(v)} onValueChange={(v) => v !== null && setSelectedPlanId(v)}
> >
<SelectTrigger className='flex-1'> <SelectTrigger className='flex-1'>
<SelectValue placeholder={t('Select subscription plan')} /> <SelectValue placeholder={t('Select subscription plan')} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{plans.map((p) => ( <SelectGroup>
<SelectItem key={p.plan.id} value={String(p.plan.id)}> {plans.map((p) => (
{p.plan.title} ($ <SelectItem key={p.plan.id} value={String(p.plan.id)}>
{Number(p.plan.price_amount || 0).toFixed(2)}) {p.plan.title} ($
</SelectItem> {Number(p.plan.price_amount || 0).toFixed(2)})
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<Button <Button

View File

@ -18,6 +18,7 @@ import { Input } from '@/components/ui/input'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -237,6 +238,10 @@ export function SubscriptionsMutateDrawer({
<FormItem> <FormItem>
<FormLabel>{t('Upgrade Group')}</FormLabel> <FormLabel>{t('Upgrade Group')}</FormLabel>
<Select <Select
items={[
{ value: '__none__', label: t('No Upgrade') },
...groupOptions.map((g) => ({ value: g, label: g })),
]}
onValueChange={(v) => onValueChange={(v) =>
field.onChange(v === '__none__' ? '' : v) field.onChange(v === '__none__' ? '' : v)
} }
@ -247,15 +252,17 @@ export function SubscriptionsMutateDrawer({
<SelectValue placeholder={t('No Upgrade')} /> <SelectValue placeholder={t('No Upgrade')} />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value='__none__'> <SelectGroup>
{t('No Upgrade')} <SelectItem value='__none__'>
</SelectItem> {t('No Upgrade')}
{groupOptions.map((g) => (
<SelectItem key={g} value={g}>
{g}
</SelectItem> </SelectItem>
))} {groupOptions.map((g) => (
<SelectItem key={g} value={g}>
{g}
</SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<FormMessage /> <FormMessage />
@ -344,6 +351,12 @@ export function SubscriptionsMutateDrawer({
<FormItem> <FormItem>
<FormLabel>{t('Duration Unit')}</FormLabel> <FormLabel>{t('Duration Unit')}</FormLabel>
<Select <Select
items={[
...durationUnitOpts.map((o) => ({
value: o.value,
label: o.label,
})),
]}
onValueChange={field.onChange} onValueChange={field.onChange}
value={field.value} value={field.value}
> >
@ -352,12 +365,14 @@ export function SubscriptionsMutateDrawer({
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{durationUnitOpts.map((o) => ( <SelectGroup>
<SelectItem key={o.value} value={o.value}> {durationUnitOpts.map((o) => (
{o.label} <SelectItem key={o.value} value={o.value}>
</SelectItem> {o.label}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<FormMessage /> <FormMessage />
@ -426,6 +441,12 @@ export function SubscriptionsMutateDrawer({
<FormItem> <FormItem>
<FormLabel>{t('Reset Cycle')}</FormLabel> <FormLabel>{t('Reset Cycle')}</FormLabel>
<Select <Select
items={[
...resetPeriodOpts.map((o) => ({
value: o.value,
label: o.label,
})),
]}
onValueChange={field.onChange} onValueChange={field.onChange}
value={field.value} value={field.value}
> >
@ -434,12 +455,14 @@ export function SubscriptionsMutateDrawer({
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{resetPeriodOpts.map((o) => ( <SelectGroup>
<SelectItem key={o.value} value={o.value}> {resetPeriodOpts.map((o) => (
{o.label} <SelectItem key={o.value} value={o.value}>
</SelectItem> {o.label}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<FormMessage /> <FormMessage />

View File

@ -6,6 +6,7 @@ import { Label } from '@/components/ui/label'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -89,18 +90,26 @@ export function PresetSelector(props: PresetSelectorProps) {
<div className='space-y-1.5'> <div className='space-y-1.5'>
<Label>{t('Preset Template')}</Label> <Label>{t('Preset Template')}</Label>
<Select <Select
items={[
...OAUTH_PRESETS.map((preset) => ({
value: preset.key,
label: preset.name,
})),
]}
value={selectedPreset} value={selectedPreset}
onValueChange={(v) => v !== null && handlePresetChange(v)} onValueChange={(v) => v !== null && handlePresetChange(v)}
> >
<SelectTrigger className='w-full'> <SelectTrigger className='w-full'>
<SelectValue placeholder={t('Select a preset...')} /> <SelectValue placeholder={t('Select a preset...')} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{OAUTH_PRESETS.map((preset) => ( <SelectGroup>
<SelectItem key={preset.key} value={preset.key}> {OAUTH_PRESETS.map((preset) => (
{preset.name} <SelectItem key={preset.key} value={preset.key}>
</SelectItem> {preset.name}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>

View File

@ -24,6 +24,7 @@ import { Input } from '@/components/ui/input'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -302,6 +303,12 @@ export function ProviderFormDialog(props: ProviderFormDialogProps) {
<FormItem> <FormItem>
<FormLabel>{t('Auth Style')}</FormLabel> <FormLabel>{t('Auth Style')}</FormLabel>
<Select <Select
items={[
...AUTH_STYLE_OPTIONS.map((option) => ({
value: String(option.value),
label: t(option.labelKey),
})),
]}
value={String(field.value)} value={String(field.value)}
onValueChange={(val) => field.onChange(Number(val))} onValueChange={(val) => field.onChange(Number(val))}
> >
@ -310,15 +317,17 @@ export function ProviderFormDialog(props: ProviderFormDialogProps) {
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{AUTH_STYLE_OPTIONS.map((option) => ( <SelectGroup>
<SelectItem {AUTH_STYLE_OPTIONS.map((option) => (
key={option.value} <SelectItem
value={String(option.value)} key={option.value}
> value={String(option.value)}
{t(option.labelKey)} >
</SelectItem> {t(option.labelKey)}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<FormDescription> <FormDescription>

View File

@ -17,6 +17,7 @@ import { Input } from '@/components/ui/input'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -221,18 +222,30 @@ export function PasskeySection({ defaultValues }: PasskeySectionProps) {
<FormItem> <FormItem>
<FormLabel>{t('User Verification')}</FormLabel> <FormLabel>{t('User Verification')}</FormLabel>
<FormControl> <FormControl>
<Select value={field.value} onValueChange={field.onChange}> <Select
items={[
{ value: 'required', label: t('Required') },
{ value: 'preferred', label: t('Recommended') },
{ value: 'discouraged', label: t('Discouraged') },
]}
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder={t('Select requirement')} /> <SelectValue placeholder={t('Select requirement')} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value='required'>{t('Required')}</SelectItem> <SelectGroup>
<SelectItem value='preferred'> <SelectItem value='required'>
{t('Recommended')} {t('Required')}
</SelectItem> </SelectItem>
<SelectItem value='discouraged'> <SelectItem value='preferred'>
{t('Discouraged')} {t('Recommended')}
</SelectItem> </SelectItem>
<SelectItem value='discouraged'>
{t('Discouraged')}
</SelectItem>
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</FormControl> </FormControl>
@ -253,18 +266,28 @@ export function PasskeySection({ defaultValues }: PasskeySectionProps) {
<FormItem> <FormItem>
<FormLabel>{t('Device Type Preference')}</FormLabel> <FormLabel>{t('Device Type Preference')}</FormLabel>
<FormControl> <FormControl>
<Select value={field.value} onValueChange={field.onChange}> <Select
items={[
{ value: 'none', label: t('Unlimited') },
{ value: 'platform', label: t('Built-in Device') },
{ value: 'cross-platform', label: t('External Device') },
]}
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder={t('No preference')} /> <SelectValue placeholder={t('No preference')} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value='none'>{t('Unlimited')}</SelectItem> <SelectGroup>
<SelectItem value='platform'> <SelectItem value='none'>{t('Unlimited')}</SelectItem>
{t('Built-in Device')} <SelectItem value='platform'>
</SelectItem> {t('Built-in Device')}
<SelectItem value='cross-platform'> </SelectItem>
{t('External Device')} <SelectItem value='cross-platform'>
</SelectItem> {t('External Device')}
</SelectItem>
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</FormControl> </FormControl>

View File

@ -39,6 +39,7 @@ import { Input } from '@/components/ui/input'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -513,7 +514,23 @@ export function AnnouncementsSection({
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t('Type')}</FormLabel> <FormLabel>{t('Type')}</FormLabel>
<Select onValueChange={field.onChange} value={field.value}> <Select
items={[
...typeOptions.map((option) => ({
value: option.value,
label: (
<div className='flex items-center gap-2'>
<div
className={`h-3 w-3 rounded-full ${option.color}`}
/>
{option.label}
</div>
),
})),
]}
onValueChange={field.onChange}
value={field.value}
>
<FormControl> <FormControl>
<SelectTrigger> <SelectTrigger>
<SelectValue <SelectValue
@ -521,17 +538,19 @@ export function AnnouncementsSection({
/> />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{typeOptions.map((option) => ( <SelectGroup>
<SelectItem key={option.value} value={option.value}> {typeOptions.map((option) => (
<div className='flex items-center gap-2'> <SelectItem key={option.value} value={option.value}>
<div <div className='flex items-center gap-2'>
className={`h-3 w-3 rounded-full ${option.color}`} <div
/> className={`h-3 w-3 rounded-full ${option.color}`}
{option.label} />
</div> {option.label}
</SelectItem> </div>
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<FormMessage /> <FormMessage />

View File

@ -38,6 +38,7 @@ import { Input } from '@/components/ui/input'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -462,23 +463,41 @@ export function ApiInfoSection({ enabled, data }: ApiInfoSectionProps) {
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t('Badge Color')}</FormLabel> <FormLabel>{t('Badge Color')}</FormLabel>
<Select onValueChange={field.onChange} value={field.value}> <Select
<FormControl> items={[
<SelectTrigger> ...colorOptions.map((option) => ({
<SelectValue placeholder={t('Select a color')} /> value: option.value,
</SelectTrigger> label: (
</FormControl>
<SelectContent>
{colorOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>
<div className='flex items-center gap-2'> <div className='flex items-center gap-2'>
<div <div
className={`h-4 w-4 rounded-full ${option.bgClass}`} className={`h-4 w-4 rounded-full ${option.bgClass}`}
/> />
{option.label} {option.label}
</div> </div>
</SelectItem> ),
))} })),
]}
onValueChange={field.onChange}
value={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder={t('Select a color')} />
</SelectTrigger>
</FormControl>
<SelectContent alignItemWithTrigger={false}>
<SelectGroup>
{colorOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>
<div className='flex items-center gap-2'>
<div
className={`h-4 w-4 rounded-full ${option.bgClass}`}
/>
{option.label}
</div>
</SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<FormDescription> <FormDescription>

View File

@ -17,6 +17,7 @@ import { Input } from '@/components/ui/input'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -129,6 +130,12 @@ export function DashboardSection({ defaultValues }: DashboardSectionProps) {
<FormItem> <FormItem>
<FormLabel>{t('Default time granularity')}</FormLabel> <FormLabel>{t('Default time granularity')}</FormLabel>
<Select <Select
items={[
...granularityOptions.map((option) => ({
value: option.value,
label: option.label,
})),
]}
onValueChange={field.onChange} onValueChange={field.onChange}
value={field.value} value={field.value}
disabled={!isEnabled} disabled={!isEnabled}
@ -138,12 +145,14 @@ export function DashboardSection({ defaultValues }: DashboardSectionProps) {
<SelectValue placeholder={t('Select granularity')} /> <SelectValue placeholder={t('Select granularity')} />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{granularityOptions.map((option) => ( <SelectGroup>
<SelectItem key={option.value} value={option.value}> {granularityOptions.map((option) => (
{option.label} <SelectItem key={option.value} value={option.value}>
</SelectItem> {option.label}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<FormDescription> <FormDescription>

View File

@ -21,6 +21,7 @@ import { Label } from '@/components/ui/label'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -276,6 +277,9 @@ export function RuleEditorDialog(props: Props) {
{keySources.map((src, idx) => ( {keySources.map((src, idx) => (
<div key={idx} className='flex items-center gap-2'> <div key={idx} className='flex items-center gap-2'>
<Select <Select
items={[
...KEY_SOURCE_TYPES.map((t) => ({ value: t, label: t })),
]}
value={src.type} value={src.type}
onValueChange={(v) => { onValueChange={(v) => {
if (v === null) return if (v === null) return
@ -290,12 +294,14 @@ export function RuleEditorDialog(props: Props) {
<SelectTrigger className='w-[160px]'> <SelectTrigger className='w-[160px]'>
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{KEY_SOURCE_TYPES.map((t) => ( <SelectGroup>
<SelectItem key={t} value={t}> {KEY_SOURCE_TYPES.map((t) => (
{t} <SelectItem key={t} value={t}>
</SelectItem> {t}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<Input <Input

View File

@ -18,6 +18,7 @@ import { Input } from '@/components/ui/input'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -162,23 +163,34 @@ export function PricingSection({ defaultValues }: PricingSectionProps) {
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t('Display Mode')}</FormLabel> <FormLabel>{t('Display Mode')}</FormLabel>
<Select value={field.value} onValueChange={field.onChange}> <Select
items={[
{ value: 'USD', label: t('USD') },
{ value: 'CNY', label: t('CNY') },
{ value: 'CUSTOM', label: t('Custom Currency') },
{ value: 'TOKENS', label: t('Tokens Only') },
]}
value={field.value}
onValueChange={field.onChange}
>
<FormControl> <FormControl>
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder={t('Select display mode')} /> <SelectValue placeholder={t('Select display mode')} />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value='USD'>{t('USD')}</SelectItem> <SelectGroup>
<SelectItem value='CNY'>{t('CNY')}</SelectItem> <SelectItem value='USD'>{t('USD')}</SelectItem>
<SelectItem value='CUSTOM'> <SelectItem value='CNY'>{t('CNY')}</SelectItem>
{t('Custom Currency')} <SelectItem value='CUSTOM'>
</SelectItem> {t('Custom Currency')}
{showTokensOnlyOption && (
<SelectItem value='TOKENS'>
{t('Tokens Only')}
</SelectItem> </SelectItem>
)} {showTokensOnlyOption && (
<SelectItem value='TOKENS'>
{t('Tokens Only')}
</SelectItem>
)}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<FormDescription> <FormDescription>

View File

@ -17,6 +17,7 @@ import { Input } from '@/components/ui/input'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -133,19 +134,31 @@ export function SystemInfoSection({ defaultValues }: SystemInfoSectionProps) {
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t('Frontend Theme')}</FormLabel> <FormLabel>{t('Frontend Theme')}</FormLabel>
<Select onValueChange={field.onChange} value={field.value}> <Select
items={[
{ value: 'default', label: t('Default (New Frontend)') },
{
value: 'classic',
label: t('Classic (Legacy Frontend)'),
},
]}
onValueChange={field.onChange}
value={field.value}
>
<FormControl> <FormControl>
<SelectTrigger className='w-full'> <SelectTrigger className='w-full'>
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value='default'> <SelectGroup>
{t('Default (New Frontend)')} <SelectItem value='default'>
</SelectItem> {t('Default (New Frontend)')}
<SelectItem value='classic'> </SelectItem>
{t('Classic (Legacy Frontend)')} <SelectItem value='classic'>
</SelectItem> {t('Classic (Legacy Frontend)')}
</SelectItem>
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<FormDescription> <FormDescription>

View File

@ -25,6 +25,7 @@ import { Input } from '@/components/ui/input'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -160,15 +161,24 @@ export function CreemProductDialog({
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t('Currency')}</FormLabel> <FormLabel>{t('Currency')}</FormLabel>
<Select onValueChange={field.onChange} value={field.value}> <Select
items={[
{ value: 'USD', label: 'USD ($)' },
{ value: 'EUR', label: 'EUR (€)' },
]}
onValueChange={field.onChange}
value={field.value}
>
<FormControl> <FormControl>
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder={t('Select currency')} /> <SelectValue placeholder={t('Select currency')} />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value='USD'>USD ($)</SelectItem> <SelectGroup>
<SelectItem value='EUR'>EUR ()</SelectItem> <SelectItem value='USD'>USD ($)</SelectItem>
<SelectItem value='EUR'>EUR ()</SelectItem>
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<FormMessage /> <FormMessage />

View File

@ -33,6 +33,7 @@ import { Progress } from '@/components/ui/progress'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -508,6 +509,11 @@ export function PerformanceSection(props: Props) {
<FormItem> <FormItem>
<FormLabel>{t('Aggregation bucket')}</FormLabel> <FormLabel>{t('Aggregation bucket')}</FormLabel>
<Select <Select
items={[
{ value: 'minute', label: t('1 minute') },
{ value: '5min', label: t('5 minutes') },
{ value: 'hour', label: t('1 hour') },
]}
value={field.value} value={field.value}
onValueChange={field.onChange} onValueChange={field.onChange}
disabled={!perfMetricsEnabled} disabled={!perfMetricsEnabled}
@ -517,10 +523,12 @@ export function PerformanceSection(props: Props) {
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value='minute'>{t('1 minute')}</SelectItem> <SelectGroup>
<SelectItem value='5min'>{t('5 minutes')}</SelectItem> <SelectItem value='minute'>{t('1 minute')}</SelectItem>
<SelectItem value='hour'>{t('1 hour')}</SelectItem> <SelectItem value='5min'>{t('5 minutes')}</SelectItem>
<SelectItem value='hour'>{t('1 hour')}</SelectItem>
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</FormItem> </FormItem>
@ -605,19 +613,25 @@ export function PerformanceSection(props: Props) {
<div className='grid gap-1.5'> <div className='grid gap-1.5'>
<Label className='text-xs'>{t('Cleanup Mode')}</Label> <Label className='text-xs'>{t('Cleanup Mode')}</Label>
<Select <Select
items={[
{ value: 'by_count', label: t('Retain last N files') },
{ value: 'by_days', label: t('Retain last N days') },
]}
value={logCleanupMode} value={logCleanupMode}
onValueChange={(v) => v !== null && setLogCleanupMode(v)} onValueChange={(v) => v !== null && setLogCleanupMode(v)}
> >
<SelectTrigger className='w-[160px]'> <SelectTrigger className='w-[160px]'>
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value='by_count'> <SelectGroup>
{t('Retain last N files')} <SelectItem value='by_count'>
</SelectItem> {t('Retain last N files')}
<SelectItem value='by_days'> </SelectItem>
{t('Retain last N days')} <SelectItem value='by_days'>
</SelectItem> {t('Retain last N days')}
</SelectItem>
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>

View File

@ -24,6 +24,7 @@ import { Input } from '@/components/ui/input'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -224,18 +225,26 @@ export function ChannelSelectorDialog({
return ( return (
<div className='flex items-center gap-2'> <div className='flex items-center gap-2'>
<Select <Select
items={[
...ENDPOINT_OPTIONS.map((option) => ({
value: option.value,
label: option.label,
})),
]}
value={endpointType} value={endpointType}
onValueChange={(v) => v !== null && handleTypeChange(v)} onValueChange={(v) => v !== null && handleTypeChange(v)}
> >
<SelectTrigger className='h-8 w-32'> <SelectTrigger className='h-8 w-32'>
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{ENDPOINT_OPTIONS.map((option) => ( <SelectGroup>
<SelectItem key={option.value} value={option.value}> {ENDPOINT_OPTIONS.map((option) => (
{option.label} <SelectItem key={option.value} value={option.value}>
</SelectItem> {option.label}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
{endpointType === 'custom' && ( {endpointType === 'custom' && (

View File

@ -18,6 +18,7 @@ import { Input } from '@/components/ui/input'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -176,6 +177,38 @@ function GroupSection(props: GroupSectionProps) {
{props.items.map((rule) => ( {props.items.map((rule) => (
<div key={rule._id} className='flex items-center gap-2'> <div key={rule._id} className='flex items-center gap-2'>
<Select <Select
items={[
{
value: OP_ADD,
label: (
<StatusBadge
label={t(OP_BADGE_MAP[OP_ADD].label)}
variant={OP_BADGE_MAP[OP_ADD].variant}
copyable={false}
/>
),
},
{
value: OP_REMOVE,
label: (
<StatusBadge
label={t(OP_BADGE_MAP[OP_REMOVE].label)}
variant={OP_BADGE_MAP[OP_REMOVE].variant}
copyable={false}
/>
),
},
{
value: OP_APPEND,
label: (
<StatusBadge
label={t(OP_BADGE_MAP[OP_APPEND].label)}
variant={OP_BADGE_MAP[OP_APPEND].variant}
copyable={false}
/>
),
},
]}
value={rule.op} value={rule.op}
onValueChange={(v) => onValueChange={(v) =>
v !== null && props.onUpdate(rule._id, 'op', v) v !== null && props.onUpdate(rule._id, 'op', v)
@ -190,28 +223,30 @@ function GroupSection(props: GroupSectionProps) {
/> />
</SelectValue> </SelectValue>
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value={OP_ADD}> <SelectGroup>
<StatusBadge <SelectItem value={OP_ADD}>
label={t(OP_BADGE_MAP[OP_ADD].label)} <StatusBadge
variant={OP_BADGE_MAP[OP_ADD].variant} label={t(OP_BADGE_MAP[OP_ADD].label)}
copyable={false} variant={OP_BADGE_MAP[OP_ADD].variant}
/> copyable={false}
</SelectItem> />
<SelectItem value={OP_REMOVE}> </SelectItem>
<StatusBadge <SelectItem value={OP_REMOVE}>
label={t(OP_BADGE_MAP[OP_REMOVE].label)} <StatusBadge
variant={OP_BADGE_MAP[OP_REMOVE].variant} label={t(OP_BADGE_MAP[OP_REMOVE].label)}
copyable={false} variant={OP_BADGE_MAP[OP_REMOVE].variant}
/> copyable={false}
</SelectItem> />
<SelectItem value={OP_APPEND}> </SelectItem>
<StatusBadge <SelectItem value={OP_APPEND}>
label={t(OP_BADGE_MAP[OP_APPEND].label)} <StatusBadge
variant={OP_BADGE_MAP[OP_APPEND].variant} label={t(OP_BADGE_MAP[OP_APPEND].label)}
copyable={false} variant={OP_BADGE_MAP[OP_APPEND].variant}
/> copyable={false}
</SelectItem> />
</SelectItem>
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<Input <Input

View File

@ -27,6 +27,7 @@ import { Label } from '@/components/ui/label'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -415,6 +416,12 @@ function ConditionRow({ condition, onChange, onRemove }: ConditionRowProps) {
return ( return (
<div className='flex items-center gap-2'> <div className='flex items-center gap-2'>
<Select <Select
items={[
...CONDITION_INPUT_OPTIONS.map((option) => ({
value: option.value,
label: t(option.labelKey),
})),
]}
value={condition.var} value={condition.var}
onValueChange={(value) => onValueChange={(value) =>
onChange({ ...condition, var: value as TierConditionInput['var'] }) onChange({ ...condition, var: value as TierConditionInput['var'] })
@ -427,15 +434,18 @@ function ConditionRow({ condition, onChange, onRemove }: ConditionRowProps) {
: condition.var} : condition.var}
</SelectValue> </SelectValue>
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{CONDITION_INPUT_OPTIONS.map((option) => ( <SelectGroup>
<SelectItem key={option.value} value={option.value}> {CONDITION_INPUT_OPTIONS.map((option) => (
{t(option.labelKey)} <SelectItem key={option.value} value={option.value}>
</SelectItem> {t(option.labelKey)}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<Select <Select
items={[...OPS.map((op) => ({ value: op, label: op }))]}
value={condition.op} value={condition.op}
onValueChange={(value) => onValueChange={(value) =>
onChange({ ...condition, op: value as TierConditionInput['op'] }) onChange({ ...condition, op: value as TierConditionInput['op'] })
@ -444,12 +454,14 @@ function ConditionRow({ condition, onChange, onRemove }: ConditionRowProps) {
<SelectTrigger className='w-20' size='sm'> <SelectTrigger className='w-20' size='sm'>
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{OPS.map((op) => ( <SelectGroup>
<SelectItem key={op} value={op}> {OPS.map((op) => (
{op} <SelectItem key={op} value={op}>
</SelectItem> {op}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<DraftNumberInput <DraftNumberInput
@ -960,6 +972,12 @@ function RuleConditionRow({
const renderTimeCondition = (timeCond: TimeCondition) => ( const renderTimeCondition = (timeCond: TimeCondition) => (
<> <>
<Select <Select
items={[
...TIME_FUNCS.map((fn) => ({
value: fn,
label: getTimeFuncLabel(fn),
})),
]}
value={timeCond.timeFunc} value={timeCond.timeFunc}
onValueChange={(value) => onValueChange={(value) =>
onChange({ ...timeCond, timeFunc: value as TimeFunc }) onChange({ ...timeCond, timeFunc: value as TimeFunc })
@ -968,15 +986,23 @@ function RuleConditionRow({
<SelectTrigger className='w-32' size='sm'> <SelectTrigger className='w-32' size='sm'>
<SelectValue>{getTimeFuncLabel(timeCond.timeFunc)}</SelectValue> <SelectValue>{getTimeFuncLabel(timeCond.timeFunc)}</SelectValue>
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{TIME_FUNCS.map((fn) => ( <SelectGroup>
<SelectItem key={fn} value={fn}> {TIME_FUNCS.map((fn) => (
{getTimeFuncLabel(fn)} <SelectItem key={fn} value={fn}>
</SelectItem> {getTimeFuncLabel(fn)}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<Select <Select
items={[
...COMMON_TIMEZONES.map((tz) => ({
value: tz.value,
label: tz.label,
})),
]}
value={timeCond.timezone} value={timeCond.timezone}
onValueChange={(value) => onValueChange={(value) =>
value !== null && onChange({ ...timeCond, timezone: value }) value !== null && onChange({ ...timeCond, timezone: value })
@ -988,27 +1014,37 @@ function RuleConditionRow({
?.label ?? timeCond.timezone} ?.label ?? timeCond.timezone}
</SelectValue> </SelectValue>
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{COMMON_TIMEZONES.map((tz) => ( <SelectGroup>
<SelectItem key={tz.value} value={tz.value}> {COMMON_TIMEZONES.map((tz) => (
{tz.label} <SelectItem key={tz.value} value={tz.value}>
</SelectItem> {tz.label}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<Select <Select
items={[
...matchOptions.map((option) => ({
value: option.value,
label: getMatchLabel(option.value),
})),
]}
value={timeCond.mode} value={timeCond.mode}
onValueChange={(v) => v !== null && handleModeChange(v)} onValueChange={(v) => v !== null && handleModeChange(v)}
> >
<SelectTrigger className='w-32' size='sm'> <SelectTrigger className='w-32' size='sm'>
<SelectValue>{getMatchLabel(timeCond.mode)}</SelectValue> <SelectValue>{getMatchLabel(timeCond.mode)}</SelectValue>
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{matchOptions.map((option) => ( <SelectGroup>
<SelectItem key={option.value} value={option.value}> {matchOptions.map((option) => (
{getMatchLabel(option.value)} <SelectItem key={option.value} value={option.value}>
</SelectItem> {getMatchLabel(option.value)}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
{timeCond.mode === MATCH_RANGE ? ( {timeCond.mode === MATCH_RANGE ? (
@ -1055,18 +1091,26 @@ function RuleConditionRow({
className='w-44' className='w-44'
/> />
<Select <Select
items={[
...matchOptions.map((option) => ({
value: option.value,
label: getMatchLabel(option.value),
})),
]}
value={phCond.mode} value={phCond.mode}
onValueChange={(v) => v !== null && handleModeChange(v)} onValueChange={(v) => v !== null && handleModeChange(v)}
> >
<SelectTrigger className='w-32' size='sm'> <SelectTrigger className='w-32' size='sm'>
<SelectValue>{getMatchLabel(phCond.mode)}</SelectValue> <SelectValue>{getMatchLabel(phCond.mode)}</SelectValue>
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{matchOptions.map((option) => ( <SelectGroup>
<SelectItem key={option.value} value={option.value}> {matchOptions.map((option) => (
{getMatchLabel(option.value)} <SelectItem key={option.value} value={option.value}>
</SelectItem> {getMatchLabel(option.value)}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
{phCond.mode !== MATCH_EXISTS && ( {phCond.mode !== MATCH_EXISTS && (
@ -1085,16 +1129,23 @@ function RuleConditionRow({
return ( return (
<div className='flex flex-wrap items-center gap-2'> <div className='flex flex-wrap items-center gap-2'>
<Select <Select
items={[
{ value: SOURCE_PARAM, label: t('Body param') },
{ value: SOURCE_HEADER, label: t('Header') },
{ value: SOURCE_TIME, label: t('Time') },
]}
value={condition.source} value={condition.source}
onValueChange={(v) => v !== null && handleSourceChange(v)} onValueChange={(v) => v !== null && handleSourceChange(v)}
> >
<SelectTrigger className='w-28' size='sm'> <SelectTrigger className='w-28' size='sm'>
<SelectValue>{sourceLabel}</SelectValue> <SelectValue>{sourceLabel}</SelectValue>
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value={SOURCE_PARAM}>{t('Body param')}</SelectItem> <SelectGroup>
<SelectItem value={SOURCE_HEADER}>{t('Header')}</SelectItem> <SelectItem value={SOURCE_PARAM}>{t('Body param')}</SelectItem>
<SelectItem value={SOURCE_TIME}>{t('Time')}</SelectItem> <SelectItem value={SOURCE_HEADER}>{t('Header')}</SelectItem>
<SelectItem value={SOURCE_TIME}>{t('Time')}</SelectItem>
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
{condition.source === SOURCE_TIME {condition.source === SOURCE_TIME
@ -1705,15 +1756,21 @@ export const TieredPricingEditor = memo(function TieredPricingEditor({
<div className='flex items-center justify-between gap-2'> <div className='flex items-center justify-between gap-2'>
<Label className='text-xs'>{t('Editor mode')}</Label> <Label className='text-xs'>{t('Editor mode')}</Label>
<Select <Select
items={[
{ value: 'visual', label: t('Visual editor') },
{ value: 'raw', label: t('Expression editor') },
]}
value={editorMode} value={editorMode}
onValueChange={(value) => handleModeChange(value as EditorMode)} onValueChange={(value) => handleModeChange(value as EditorMode)}
> >
<SelectTrigger className='w-44' size='sm'> <SelectTrigger className='w-44' size='sm'>
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value='visual'>{t('Visual editor')}</SelectItem> <SelectGroup>
<SelectItem value='raw'>{t('Expression editor')}</SelectItem> <SelectItem value='visual'>{t('Visual editor')}</SelectItem>
<SelectItem value='raw'>{t('Expression editor')}</SelectItem>
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>

View File

@ -11,6 +11,7 @@ import { Input } from '@/components/ui/input'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -212,6 +213,13 @@ export function UpstreamRatioSyncTable({
/> />
</div> </div>
<Select <Select
items={[
{ value: '__all__', label: t('All Types') },
...RATIO_TYPE_OPTIONS.map((option) => ({
value: option.value,
label: t(option.label),
})),
]}
value={ratioTypeFilter} value={ratioTypeFilter}
onValueChange={(v) => v !== null && setRatioTypeFilter(v)} onValueChange={(v) => v !== null && setRatioTypeFilter(v)}
disabled={isDisabled} disabled={isDisabled}
@ -219,13 +227,15 @@ export function UpstreamRatioSyncTable({
<SelectTrigger className='w-full sm:w-56'> <SelectTrigger className='w-full sm:w-56'>
<SelectValue placeholder={t('Filter by price field')} /> <SelectValue placeholder={t('Filter by price field')} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value='__all__'>{t('All Types')}</SelectItem> <SelectGroup>
{RATIO_TYPE_OPTIONS.map((option) => ( <SelectItem value='__all__'>{t('All Types')}</SelectItem>
<SelectItem key={option.value} value={option.value}> {RATIO_TYPE_OPTIONS.map((option) => (
{t(option.label)} <SelectItem key={option.value} value={option.value}>
</SelectItem> {t(option.label)}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>

View File

@ -18,6 +18,7 @@ import { Input } from '@/components/ui/input'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -242,6 +243,16 @@ export function SSRFSection({ defaultValues }: SSRFSectionProps) {
<FormItem> <FormItem>
<FormLabel>{t('Domain Filter Mode')}</FormLabel> <FormLabel>{t('Domain Filter Mode')}</FormLabel>
<Select <Select
items={[
{
value: 'false',
label: t('Blacklist (Block listed domains)'),
},
{
value: 'true',
label: t('Whitelist (Only allow listed domains)'),
},
]}
onValueChange={(value) => field.onChange(value === 'true')} onValueChange={(value) => field.onChange(value === 'true')}
value={field.value ? 'true' : 'false'} value={field.value ? 'true' : 'false'}
> >
@ -250,13 +261,15 @@ export function SSRFSection({ defaultValues }: SSRFSectionProps) {
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value='false'> <SelectGroup>
{t('Blacklist (Block listed domains)')} <SelectItem value='false'>
</SelectItem> {t('Blacklist (Block listed domains)')}
<SelectItem value='true'> </SelectItem>
{t('Whitelist (Only allow listed domains)')} <SelectItem value='true'>
</SelectItem> {t('Whitelist (Only allow listed domains)')}
</SelectItem>
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<FormDescription> <FormDescription>
@ -295,6 +308,16 @@ export function SSRFSection({ defaultValues }: SSRFSectionProps) {
<FormItem> <FormItem>
<FormLabel>{t('IP Filter Mode')}</FormLabel> <FormLabel>{t('IP Filter Mode')}</FormLabel>
<Select <Select
items={[
{
value: 'false',
label: t('Blacklist (Block listed IPs)'),
},
{
value: 'true',
label: t('Whitelist (Only allow listed IPs)'),
},
]}
onValueChange={(value) => field.onChange(value === 'true')} onValueChange={(value) => field.onChange(value === 'true')}
value={field.value ? 'true' : 'false'} value={field.value ? 'true' : 'false'}
> >
@ -303,13 +326,15 @@ export function SSRFSection({ defaultValues }: SSRFSectionProps) {
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value='false'> <SelectGroup>
{t('Blacklist (Block listed IPs)')} <SelectItem value='false'>
</SelectItem> {t('Blacklist (Block listed IPs)')}
<SelectItem value='true'> </SelectItem>
{t('Whitelist (Only allow listed IPs)')} <SelectItem value='true'>
</SelectItem> {t('Whitelist (Only allow listed IPs)')}
</SelectItem>
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<FormDescription> <FormDescription>

View File

@ -7,18 +7,19 @@ import { useTranslation } from 'react-i18next'
import { useIsAdmin } from '@/hooks/use-admin' import { useIsAdmin } from '@/hooks/use-admin'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input' import { Input } from '@/components/ui/input'
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from '@/components/ui/tooltip'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from '@/components/ui/select' } from '@/components/ui/select'
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from '@/components/ui/tooltip'
import { DataTableToolbar } from '@/components/data-table' import { DataTableToolbar } from '@/components/data-table'
import { LOG_TYPES } from '../constants' import { LOG_TYPES } from '../constants'
import { buildSearchParams } from '../lib/filter' import { buildSearchParams } from '../lib/filter'
@ -207,6 +208,13 @@ export function CommonLogsFilterBar<TData>(
className={inputClass} className={inputClass}
/> />
<Select <Select
items={[
{ value: 'all', label: t('All Types') },
...LOG_TYPES.map((type) => ({
value: String(type.value),
label: t(type.label),
})),
]}
value={logType} value={logType}
onValueChange={(value) => { onValueChange={(value) => {
setLogType(value !== null && isLogTypeValue(value) ? value : '') setLogType(value !== null && isLogTypeValue(value) ? value : '')
@ -215,13 +223,15 @@ export function CommonLogsFilterBar<TData>(
<SelectTrigger className={inputClass}> <SelectTrigger className={inputClass}>
<SelectValue placeholder={t('All Types')} /> <SelectValue placeholder={t('All Types')} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value='all'>{t('All Types')}</SelectItem> <SelectGroup>
{LOG_TYPES.map((type) => ( <SelectItem value='all'>{t('All Types')}</SelectItem>
<SelectItem key={type.value} value={String(type.value)}> {LOG_TYPES.map((type) => (
{t(type.label)} <SelectItem key={type.value} value={String(type.value)}>
</SelectItem> {t(type.label)}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</> </>

View File

@ -22,6 +22,7 @@ import { Label } from '@/components/ui/label'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -201,6 +202,10 @@ export function UsersMutateDrawer({
<FormItem> <FormItem>
<FormLabel>{t('Role')}</FormLabel> <FormLabel>{t('Role')}</FormLabel>
<Select <Select
items={[
{ value: '1', label: t('Common User') },
{ value: '10', label: t('Admin') },
]}
onValueChange={(value) => onValueChange={(value) =>
value !== null && field.onChange(parseInt(value)) value !== null && field.onChange(parseInt(value))
} }
@ -211,11 +216,13 @@ export function UsersMutateDrawer({
<SelectValue placeholder={t('Select a role')} /> <SelectValue placeholder={t('Select a role')} />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value='1'> <SelectGroup>
{t('Common User')} <SelectItem value='1'>
</SelectItem> {t('Common User')}
<SelectItem value='10'>{t('Admin')}</SelectItem> </SelectItem>
<SelectItem value='10'>{t('Admin')}</SelectItem>
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<FormDescription> <FormDescription>
@ -282,6 +289,12 @@ export function UsersMutateDrawer({
<FormItem> <FormItem>
<FormLabel>{t('Group')}</FormLabel> <FormLabel>{t('Group')}</FormLabel>
<Select <Select
items={[
...groups.map((group) => ({
value: group,
label: group,
})),
]}
onValueChange={field.onChange} onValueChange={field.onChange}
value={field.value} value={field.value}
> >
@ -290,12 +303,14 @@ export function UsersMutateDrawer({
<SelectValue placeholder={t('Select a group')} /> <SelectValue placeholder={t('Select a group')} />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
{groups.map((group) => ( <SelectGroup>
<SelectItem key={group} value={group}> {groups.map((group) => (
{group} <SelectItem key={group} value={group}>
</SelectItem> {group}
))} </SelectItem>
))}
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<FormMessage /> <FormMessage />

View File

@ -28,6 +28,7 @@ import { ScrollArea } from '@/components/ui/scroll-area'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -104,6 +105,12 @@ export function BillingHistoryDialog({
/> />
</div> </div>
<Select <Select
items={[
{ value: '10', label: t('10 / page') },
{ value: '20', label: t('20 / page') },
{ value: '50', label: t('50 / page') },
{ value: '100', label: t('100 / page') },
]}
value={pageSize.toString()} value={pageSize.toString()}
onValueChange={(value) => onValueChange={(value) =>
value !== null && handlePageSizeChange(parseInt(value)) value !== null && handlePageSizeChange(parseInt(value))
@ -112,11 +119,13 @@ export function BillingHistoryDialog({
<SelectTrigger className='h-9 w-[92px] sm:w-32'> <SelectTrigger className='h-9 w-[92px] sm:w-32'>
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value='10'>{t('10 / page')}</SelectItem> <SelectGroup>
<SelectItem value='20'>{t('20 / page')}</SelectItem> <SelectItem value='10'>{t('10 / page')}</SelectItem>
<SelectItem value='50'>{t('50 / page')}</SelectItem> <SelectItem value='20'>{t('20 / page')}</SelectItem>
<SelectItem value='100'>{t('100 / page')}</SelectItem> <SelectItem value='50'>{t('50 / page')}</SelectItem>
<SelectItem value='100'>{t('100 / page')}</SelectItem>
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>

View File

@ -10,6 +10,7 @@ import { Progress } from '@/components/ui/progress'
import { import {
Select, Select,
SelectContent, SelectContent,
SelectGroup,
SelectItem, SelectItem,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
@ -276,6 +277,34 @@ export function SubscriptionPlansCard({
</div> </div>
<div className='flex w-full items-center gap-2 sm:w-auto'> <div className='flex w-full items-center gap-2 sm:w-auto'>
<Select <Select
items={[
{
value: 'subscription_first',
label: (
<>
{getBillingPreferenceLabel('subscription_first', t)}
{disablePref ? ` (${t('No Active')})` : ''}
</>
),
},
{
value: 'wallet_first',
label: getBillingPreferenceLabel('wallet_first', t),
},
{
value: 'subscription_only',
label: (
<>
{getBillingPreferenceLabel('subscription_only', t)}
{disablePref ? ` (${t('No Active')})` : ''}
</>
),
},
{
value: 'wallet_only',
label: getBillingPreferenceLabel('wallet_only', t),
},
]}
value={displayPref} value={displayPref}
onValueChange={(v) => v !== null && handlePreferenceChange(v)} onValueChange={(v) => v !== null && handlePreferenceChange(v)}
> >
@ -284,21 +313,29 @@ export function SubscriptionPlansCard({
{getBillingPreferenceLabel(displayPref, t)} {getBillingPreferenceLabel(displayPref, t)}
</SelectValue> </SelectValue>
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent alignItemWithTrigger={false}>
<SelectItem value='subscription_first' disabled={disablePref}> <SelectGroup>
{getBillingPreferenceLabel('subscription_first', t)} <SelectItem
{disablePref ? ` (${t('No Active')})` : ''} value='subscription_first'
</SelectItem> disabled={disablePref}
<SelectItem value='wallet_first'> >
{getBillingPreferenceLabel('wallet_first', t)} {getBillingPreferenceLabel('subscription_first', t)}
</SelectItem> {disablePref ? ` (${t('No Active')})` : ''}
<SelectItem value='subscription_only' disabled={disablePref}> </SelectItem>
{getBillingPreferenceLabel('subscription_only', t)} <SelectItem value='wallet_first'>
{disablePref ? ` (${t('No Active')})` : ''} {getBillingPreferenceLabel('wallet_first', t)}
</SelectItem> </SelectItem>
<SelectItem value='wallet_only'> <SelectItem
{getBillingPreferenceLabel('wallet_only', t)} value='subscription_only'
</SelectItem> disabled={disablePref}
>
{getBillingPreferenceLabel('subscription_only', t)}
{disablePref ? ` (${t('No Active')})` : ''}
</SelectItem>
<SelectItem value='wallet_only'>
{getBillingPreferenceLabel('wallet_only', t)}
</SelectItem>
</SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
<Button <Button