Skip to content

fix(ui): admin panel UI improvements#538

Merged
ding113 merged 3 commits intoding113:devfrom
miraserver:fix/ui-admin-2
Jan 4, 2026
Merged

fix(ui): admin panel UI improvements#538
ding113 merged 3 commits intoding113:devfrom
miraserver:fix/ui-admin-2

Conversation

@miraserver
Copy link
Contributor

@miraserver miraserver commented Jan 4, 2026

Summary

Admin panel UI improvements addressing provider form validation, dashboard layout stability, and circuit breaker filter UX.

Problem

Related Issues:

This PR addresses three categories of admin panel issues:

  1. Provider form validation gap: Group tags have a DB limit of 50 chars, but the UI allowed creating tags that would fail on save
  2. Dashboard layout glitches: User table scroll reset and chart initialization caused visual instability
  3. Circuit breaker filter UX: Filter persisted even when no providers were broken, showing empty results

Solution

1. Provider Form Validation Enhancement

  • Validate total group tag length (max 50 chars) before submission
  • Prevent adding tags that would exceed limit with real-time validation
  • Add accessibility DialogDescription for screen readers
  • Add i18n error messages for all 5 locales

2. Dashboard Layout Stability

  • User table scroll: Simplify scroll reset using rowVirtualizer.scrollToOffset(0) instead of manual scroll + requestAnimationFrame (improves on fix(ui): improve admin dialogs UX and fix i18n issues #514's fix)
  • Chart rendering: Add initialDimension={{ width: 0, height: 1 }} to ResponsiveContainer to prevent layout shift on first render

3. Circuit Breaker Filter Auto-Reset

  • Automatically clear "Circuit Broken" filter when circuitBrokenCount reaches 0
  • Prevents confusing empty state when user fixes all broken providers

Changes

Core Changes

  • provider-form.tsx (src/app/[locale]/settings/providers/_components/forms/provider-form.tsx:297-309, 319-325):
    • Pre-submission validation for group tag total length
    • Real-time validation in handleGroupTagChange()
    • Add DialogDescription for WCAG compliance
  • user-management-table.tsx (src/app/[locale]/dashboard/_components/user/user-management-table.tsx:262-264):
    • Simplify scroll reset logic (removes manual scrollTo + RAF)
  • chart.tsx (src/components/ui/chart.tsx:63-66):
    • Add initialDimension prop to prevent layout shift
  • provider-manager.tsx (src/app/[locale]/settings/providers/_components/provider-manager.tsx:73-79):
    • Auto-reset circuit broken filter via useEffect
  • url-preview.tsx (src/app/[locale]/settings/providers/_components/forms/url-preview.tsx:42-43):
    • Return null for empty preview results instead of empty object

Supporting Changes

  • Add groupTagTooLong and dialogDescription i18n keys across 5 locales (en, ja, ru, zh-CN, zh-TW)

Breaking Changes

None

Testing

Automated Tests

  • Manual testing of provider form validation
  • Manual testing of circuit breaker filter reset
  • Verified no layout glitches on dashboard

Manual Testing Checklist

  1. Provider form validation:
    • Add multiple group tags until total exceeds 50 chars → verify error toast
    • Try to submit form with long tags → verify error message
  2. User table scroll:
    • Apply filter in user management → verify table scrolls to top smoothly
  3. Circuit breaker filter:
    • Enable "Circuit Broken" filter with broken providers
    • Fix all broken providers → verify filter auto-resets
  4. Chart rendering:
    • Load dashboard → verify charts render without layout shift

Checklist

  • Code follows project conventions
  • Self-review completed
  • Tests pass locally
  • i18n keys added for all 5 locales

Description enhanced by Claude AI

John Doe and others added 3 commits January 3, 2026 23:40
- Validate provider group tags total length (max 50 chars)
- Add DialogDescription for screen reader accessibility
- Fix URL preview for empty results

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When the last circuit-broken provider is reset, the filter toggle
disappears but the filter state remains true, causing an empty list.

Add useEffect to automatically disable the filter when
circuitBrokenCount becomes 0.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Jan 4, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

向五个locale的settings.json文件添加国际化文本(dialogDescription和groupTagTooLong错误消息),同时在provider表单中实现group tag长度验证,并对多个UI组件进行小幅优化。

Changes

内聚/ 文件 变更摘要
国际化消息文件
messages/en/settings.jsonmessages/ja/settings.jsonmessages/ru/settings.jsonmessages/zh-CN/settings.jsonmessages/zh-TW/settings.json
在providers部分添加两个新的localized字符串:dialogDescription(描述provider详情与高级设置)和providers.errors.groupTagTooLong(group tag长度超限错误提示)
Provider表单逻辑
src/app/[locale]/settings/providers/_components/forms/provider-form.tsx
引入GROUP\_TAG\_MAX\_TOTAL\_LENGTH常量(50)、添加DialogDescription UI元素、新增handleGroupTagChange函数强制执行group tag总长度限制、在表单提交和tag变更时均进行长度验证并显示本地化错误提示
URL预览处理
src/app/[locale]/settings/providers/_components/forms/url-preview.tsx
修改useMemo逻辑,当preview结果为空对象时返回null而非空对象本身
用户管理表格
src/app/[locale]/dashboard/_components/user/user-management-table.tsx
使用rowVirtualizer.scrollToOffset(0)替换原有的DOM scrollTo()和requestAnimationFrame组合,简化滚动重置逻辑
Provider管理器
src/app/[locale]/settings/providers/_components/provider-manager.tsx
添加useEffect监听circuitBrokenCount,当其变为0时自动清除circuitBrokenFilter
图表组件
src/components/ui/chart.tsx
为ChartContainer中的RechartsPrimitive.ResponsiveContainer添加initialDimension={{ width: 0, height: 1 }}属性

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • release v0.3.41 #522: 主PR添加了provider dialogDescription和groupTagTooLong验证机制,与本次变更中的provider表单国际化文本和GROUP\_TAG\_MAX\_TOTAL\_LENGTH验证逻辑高度重叠。

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed 标题准确概括了主要变更内容,涵盖了提供者表单验证、用户管理表格和图表等多个UI改进。
Description check ✅ Passed 拉取请求描述与代码更改相关联,详细说明了提供商表单验证、仪表板布局稳定性和断路器过滤器用户体验等改进。
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @miraserver, 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 focuses on a series of UI improvements and bug fixes across the admin panel. It enhances the user experience by adding crucial validation to provider forms, improving accessibility, and making the URL preview more robust. Furthermore, it resolves dashboard layout inconsistencies and introduces an intelligent auto-reset feature for the circuit breaker filter, contributing to a more stable and user-friendly interface.

Highlights

  • Provider Form Enhancements: Implemented validation for provider group tags, ensuring their total length does not exceed 50 characters, and improved accessibility by adding a DialogDescription to the provider form.
  • URL Preview Robustness: Enhanced the URL preview functionality to correctly handle and display empty results, preventing potential UI issues.
  • Dashboard Layout Stability: Addressed and resolved layout glitches in the user management table's scroll behavior and improved the responsiveness of charts to prevent layout shifts.
  • Circuit Breaker Filter Automation: Introduced an automatic reset mechanism for the circuit breaker filter, clearing it when no providers are in a broken state.
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.

Copy link
Contributor

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

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 introduces a nice set of UI improvements and bug fixes to the admin panel, enhancing both functionality and user experience. Key changes include improved form validation for provider group tags, accessibility enhancements with DialogDescription, and fixes for layout glitches in the dashboard. The auto-resetting circuit breaker filter is also a thoughtful UX improvement. The code is well-structured, but I've pointed out one instance of duplicated validation logic in the provider form that could be refactored for better maintainability. Overall, this is a solid contribution that refines the admin interface.

Comment on lines +300 to +306
// group_tag 在 DB/schema 中限制为 varchar(50),并且后端按整串校验 max(50)
// 这里限制逗号拼接后的总长度,避免“UI 看似可选多标签,但保存必失败”的体验
const serializedGroupTag = groupTag.join(",");
if (serializedGroupTag.length > GROUP_TAG_MAX_TOTAL_LENGTH) {
toast.error(t("errors.groupTagTooLong", { max: GROUP_TAG_MAX_TOTAL_LENGTH }));
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This validation for the total length of groupTag is redundant. The handleGroupTagChange function, which is called on every change to the tags, already performs this validation and prevents the state from being updated if the length is invalid. Removing this duplicated check from handleSubmit will make the code cleaner and rely on a single source of truth for this validation.

@github-actions github-actions bot added the size/XS Extra Small PR (< 50 lines) label Jan 4, 2026
Copy link
Contributor

@github-actions github-actions bot left a comment

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 changes are well-implemented and improve admin panel UI/UX.

PR Size: XS

  • Lines changed: 63 (50 additions, 13 deletions)
  • Files changed: 10

Changes Reviewed

File Analysis
provider-form.tsx ✅ Group tag validation matches DB schema (varchar(50)). DialogDescription improves accessibility.
url-preview.tsx ✅ Empty result handling prevents rendering empty preview card.
user-management-table.tsx ✅ Simplified scroll reset using virtualizer API.
chart.tsx ✅ initialDimension prevents layout shift during chart render.
provider-manager.tsx ✅ Auto-reset filter when no providers are broken prevents stale UI state.
messages/*.json ✅ Translations added for all 5 locales.

Validation Details

  1. Group Tag Validation (provider-form.tsx):

    • Confirmed DB schema has varchar('group_tag', { length: 50 }) at src/drizzle/schema.ts:141
    • Client-side validation with GROUP_TAG_MAX_TOTAL_LENGTH = 50 correctly matches backend constraint
    • Dual validation (onChange + onSubmit) provides good UX feedback
  2. URL Preview Fix (url-preview.tsx):

    • previewProxyUrls() returns empty object {} for invalid URLs or unknown provider types
    • The fix correctly treats empty results as null to show the error card
  3. Scroll Reset Simplification (user-management-table.tsx):

    • Using rowVirtualizer.scrollToOffset(0) is cleaner than manual parentRef.scrollTo() + requestAnimationFrame
    • The virtualizer handles both scroll position and measurement internally
  4. Circuit Breaker Filter Auto-Reset (provider-manager.tsx):

    • Prevents showing empty filtered list when circuit breakers recover
    • Effect dependencies are correct: [circuitBrokenCount, circuitBrokenFilter]

Review Coverage

  • Logic and correctness - Clean
  • Security (OWASP Top 10) - Clean
  • Error handling - Clean
  • Type safety - Clean
  • Documentation accuracy - Clean
  • Test coverage - Manual testing noted in PR
  • Code clarity - Good

Automated review by Claude AI

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
messages/ja/settings.json (1)

1045-1048: dialogDescription 日文翻译自然、语义与英文保持一致

providers.form.dialogDescription 的日文表述清楚说明“配置提供者详情和高级设置”,与其他 locale 对齐。如希望与同文件中其它句式完全统一,也可以考虑将「構成します」微调为「設定します」,但当前版本已可接受。

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to Reviews > Disable Cache setting

📥 Commits

Reviewing files that changed from the base of the PR and between 07f35c5 and 60abcef.

📒 Files selected for processing (10)
  • messages/en/settings.json
  • messages/ja/settings.json
  • messages/ru/settings.json
  • messages/zh-CN/settings.json
  • messages/zh-TW/settings.json
  • src/app/[locale]/dashboard/_components/user/user-management-table.tsx
  • src/app/[locale]/settings/providers/_components/forms/provider-form.tsx
  • src/app/[locale]/settings/providers/_components/forms/url-preview.tsx
  • src/app/[locale]/settings/providers/_components/provider-manager.tsx
  • src/components/ui/chart.tsx
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{ts,tsx,js,jsx,json}

📄 CodeRabbit inference engine (CLAUDE.md)

Use 2-space indentation in all code files

Files:

  • src/app/[locale]/dashboard/_components/user/user-management-table.tsx
  • messages/ru/settings.json
  • src/components/ui/chart.tsx
  • messages/ja/settings.json
  • messages/zh-TW/settings.json
  • messages/zh-CN/settings.json
  • src/app/[locale]/settings/providers/_components/provider-manager.tsx
  • messages/en/settings.json
  • src/app/[locale]/settings/providers/_components/forms/url-preview.tsx
  • src/app/[locale]/settings/providers/_components/forms/provider-form.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Use double quotes for strings instead of single quotes
Use trailing commas in multi-line structures
Enforce maximum line length of 100 characters
Use path alias @/* to reference files from ./src/* directory

**/*.{ts,tsx,js,jsx}: Use Biome for linting and formatting with 2-space indent, double quotes, trailing commas, and 100 character max line length
Use path alias @/* to reference files in ./src/* directory

Files:

  • src/app/[locale]/dashboard/_components/user/user-management-table.tsx
  • src/components/ui/chart.tsx
  • src/app/[locale]/settings/providers/_components/provider-manager.tsx
  • src/app/[locale]/settings/providers/_components/forms/url-preview.tsx
  • src/app/[locale]/settings/providers/_components/forms/provider-form.tsx
src/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{tsx,jsx}: Use lucide-react for icons, no custom SVGs
Use React's automatic escaping to prevent XSS vulnerabilities

Files:

  • src/app/[locale]/dashboard/_components/user/user-management-table.tsx
  • src/components/ui/chart.tsx
  • src/app/[locale]/settings/providers/_components/provider-manager.tsx
  • src/app/[locale]/settings/providers/_components/forms/url-preview.tsx
  • src/app/[locale]/settings/providers/_components/forms/provider-form.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use TypeScript strict mode for type safety
Use readonly or const assertions for immutable data structures

Files:

  • src/app/[locale]/dashboard/_components/user/user-management-table.tsx
  • src/components/ui/chart.tsx
  • src/app/[locale]/settings/providers/_components/provider-manager.tsx
  • src/app/[locale]/settings/providers/_components/forms/url-preview.tsx
  • src/app/[locale]/settings/providers/_components/forms/provider-form.tsx
**/*.{tsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Use next-intl for internationalization with 5 locales: en, ja, ru, zh-CN, zh-TW

Files:

  • src/app/[locale]/dashboard/_components/user/user-management-table.tsx
  • messages/ru/settings.json
  • src/components/ui/chart.tsx
  • messages/ja/settings.json
  • messages/zh-TW/settings.json
  • messages/zh-CN/settings.json
  • src/app/[locale]/settings/providers/_components/provider-manager.tsx
  • messages/en/settings.json
  • src/app/[locale]/settings/providers/_components/forms/url-preview.tsx
  • src/app/[locale]/settings/providers/_components/forms/provider-form.tsx
messages/**/*.json

📄 CodeRabbit inference engine (CLAUDE.md)

Support 5 locales via next-intl: en, ja, ru, zh-CN, zh-TW with messages in messages/{locale}/*.json

Store message translations in messages/{locale}/*.json files

Files:

  • messages/ru/settings.json
  • messages/ja/settings.json
  • messages/zh-TW/settings.json
  • messages/zh-CN/settings.json
  • messages/en/settings.json
src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use lucide-react for icons instead of custom SVGs

Files:

  • src/components/ui/chart.tsx
src/components/ui/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Place UI components in src/components/ui/ directory (excluded from typecheck)

Files:

  • src/components/ui/chart.tsx
src/components/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/components/**/*.{tsx,jsx}: Use Tailwind CSS for styling, place utility classes close to JSX
Use shadcn/ui component library for high-quality, accessible UI components

Files:

  • src/components/ui/chart.tsx
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-03T09:08:20.573Z
Learning: Applies to messages/**/*.json : Support 5 locales via next-intl: en, ja, ru, zh-CN, zh-TW with messages in `messages/{locale}/*.json`
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to **/*.{tsx,json} : Use next-intl for internationalization with 5 locales: en, ja, ru, zh-CN, zh-TW
📚 Learning: 2026-01-03T09:08:20.573Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-03T09:08:20.573Z
Learning: Applies to messages/**/*.json : Support 5 locales via next-intl: en, ja, ru, zh-CN, zh-TW with messages in `messages/{locale}/*.json`

Applied to files:

  • messages/zh-CN/settings.json
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/**/*provider*.ts : Set provider circuit breaker failure threshold, open duration, and half-open success threshold in configuration

Applied to files:

  • src/app/[locale]/settings/providers/_components/provider-manager.tsx
📚 Learning: 2026-01-03T09:08:20.573Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-03T09:08:20.573Z
Learning: Applies to src/lib/circuit-breaker.ts : Circuit Breaker must implement state machine: CLOSED -> OPEN -> HALF_OPEN -> CLOSED with per-provider isolation and Redis persistence

Applied to files:

  • src/app/[locale]/settings/providers/_components/provider-manager.tsx
📚 Learning: 2026-01-03T09:08:20.573Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-03T09:08:20.573Z
Learning: Applies to src/components/**/*.{ts,tsx} : Use `lucide-react` for icons instead of custom SVGs

Applied to files:

  • src/app/[locale]/settings/providers/_components/provider-manager.tsx
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/**/*.{tsx,jsx} : Use `lucide-react` for icons, no custom SVGs

Applied to files:

  • src/app/[locale]/settings/providers/_components/provider-manager.tsx
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/components/**/*.{tsx,jsx} : Use shadcn/ui component library for high-quality, accessible UI components

Applied to files:

  • src/app/[locale]/settings/providers/_components/provider-manager.tsx
  • src/app/[locale]/settings/providers/_components/forms/provider-form.tsx
🧬 Code graph analysis (2)
src/app/[locale]/settings/providers/_components/forms/url-preview.tsx (1)
src/app/v1/_lib/url.ts (1)
  • previewProxyUrls (95-179)
src/app/[locale]/settings/providers/_components/forms/provider-form.tsx (1)
src/components/ui/dialog.tsx (1)
  • DialogDescription (122-122)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: pr-review
  • GitHub Check: Greptile Review
  • GitHub Check: pr-description
  • GitHub Check: 🌐 API Tests
  • GitHub Check: Docker Build Test
🔇 Additional comments (19)
src/app/[locale]/dashboard/_components/user/user-management-table.tsx (1)

262-265: 代码简化得很好!

使用 rowVirtualizer.scrollToOffset(0) 替代之前的 DOM 操作和 requestAnimationFrame 是一个很好的改进。这种方式更简洁、更易维护,并且直接使用虚拟化器的原生 API 更符合最佳实践。

src/app/[locale]/settings/providers/_components/provider-manager.tsx (2)

4-4: 导入更改看起来正确。

添加 useEffect 导入用于下方新增的 effect,符合 React 使用规范。


73-78: 自动重置熔断器过滤器的实现正确。

当没有处于熔断状态的供应商时(circuitBrokenCount === 0),自动清除熔断器过滤器,避免显示空列表。依赖数组完整且正确,逻辑清晰。

messages/zh-TW/settings.json (1)

1048-1048: 国际化文本添加正确。

新增的 dialogDescriptiongroupTagTooLong 字符串符合 next-intl 格式要求,占位符 {max} 的使用方式正确。根据 PR 上下文,这些消息支持供应商表单中的新验证逻辑(最大总长度 50 字符)和无障碍改进。

基于 learnings:该项目通过 next-intl 支持 5 种语言环境(en, ja, ru, zh-CN, zh-TW)。

Also applies to: 1350-1350

src/app/[locale]/settings/providers/_components/forms/url-preview.tsx (1)

42-43: 空预览结果处理改进正确。

新增的空对象检查(Object.keys(result).length > 0)确保当 previewProxyUrls 返回空对象时,组件返回 null 而不是渲染空的预览 UI。这改善了边缘情况的处理,与 PR 目标中提到的"修复空结果的 URL 预览处理"一致。

src/components/ui/chart.tsx (1)

63-67: 添加初始尺寸以防止布局偏移。

ResponsiveContainer 中添加 initialDimension={{ width: 0, height: 1 }} 是 recharts 的已知模式,用于在初始渲染时提供最小尺寸,避免布局闪烁和偏移。这与 PR 目标中提到的"防止仪表板布局故障"一致。

messages/ru/settings.json (1)

1048-1048: 俄语国际化文本添加正确。

新增的 dialogDescriptiongroupTagTooLong 俄语翻译字符串格式正确,占位符使用方式符合 next-intl 规范。这些消息与其他语言环境文件(如 zh-TW)保持一致,共同支持供应商表单的验证和无障碍功能。

基于 learnings:项目支持 5 种语言环境,所有语言环境文件应保持一致的键结构。

Also applies to: 1344-1344

messages/zh-CN/settings.json (2)

658-662: Provider 表单 dialogDescription 文案与英文语义一致,可以直接使用

放在 providers.form 下、紧挨着标题键,结构合理,简短说明“配置供应商信息及高级设置”,与 UI 中 DialogDescription 的用途吻合,无占位符风险。


980-987: groupTagTooLong 错误文案正确表达了总长度限制

键路径 providers.form.errors.groupTagTooLong 与前端验证逻辑匹配,文案“分组标签总长度不能超过 {max} 个字符”清晰准确,{max} 占位符也与代码中的上限常量相符。

messages/en/settings.json (2)

1150-1153: Provider 对话框描述英文文案清晰且位置正确

作为 providers.form.dialogDescription 紧跟 title,简洁说明对话框用途,符合无障碍/可读性预期,无占位符,风险很小。


1471-1477: groupTagTooLong 错误消息与前端约束语义一致

错误键新增在 providers.form.errors 下,提示“max {max} chars total”明确且与“按逗号拼接后的总长度限制”为 50 的实现契合,占位符 {max} 书写无误。

messages/ja/settings.json (1)

1341-1347: groupTagTooLong 日文错误消息占位符与语义均正确

错误键位于 providers.form.errors 下,提示「プロバイダーグループが長すぎます(合計{max}文字まで)」准确表达“总长度不超过 {max} 字符”,{max} 占位符与前端逻辑一致,无需调整。

src/app/[locale]/settings/providers/_components/forms/provider-form.tsx (7)

22-22: 正确引入 DialogDescription 组件。

此导入用于改善可访问性,符合使用 shadcn/ui 组件库的编码规范。


44-44: 良好实践:使用命名常量。

定义 GROUP_TAG_MAX_TOTAL_LENGTH 常量提高了代码可维护性,并与数据库 schema 中的 varchar(50) 约束保持一致。


300-306: 提交前验证逻辑正确。

在提交前验证 group tags 的序列化总长度(逗号分隔),避免后端验证失败,提升用户体验。错误提示正确使用了国际化。


319-327: 实时验证提升用户体验。

handleGroupTagChange 函数在用户添加标签时进行实时验证,防止超出长度限制,与提交时的验证逻辑保持一致。


541-541: 提升可访问性。

添加 DialogDescription 组件并使用 sr-only 类,为屏幕阅读器用户提供对话框描述,符合 WCAG 可访问性指南。


715-715: 正确应用新的验证处理器。

onChange 更新为使用 handleGroupTagChange,确保在用户输入时进行实时验证。


724-724: 保持错误消息的一致性。

使用 GROUP_TAG_MAX_TOTAL_LENGTH 常量替换硬编码值,确保错误消息与验证逻辑保持一致。注意:如果 onInvalidTagtoo_long 原因是针对单个标签长度的验证,这里的最大值可能需要调整。

placeholder={t("sections.routing.scheduleParams.group.placeholder")}
disabled={isPending}
maxTagLength={50}
maxTagLength={GROUP_TAG_MAX_TOTAL_LENGTH}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find the provider-form.tsx file and relevant constants/components
find . -name "provider-form.tsx" -type f | head -5

Repository: ding113/claude-code-hub

Length of output: 140


🏁 Script executed:

# Search for GROUP_TAG_MAX_TOTAL_LENGTH constant definition
rg -n "GROUP_TAG_MAX_TOTAL_LENGTH" --type=ts --type=tsx -B 2 -A 2

Repository: ding113/claude-code-hub

Length of output: 94


🏁 Script executed:

# Find TagInput component
fd -name "*tag-input*" -type f

Repository: ding113/claude-code-hub

Length of output: 297


🏁 Script executed:

# Search for GROUP_TAG_MAX_TOTAL_LENGTH constant definition
rg "GROUP_TAG_MAX_TOTAL_LENGTH" -B 2 -A 2

Repository: ding113/claude-code-hub

Length of output: 3269


🏁 Script executed:

# Find TagInput component
fd "tag-input" -type f

Repository: ding113/claude-code-hub

Length of output: 237


🏁 Script executed:

# Check the provider-form.tsx file for imports and relevant code around line 718
head -750 ./src/app/[locale]/settings/providers/_components/forms/provider-form.tsx | tail -100

Repository: ding113/claude-code-hub

Length of output: 5234


🏁 Script executed:

# Find TagInput component
find . -name "*" -type f | xargs grep -l "export.*TagInput" | head -10

Repository: ding113/claude-code-hub

Length of output: 137


🏁 Script executed:

# Search for TagInput prop definition or component
rg "maxTagLength" -B 5 -A 5 --max-count=20

Repository: ding113/claude-code-hub

Length of output: 17009


🏁 Script executed:

# Look for TagInput component more broadly
rg "component.*TagInput|export.*function.*TagInput|const.*TagInput.*=" -A 20

Repository: ding113/claude-code-hub

Length of output: 5866


🏁 Script executed:

# Get the complete TagInput component implementation
cat ./src/components/ui/tag-input.tsx | head -200

Repository: ding113/claude-code-hub

Length of output: 6074


🏁 Script executed:

# Check how handleGroupTagChange is implemented in context
rg -B 10 "const handleGroupTagChange" src/app/[locale]/settings/providers/_components/forms/provider-form.tsx

Repository: ding113/claude-code-hub

Length of output: 318


纠正 maxTagLength 属性的语义用途。

maxTagLength 属性在 TagInput 组件中用于验证单个标签的长度(见 validateBaseTagif (tag.length > maxTagLength)),但此处被设置为 GROUP_TAG_MAX_TOTAL_LENGTH(用于限制所有标签逗号拼接后的总长度)。

这会导致语义混乱:maxTagLength={50} 表示每个单个标签可长达 50 字符,而不是所有标签的总长度为 50。如果 GROUP_TAG_MAX_TOTAL_LENGTH 在未来被修改(例如改为 200),则单个标签验证和总长度验证的含义将完全不符。

虽然 handleGroupTagChange 作为安全网能防止问题发生,但建议为 maxTagLength 设置更合理的值(如 25-30),使其在语义上与 GROUP_TAG_MAX_TOTAL_LENGTH 的限制条件相适配。

🤖 Prompt for AI Agents
In src/app/[locale]/settings/providers/_components/forms/provider-form.tsx
around line 718, maxTagLength is incorrectly set to GROUP_TAG_MAX_TOTAL_LENGTH
(which represents the total length of all tags combined) but TagInput uses
maxTagLength to validate each individual tag; change the prop to a sensible
per-tag limit (e.g. 25–30) by introducing or using a per-tag constant like
GROUP_SINGLE_TAG_MAX_LENGTH (or a literal such as 30) and keep
GROUP_TAG_MAX_TOTAL_LENGTH for the combined-length checks in
handleGroupTagChange so single-tag validation semantics and total-length
validation remain consistent.

@miraserver
Copy link
Contributor Author

Response to Code Review Comments

Re: Duplicate validation in handleSubmit (line 306)

The duplicate validation is intentional (defense in depth). Here's why:

When editing an existing provider, groupTag is initialized directly from sourceProvider (lines 93-100):

const [groupTag, setGroupTag] = useState<string[]>(
  sourceProvider?.groupTag
    ? sourceProvider.groupTag.split(",").map((t) => t.trim()).filter(Boolean)
    : []
);

This bypasses handleGroupTagChange entirely. If there's legacy data in the database with groupTag > 50 chars (created before this validation was added), the form would load with invalid data.

Without the handleSubmit validation:

  1. User opens edit form for legacy provider with oversized groupTag
  2. User changes nothing, clicks "Save"
  3. Invalid data passes through → backend error

The handleSubmit check is the last line of defense before sending data to the server.

Re: maxTagLength (line 718)

Acknowledged. The maxTagLength={50} applies to individual tags while GROUP_TAG_MAX_TOTAL_LENGTH is for the total serialized length. The UX message could be slightly confusing, but handleGroupTagChange will catch the actual constraint. This is a minor UX issue, not a functional bug.


Response by maintainer

@ding113 ding113 merged commit 6ab0c9d into ding113:dev Jan 4, 2026
16 of 17 checks passed
@github-project-automation github-project-automation bot moved this from Backlog to Done in Claude Code Hub Roadmap Jan 4, 2026
This was referenced Jan 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:i18n area:provider area:UI bug Something isn't working size/XS Extra Small PR (< 50 lines)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants