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

View File

@ -93,7 +93,7 @@ function SelectContent({
data-slot='select-content'
data-align-trigger={alignItemWithTrigger}
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
)}
{...props}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -36,6 +36,7 @@ import {
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
@ -550,6 +551,12 @@ export function UpstreamConflictDialog({
{t('Rows per page')}
</span>
<Select
items={[
...[5, 10, 20, 50].map((size) => ({
value: String(size),
label: size,
})),
]}
value={String(pageSize)}
onValueChange={(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]'>
<SelectValue />
</SelectTrigger>
<SelectContent>
{[5, 10, 20, 50].map((size) => (
<SelectItem key={size} value={String(size)}>
{size}
</SelectItem>
))}
<SelectContent alignItemWithTrigger={false}>
<SelectGroup>
{[5, 10, 20, 50].map((size) => (
<SelectItem key={size} value={String(size)}>
{size}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
</div>

View File

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

View File

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

View File

@ -19,6 +19,7 @@ import { Input } from '@/components/ui/input'
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
@ -252,6 +253,22 @@ export function PrefillGroupFormDrawer({
<FormItem>
<FormLabel>Group Type</FormLabel>
<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}
onValueChange={(value) =>
value !== null &&
@ -263,20 +280,24 @@ export function PrefillGroupFormDrawer({
<SelectValue placeholder={t('Select a group type')} />
</SelectTrigger>
</FormControl>
<SelectContent>
{PREFILL_GROUP_TYPES.map((type) => (
<SelectItem key={type.value} value={type.value}>
<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>
</SelectItem>
))}
<SelectContent alignItemWithTrigger={false}>
<SelectGroup>
{PREFILL_GROUP_TYPES.map((type) => (
<SelectItem key={type.value} value={type.value}>
<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>
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
<FormDescription>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -25,6 +25,7 @@ import { Input } from '@/components/ui/input'
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
@ -160,15 +161,24 @@ export function CreemProductDialog({
render={({ field }) => (
<FormItem>
<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>
<SelectTrigger>
<SelectValue placeholder={t('Select currency')} />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value='USD'>USD ($)</SelectItem>
<SelectItem value='EUR'>EUR ()</SelectItem>
<SelectContent alignItemWithTrigger={false}>
<SelectGroup>
<SelectItem value='USD'>USD ($)</SelectItem>
<SelectItem value='EUR'>EUR ()</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<FormMessage />

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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