Skip to content

feat: enhance provider group selection in users page [#423] - #424

Merged
ding113 merged 2 commits into
ding113:devfrom
Hwwwww-dev:feat/users-provider-group-selection
Dec 24, 2025
Merged

feat: enhance provider group selection in users page [#423]#424
ding113 merged 2 commits into
ding113:devfrom
Hwwwww-dev:feat/users-provider-group-selection

Conversation

@Hwwwww-dev

@Hwwwww-dev Hwwwww-dev commented Dec 23, 2025

Copy link
Copy Markdown
Contributor

Summary

Enhances provider group selection in the users page by adding multi-select capability for non-admin key creation and improving UX with auto-removal of the "default" group when specific groups are selected.

Problem

When non-admin users create API keys, they can only select a single provider group via dropdown. This limits flexibility since users may have access to multiple provider groups and want to assign multiple groups to a new key.

Fixes #423 - 用户模式创建 KEY 时供应商分组优化 (Optimize provider group selection when users create keys)

Solution

1. Multi-Select for Non-Admin Key Creation

Replaced the single-select dropdown with TagInputField component that supports multi-select:

  • Users can now select multiple provider groups from their available groups
  • Groups display as tags that can be easily added/removed
  • Dropdown stays open during multi-selection for better UX

2. Auto-Remove "default" Group Logic

Added smart default group handling:

  • When user selects a specific group, the "default" group is automatically removed
  • This prevents mixing "default" with specific groups which could lead to confusing routing behavior

3. Read-Only Display for Existing Keys

Changed behavior when editing existing keys:

  • Provider groups now display as read-only badges instead of a disabled dropdown
  • Clearer visual indication that existing key groups cannot be modified

4. UI Polish

  • Tag-input dropdown now stays open when clicking suggestions (improved multi-select UX)
  • Key row grid layout adjusted for better group/expiry column balance
  • Shows "+N" for remaining groups when more than 1 group exists

Changes

Core Changes

  • src/app/[locale]/dashboard/_components/user/forms/key-edit-section.tsx - Switch to TagInputField for multi-select, add auto-remove default logic
  • src/app/[locale]/dashboard/_components/user/forms/provider-group-select.tsx - Add auto-remove "default" when selecting other groups
  • src/components/ui/tag-input.tsx - Keep dropdown open during multi-selection

Supporting Changes

  • src/app/[locale]/dashboard/_components/user/forms/user-edit-section.tsx - Display provider group as read-only badges
  • src/app/[locale]/dashboard/_components/user/key-row-item.tsx - Adjust grid layout, show +N for excess groups
  • public/seed/litellm-prices.json - Updated model pricing data (unrelated maintenance)

Related PRs

Testing

Manual Testing

  1. Multi-Select Key Creation:

    • Login as non-admin user with multiple provider groups
    • Click "Create Key"
    • Verify multiple groups can be selected from the dropdown
    • Verify dropdown stays open when selecting items
  2. Auto-Remove Default:

    • Create a key starting with "default" group
    • Select a specific group (e.g., "premium")
    • Verify "default" is automatically removed
  3. Read-Only Edit Mode:

    • Edit an existing key
    • Verify provider groups show as read-only badges
    • Verify no dropdown/selection appears

Checklist

  • Code follows project conventions
  • Self-review completed
  • Maintains backward compatibility
  • Builds on existing provider group infrastructure

Description enhanced by Claude AI

image image image

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @Hwwwww-dev, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly refines the user interface and functionality for managing provider groups within the application's users page. It introduces a more intuitive multi-selection experience for creating API keys, ensures smart handling of the 'default' provider group, and enhances the visual presentation of assigned groups across various user and key management views. These changes aim to streamline the process of assigning and viewing provider groups, improving overall usability.

Highlights

  • Enhanced Provider Group Selection: Introduced ProviderGroupSelect for admin user/key editing and multi-select for non-admin key creation using TagInputField.
  • Intelligent 'default' Group Handling: Automatically removes the 'default' group when other groups are selected during key creation.
  • Improved Multi-selection UX: The dropdown for multi-selection in TagInputField now remains open after selecting an item.
  • Read-only Group Display: User provider groups are now displayed as read-only badges in user and key edit sections.
  • Optimized Key Row Layout: Adjusted the grid layout for key row items to better balance group and expiry columns, and show '+N' for remaining groups more concisely.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request enhances the provider group selection by introducing multi-select capabilities, automatically handling the 'default' group, and improving the UI for displaying groups. The changes are generally good, but I've found a recurring logic issue in how provider group strings are processed, which could lead to saving malformed data. I've also identified some dead code that can be cleaned up. My review includes suggestions to fix the bug and improve code quality.

Comment on lines +300 to +315
const handleUserProviderGroupChange = useCallback(
(newValue: string) => {
const groups = newValue
.split(",")
.map((g) => g.trim())
.filter(Boolean);
// 如果有多个分组且包含 default,移除 default
if (groups.length > 1 && groups.includes(PROVIDER_GROUP.DEFAULT)) {
const withoutDefault = groups.filter((g) => g !== PROVIDER_GROUP.DEFAULT);
onChange("providerGroup", withoutDefault.join(","));
} else {
onChange("providerGroup", newValue);
}
},
[onChange]
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The handleUserProviderGroupChange function has a logic flaw. In the else block, it passes the raw newValue string to onChange. This string is not trimmed or cleaned of empty parts, unlike the logic in the if block. This can lead to inconsistent and malformed data being saved, for example, with leading/trailing spaces or extra commas.

A similar issue exists in src/app/[locale]/dashboard/_components/user/forms/provider-group-select.tsx. Consider extracting this logic into a shared utility function to avoid duplication and ensure consistency.

  const handleUserProviderGroupChange = useCallback(
    (newValue: string) => {
      let groups = newValue
        .split(",")
        .map((g) => g.trim())
        .filter(Boolean);
      // 如果有多个分组且包含 default,移除 default
      if (groups.length > 1 && groups.includes(PROVIDER_GROUP.DEFAULT)) {
        groups = groups.filter((g) => g !== PROVIDER_GROUP.DEFAULT);
      }
      onChange("providerGroup", groups.join(","));
    },
    [onChange]
  );

Comment on lines +108 to +123
const handleChange = useCallback(
(newValue: string) => {
const groupList = newValue
.split(",")
.map((g) => g.trim())
.filter(Boolean);
// 如果有多个分组且包含 default,移除 default
if (groupList.length > 1 && groupList.includes(PROVIDER_GROUP.DEFAULT)) {
const withoutDefault = groupList.filter((g) => g !== PROVIDER_GROUP.DEFAULT);
onChange(withoutDefault.join(","));
} else {
onChange(newValue);
}
},
[onChange]
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This handleChange function has a logic flaw. The else branch passes the raw, unprocessed newValue to onChange, which can lead to saving malformed data (e.g. with extra spaces or commas).

This logic is also duplicated in src/app/[locale]/dashboard/_components/user/forms/key-edit-section.tsx. It would be best to extract this into a shared utility function to ensure consistency and maintainability.

  const handleChange = useCallback(
    (newValue: string) => {
      let groupList = newValue
        .split(",")
        .map((g) => g.trim())
        .filter(Boolean);
      // 如果有多个分组且包含 default,移除 default
      if (groupList.length > 1 && groupList.includes(PROVIDER_GROUP.DEFAULT)) {
        groupList = groupList.filter((g) => g !== PROVIDER_GROUP.DEFAULT);
      }
      onChange(groupList.join(","));
    },
    [onChange]
  );

Comment on lines +451 to +461
{keyGroupOptions.length > 0 ? (
keyGroupOptions.map((group) => (
<Badge key={group} variant="secondary" className="text-xs">
{group}
</Badge>
))
) : (
<Badge variant="outline" className="text-xs">
{group}
{PROVIDER_GROUP.DEFAULT}
</Badge>
</SelectItem>
))}
{userGroups.length > 1 && (
<SelectItem value={normalizedUserProviderGroup}>
{translations.fields.providerGroup.allGroups || "全部分组"}
</SelectItem>
)}
</SelectContent>
</Select>
<p className="text-xs text-muted-foreground">
{keyData.id > 0
? translations.fields.providerGroup.editHint || "已有密钥的分组不可修改"
: translations.fields.providerGroup.selectHint || "选择此 Key 可使用的供应商分组"}
</p>
)}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The else branch in this ternary operator appears to be unreachable. The keyGroupOptions array is derived from normalizedKeyProviderGroup, which is processed by normalizeGroupList. This utility function ensures it always returns a non-empty string (at least "default"), so keyGroupOptions will never be an empty array. This makes the else block dead code.

You can simplify the code by removing the ternary operator and the unreachable else block.

                  {keyGroupOptions.map((group) => (
                    <Badge key={group} variant="secondary" className="text-xs">
                      {group}
                    </Badge>
                  ))}

@github-actions github-actions Bot added the size/S Small PR (< 200 lines) label Dec 23, 2025

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

No significant issues identified in this PR. The implementation cleanly adds provider group selection functionality with proper auto-removal of the "default" group when users select other groups.

PR Size: S

  • Lines changed: 475 (181 additions, 294 deletions)
  • Files changed: 7

Review Highlights

New Functionality Reviewed:

  • handleUserProviderGroupChange callback in key-edit-section.tsx correctly filters out "default" group when other groups are selected
  • handleChange callback in provider-group-select.tsx implements identical logic for admin context
  • TagInput keepOpen parameter improves UX for multi-selection scenarios
  • Grid layout adjustments in key-row-item.tsx for better column balance
  • Read-only badge display in user-edit-section.tsx for provider groups

Design Notes:

  • The "default" group auto-removal logic is duplicated in two components, which is acceptable given they serve different user contexts (admin vs non-admin)
  • The maxTags={userGroups.length + 1} setting appropriately allows for the default group case

Review Coverage

  • Logic and correctness - Clean
  • Security (OWASP Top 10) - Clean
  • Error handling - Clean
  • Type safety - Clean
  • Documentation accuracy - Clean
  • Test coverage - N/A (UI components)
  • Code clarity - Good

Automated review by Claude AI

@ding113
ding113 changed the base branch from main to dev December 23, 2025 17:29
@ding113

ding113 commented Dec 23, 2025

Copy link
Copy Markdown
Owner

对于 Admin 侧,用户编辑部分的 ProviderGroup 也应该使用 Read-Only Display。
因为这个字段实际上是同步自 Key 的 ProviderGroup 。

@Hwwwww-dev

Copy link
Copy Markdown
Contributor Author

抱歉没注意,pr 到 main 了 🥲

@ding113

ding113 commented Dec 23, 2025

Copy link
Copy Markdown
Owner

抱歉没注意,pr 到 main 了 🥲

base 分支我帮你改了,你可以考虑强推一次把 fork 这边的 rebase 为当前的 dev。非常感谢。

@Hwwwww-dev

Copy link
Copy Markdown
Contributor Author

对于 Admin 侧,用户编辑部分的 ProviderGroup 也应该使用 Read-Only Display。

因为这个字段实际上是同步自 Key 的 ProviderGroup 。

已经修改成了 RO,只不过使用了 Tag 显示

@Hwwwww-dev

Copy link
Copy Markdown
Contributor Author

抱歉没注意,pr 到 main 了 🥲

base 分支我帮你改了,你可以考虑强推一次把 fork 这边的 rebase 为当前的 dev。非常感谢。

好的,我一会处理一下

github-actions Bot and others added 2 commits December 24, 2025 01:46
- Add ProviderGroupSelect for admin user/key editing
- Support multi-select for non-admin key creation with TagInputField
- Auto-remove 'default' group when selecting other groups
- Keep dropdown open during multi-selection (tag-input)
- Display user provider group as read-only badges
- Adjust key row grid layout for better group/expiry column balance
- Show +N for remaining groups when >1 group exists

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@Hwwwww-dev
Hwwwww-dev force-pushed the feat/users-provider-group-selection branch from a509a6d to 9498358 Compare December 23, 2025 17:46
@Hwwwww-dev

Copy link
Copy Markdown
Contributor Author

@ding113 Rebase 已完成

@ding113
ding113 merged commit 9470bd9 into ding113:dev Dec 24, 2025
5 of 7 checks passed
@github-project-automation github-project-automation Bot moved this from Backlog to Done in Claude Code Hub Roadmap Dec 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:provider area:UI enhancement New feature or request size/S Small PR (< 200 lines)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[Feat] 用户模式创建 KEY 时供应商分组优化

2 participants