Browse Source

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

yyhhyyyyyy 1 week ago
parent
commit
d98f0e8ac3
38 changed files with 1187 additions and 485 deletions
  1. 15 6
      web/default/src/components/data-table/pagination.tsx
  2. 1 1
      web/default/src/components/ui/select.tsx
  3. 18 9
      web/default/src/features/channels/components/dialogs/channel-test-dialog.tsx
  4. 17 8
      web/default/src/features/channels/components/dialogs/edit-tag-dialog.tsx
  5. 15 6
      web/default/src/features/channels/components/dialogs/multi-key-manage-dialog.tsx
  6. 105 44
      web/default/src/features/channels/components/dialogs/param-override-editor-dialog.tsx
  7. 107 45
      web/default/src/features/channels/components/drawers/channel-mutate-drawer.tsx
  8. 57 24
      web/default/src/features/dashboard/components/models/models-chart-preferences.tsx
  9. 15 6
      web/default/src/features/dashboard/components/models/models-filter-dialog.tsx
  10. 24 9
      web/default/src/features/models/components/dialogs/create-deployment-drawer.tsx
  11. 15 6
      web/default/src/features/models/components/dialogs/upstream-conflict-dialog.tsx
  12. 50 19
      web/default/src/features/models/components/dialogs/view-logs-dialog.tsx
  13. 32 12
      web/default/src/features/models/components/drawers/model-mutate-drawer.tsx
  14. 35 14
      web/default/src/features/models/components/drawers/prefill-group-form-drawer.tsx
  15. 15 6
      web/default/src/features/profile/components/language-preferences-card.tsx
  16. 15 6
      web/default/src/features/subscriptions/components/dialogs/subscription-purchase-dialog.tsx
  17. 21 7
      web/default/src/features/subscriptions/components/dialogs/user-subscriptions-dialog.tsx
  18. 43 20
      web/default/src/features/subscriptions/components/subscriptions-mutate-drawer.tsx
  19. 15 6
      web/default/src/features/system-settings/auth/custom-oauth/components/preset-selector.tsx
  20. 18 9
      web/default/src/features/system-settings/auth/custom-oauth/components/provider-form-dialog.tsx
  21. 41 18
      web/default/src/features/system-settings/auth/passkey-section.tsx
  22. 31 12
      web/default/src/features/system-settings/content/announcements-section.tsx
  23. 30 11
      web/default/src/features/system-settings/content/api-info-section.tsx
  24. 15 6
      web/default/src/features/system-settings/content/dashboard-section.tsx
  25. 12 6
      web/default/src/features/system-settings/general/channel-affinity/rule-editor-dialog.tsx
  26. 23 11
      web/default/src/features/system-settings/general/pricing-section.tsx
  27. 21 8
      web/default/src/features/system-settings/general/system-info-section.tsx
  28. 14 4
      web/default/src/features/system-settings/integrations/creem-product-dialog.tsx
  29. 25 11
      web/default/src/features/system-settings/maintenance/performance-section.tsx
  30. 15 6
      web/default/src/features/system-settings/models/channel-selector-dialog.tsx
  31. 57 22
      web/default/src/features/system-settings/models/group-special-usable-editor.tsx
  32. 100 43
      web/default/src/features/system-settings/models/tiered-pricing-editor.tsx
  33. 17 7
      web/default/src/features/system-settings/models/upstream-ratio-sync-table.tsx
  34. 39 14
      web/default/src/features/system-settings/request-limits/ssrf-section.tsx
  35. 22 12
      web/default/src/features/usage-logs/components/common-logs-filter-bar.tsx
  36. 26 11
      web/default/src/features/users/components/users-mutate-drawer.tsx
  37. 14 5
      web/default/src/features/wallet/components/dialogs/billing-history-dialog.tsx
  38. 52 15
      web/default/src/features/wallet/components/subscription-plans-card.tsx

+ 15 - 6
web/default/src/components/data-table/pagination.tsx

@@ -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'>

+ 1 - 1
web/default/src/components/ui/select.tsx

@@ -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}

+ 18 - 9
web/default/src/features/channels/components/dialogs/channel-test-dialog.tsx

@@ -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'>

+ 17 - 8
web/default/src/features/channels/components/dialogs/edit-tag-dialog.tsx

@@ -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>

+ 15 - 6
web/default/src/features/channels/components/dialogs/multi-key-manage-dialog.tsx

@@ -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>
 

+ 105 - 44
web/default/src/features/channels/components/dialogs/param-override-editor-dialog.tsx

@@ -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

+ 107 - 45
web/default/src/features/channels/components/drawers/channel-mutate-drawer.tsx

@@ -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>

+ 57 - 24
web/default/src/features/dashboard/components/models/models-chart-preferences.tsx

@@ -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>

+ 15 - 6
web/default/src/features/dashboard/components/models/models-filter-dialog.tsx

@@ -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>

+ 24 - 9
web/default/src/features/models/components/dialogs/create-deployment-drawer.tsx

@@ -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>

+ 15 - 6
web/default/src/features/models/components/dialogs/upstream-conflict-dialog.tsx

@@ -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>

+ 50 - 19
web/default/src/features/models/components/dialogs/view-logs-dialog.tsx

@@ -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>

+ 32 - 12
web/default/src/features/models/components/drawers/model-mutate-drawer.tsx

@@ -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>

+ 35 - 14
web/default/src/features/models/components/drawers/prefill-group-form-drawer.tsx

@@ -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>

+ 15 - 6
web/default/src/features/profile/components/language-preferences-card.tsx

@@ -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 && (

+ 15 - 6
web/default/src/features/subscriptions/components/dialogs/subscription-purchase-dialog.tsx

@@ -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

+ 21 - 7
web/default/src/features/subscriptions/components/dialogs/user-subscriptions-dialog.tsx

@@ -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

+ 43 - 20
web/default/src/features/subscriptions/components/subscriptions-mutate-drawer.tsx

@@ -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 />

+ 15 - 6
web/default/src/features/system-settings/auth/custom-oauth/components/preset-selector.tsx

@@ -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>

+ 18 - 9
web/default/src/features/system-settings/auth/custom-oauth/components/provider-form-dialog.tsx

@@ -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>

+ 41 - 18
web/default/src/features/system-settings/auth/passkey-section.tsx

@@ -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>

+ 31 - 12
web/default/src/features/system-settings/content/announcements-section.tsx

@@ -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 />

+ 30 - 11
web/default/src/features/system-settings/content/api-info-section.tsx

@@ -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>

+ 15 - 6
web/default/src/features/system-settings/content/dashboard-section.tsx

@@ -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>

+ 12 - 6
web/default/src/features/system-settings/general/channel-affinity/rule-editor-dialog.tsx

@@ -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

+ 23 - 11
web/default/src/features/system-settings/general/pricing-section.tsx

@@ -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>

+ 21 - 8
web/default/src/features/system-settings/general/system-info-section.tsx

@@ -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>

+ 14 - 4
web/default/src/features/system-settings/integrations/creem-product-dialog.tsx

@@ -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 />

+ 25 - 11
web/default/src/features/system-settings/maintenance/performance-section.tsx

@@ -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>

+ 15 - 6
web/default/src/features/system-settings/models/channel-selector-dialog.tsx

@@ -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' && (

+ 57 - 22
web/default/src/features/system-settings/models/group-special-usable-editor.tsx

@@ -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

+ 100 - 43
web/default/src/features/system-settings/models/tiered-pricing-editor.tsx

@@ -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>

+ 17 - 7
web/default/src/features/system-settings/models/upstream-ratio-sync-table.tsx

@@ -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>

+ 39 - 14
web/default/src/features/system-settings/request-limits/ssrf-section.tsx

@@ -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>

+ 22 - 12
web/default/src/features/usage-logs/components/common-logs-filter-bar.tsx

@@ -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>
         </>

+ 26 - 11
web/default/src/features/users/components/users-mutate-drawer.tsx

@@ -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 />

+ 14 - 5
web/default/src/features/wallet/components/dialogs/billing-history-dialog.tsx

@@ -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>

+ 52 - 15
web/default/src/features/wallet/components/subscription-plans-card.tsx

@@ -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