Skip to content

Fix #456: Add inline editing for provider priority, weight and cost multiplier#486

Merged
ding113 merged 4 commits intoding113:devfrom
YangQing-Lin:feature/inline-provider-editing
Dec 30, 2025
Merged

Fix #456: Add inline editing for provider priority, weight and cost multiplier#486
ding113 merged 4 commits intoding113:devfrom
YangQing-Lin:feature/inline-provider-editing

Conversation

@YangQing-Lin
Copy link
Collaborator

@YangQing-Lin YangQing-Lin commented Dec 30, 2025

PR: 供应商优先级/权重/成本倍数行内编辑

分支: feature/inline-provider-editing
Issue: #456
里程碑: v0.4.1


概述

在供应商列表页添加行内编辑功能,允许管理员直接点击数值快速修改 priority、weight、costMultiplier 三个调度相关字段,无需打开完整的编辑对话框。

变更内容

新增组件

  • src/app/[locale]/settings/providers/_components/inline-edit-popover.tsx
    • Popover + Input 组件,支持保存/取消操作
    • 键盘快捷键:Enter 保存,Escape 取消
    • 实时验证与错误提示
    • Loading 状态与禁用态处理

修改文件

文件 变更
provider-rich-list-item.tsx 集成 InlineEditPopover,为管理员用户启用行内编辑
provider.constants.ts weight 范围从 0-100 改为 1-100
schemas.ts 添加中文错误提示信息
schemas.test.ts 新增验证规则单元测试
messages/*/settings.json 5 种语言的 i18n 字符串

设计决策

1. Weight 最小值改为 1

  • 原设计允许 weight=0 表示"不参与加权轮询"
  • 问题:与 is_enabled=false 语义重叠,容易混淆
  • 方案:强制 1-100 范围,禁用供应商应通过 is_enabled 显式表达

2. CostMultiplier 支持行内编辑

  • 原 Issue 仅要求 priority/weight
  • 扩展:costMultiplier 同样是高频调整字段,一并支持

3. 仅管理员可编辑

  • 通过 canEdit 权限判断控制
  • 普通用户仅显示静态数值

测试计划

  • 单元测试:schemas.test.ts 验证规则覆盖
  • bun run test 全部通过 (170 tests)
  • Docker 构建成功
  • 手动测试:
    • 点击数值弹出编辑框
    • Enter/Escape 快捷键
    • 验证错误提示
    • 保存后数据刷新
    • 非管理员用户不可编辑

截图

image image image

提交记录

  1. 07dd22f feat(providers): add inline editing for priority/weight/costMultiplier fields (#456)

    • 主功能实现:组件、集成、i18n、测试
  2. 9b31e7a fix(providers): change weight minimum from 0 to 1 for clarity

    • Weight 下限修正及错误提示优化

风险点

风险 缓解措施
快速连续保存可能导致竞态 每次保存后调用 queryClient.invalidateQueries 刷新缓存
Popover 在移动端体验不佳 仅在 md: 断点以上显示行内编辑,移动端仍用完整表单

Greptile Summary

This PR successfully implements inline editing for provider priority, weight, and cost multiplier fields, addressing issue #456. The implementation includes a reusable InlineEditPopover component with validation, keyboard shortcuts (Enter/Escape), and proper loading states.

Key changes:

  • Created InlineEditPopover component with real-time validation and error display
  • Integrated inline editing into ProviderRichListItem for admin users only (via canEdit check)
  • Changed weight minimum from 0 to 1 (range: 1-100) to avoid semantic overlap with is_enabled=false
  • Added Chinese error messages to schemas with proper validation rules
  • Comprehensive test coverage added for priority/weight/costMultiplier validation
  • Complete i18n support across 5 languages (en, zh-CN, zh-TW, ja, ru)

Design decisions:

  • Inline editing restricted to admin users via canEdit permission check
  • Weight minimum changed to 1 to eliminate confusion with disabled state
  • Extended scope to include costMultiplier in addition to priority/weight
  • Successful saves trigger cache invalidation and router refresh for immediate UI updates

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • Well-implemented feature with proper validation, comprehensive tests, complete i18n coverage, and careful permission checks. Code follows established patterns and includes appropriate error handling.
  • No files require special attention

Important Files Changed

Filename Overview
src/app/[locale]/settings/providers/_components/inline-edit-popover.tsx New reusable popover component for inline editing with validation, keyboard shortcuts, and loading states - well-structured with proper event handling
src/app/[locale]/settings/providers/_components/provider-rich-list-item.tsx Integrated inline editing for priority/weight/costMultiplier with validation handlers - admin-only access enforced correctly
src/lib/validation/schemas.ts Updated weight minimum from 0 to 1 with Chinese error messages - validation logic aligned with constants
src/lib/validation/schemas.test.ts Comprehensive test coverage for priority/weight/costMultiplier validation rules including boundary cases and coercion behavior

Sequence Diagram

sequenceDiagram
    participant Admin as Admin User
    participant UI as Provider List UI
    participant Popover as InlineEditPopover
    participant Handler as Save Handler
    participant API as editProvider API
    participant Cache as QueryClient
    participant Router as Next Router

    Admin->>UI: Click on priority/weight/cost value
    UI->>Popover: Open popover (pass value, validator, onSave)
    Popover->>Popover: Focus input & select text
    Admin->>Popover: Edit value
    Popover->>Popover: Validate input (real-time)
    alt Invalid input
        Popover-->>Admin: Show validation error message
    end
    Admin->>Popover: Press Enter or click Save
    Popover->>Popover: Check canSave (valid & not saving)
    Popover->>Handler: onSave(parsedValue)
    Handler->>API: editProvider(id, {field: value})
    API-->>Handler: {ok: true} or {ok: false, error}
    alt Save successful
        Handler->>Cache: invalidateQueries(['providers'])
        Handler->>Router: router.refresh()
        Handler-->>Popover: return true
        Popover->>Popover: Close popover
        Popover-->>Admin: Show success toast
    else Save failed
        Handler-->>Popover: return false
        Popover-->>Admin: Show error toast & keep popover open
    end
Loading

…r fields (ding113#456)

- Add InlineEditPopover component with Popover + Input + save/cancel buttons
- Integrate inline editing in ProviderRichListItem for admin users
- Add i18n strings for 5 locales (en/ja/ru/zh-CN/zh-TW)
- Allow weight minimum value to be 0 (PROVIDER_LIMITS.WEIGHT.MIN)
- Add keyboard focus styles and upper bound validation
- Add unit tests for schema validation rules
Weight=0 previously meant "skip this provider in weighted selection",
which was confusing. Now enforce 1-100 range; disable the provider
via is_enabled flag instead.

- Update PROVIDER_LIMITS.WEIGHT.MIN to 1
- Add Chinese error messages to Zod schemas
- Update all 5 locale error messages
- Adjust unit tests accordingly
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @YangQing-Lin, 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!

此拉取请求为供应商列表页面引入了行内编辑功能,允许管理员直接修改供应商的 priority(优先级)、weight(权重)和 costMultiplier(成本倍数)这三个调度相关字段,而无需打开完整的编辑对话框。这一改进旨在简化供应商参数的管理流程,提高管理员操作效率和用户体验。

Highlights

  • 行内编辑功能: 在供应商列表页为 priority、weight 和 costMultiplier 字段添加了行内编辑功能,允许管理员直接修改数值。
  • 新增组件: 引入了 inline-edit-popover.tsx 组件,提供带有输入框、保存/取消操作、实时验证、错误提示、加载状态及键盘快捷键(Enter 保存,Escape 取消)的弹出式编辑界面。
  • 权重范围调整: 将 weight 的有效范围从 0-100 调整为 1-100,以避免与 is_enabled=false 语义混淆。
  • 成本倍数支持: 将行内编辑功能扩展到 costMultiplier 字段,因为它也是一个高频调整的参数。
  • 管理员权限控制: 行内编辑功能仅对管理员用户开放,通过权限判断进行控制。
  • 国际化支持: 为新的行内编辑功能添加了多语言(5种)的国际化字符串。
  • 验证与测试: 更新了验证 schema 并新增了单元测试,以确保新的验证规则(特别是 weight 的范围)正确执行。
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.

@github-actions github-actions bot added enhancement New feature or request area:UI area:i18n size/M Medium PR (< 500 lines) labels Dec 30, 2025
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

This PR adds inline editing functionality for provider priority, weight, and cost multiplier fields. The implementation includes a new reusable InlineEditPopover component, proper validation, and comprehensive i18n support. The code quality is high with good error handling, appropriate loading states, and keyboard shortcuts.

PR Size: M

  • Lines changed: 489 (479 additions, 10 deletions)
  • Files changed: 9

Issues Found

Category Critical High Medium Low
Logic/Bugs 0 0 0 0
Security 0 0 0 0
Error Handling 0 0 0 0
Types 0 0 0 0
Comments/Docs 0 0 0 0
Tests 0 0 0 0
Simplification 0 0 0 0

Review Coverage

  • Logic and correctness - Clean
  • Security (OWASP Top 10) - Clean
  • Error handling - Clean
  • Type safety - Clean
  • Documentation accuracy - Clean
  • Test coverage - Adequate
  • Code clarity - Good

Detailed Analysis

Strengths:

  1. Excellent error handling: All three save handlers (handleSavePriority, handleSaveWeight, handleSaveCostMultiplier) properly catch errors, log them, and provide user feedback via toast notifications
  2. Proper validation: Client-side validators use the same constants (PROVIDER_LIMITS.WEIGHT.MIN/MAX) as the server schema, ensuring consistency
  3. Good UX: Includes keyboard shortcuts (Enter to save, Escape to cancel), loading states with Loader2 spinner, and appropriate button disabling
  4. Comprehensive testing: New schemas.test.ts validates the alignment between schema rules for priority/weight/costMultiplier
  5. Good type safety: Props are properly typed with clear interfaces
  6. Proper i18n: All user-facing strings are translated across 5 locales
  7. Defensive programming: The component properly validates input before enabling save, and handles edge cases like empty input
  8. No silent failures: All errors are surfaced to users via toast notifications

Design Decision Note:
The change of WEIGHT.MIN from 0 to 1 is well-documented in the PR description and makes semantic sense - weight=0 for "disabled" conflicts with is_enabled=false. The inline edit popover enforces this 1-100 range consistently.

Code Quality Observations:

  • The InlineEditPopover component follows React best practices with proper cleanup in useEffect
  • Event propagation is correctly stopped with stopPropagation handlers
  • The component is well-designed for reusability with configurable type (integer/number) and suffix props

Minor Notes (No Action Required):

  • The three save handler functions in provider-rich-list-item.tsx are repetitive but appropriately so - extracting them would add unnecessary abstraction for just three instances
  • console.error usage in catch blocks is consistent with the existing codebase pattern for client-side error logging

Automated review by Claude AI

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

本次 PR 实现了供应商调度参数的行内编辑功能,极大地提升了管理员的操作效率,做得非常棒!新的 InlineEditPopover 组件设计得很好,可复用性强,并且考虑了加载状态、禁用态和键盘快捷键等细节。

在代码审查中,我发现 provider-rich-list-item.tsx 文件中的三个保存处理函数(handleSavePriority, handleSaveWeight, handleSaveCostMultiplier)存在较多重复代码,并且在错误处理中存在一个小 bug(错误地使用了删除失败的提示文案)。我提供了一个重构建议,通过创建一个通用的处理函数来解决这两个问题,以提高代码的可维护性。

除此之外,其他部分的实现,包括 i18n、Zod schema 的更新和单元测试的添加,都完成得很好。请查看具体的审查意见。

Comment on lines 287 to 339
const handleSavePriority = async (value: number) => {
try {
const res = await editProvider(provider.id, { priority: value });
if (res.ok) {
toast.success(tInline("saveSuccess"));
queryClient.invalidateQueries({ queryKey: ["providers"] });
router.refresh();
return true;
}
toast.error(tInline("saveFailed"), { description: res.error || tList("unknownError") });
return false;
} catch (error) {
console.error("更新 priority 失败:", error);
toast.error(tInline("saveFailed"), { description: tList("deleteError") });
return false;
}
};

const handleSaveWeight = async (value: number) => {
try {
const res = await editProvider(provider.id, { weight: value });
if (res.ok) {
toast.success(tInline("saveSuccess"));
queryClient.invalidateQueries({ queryKey: ["providers"] });
router.refresh();
return true;
}
toast.error(tInline("saveFailed"), { description: res.error || tList("unknownError") });
return false;
} catch (error) {
console.error("更新 weight 失败:", error);
toast.error(tInline("saveFailed"), { description: tList("deleteError") });
return false;
}
};

const handleSaveCostMultiplier = async (value: number) => {
try {
const res = await editProvider(provider.id, { cost_multiplier: value });
if (res.ok) {
toast.success(tInline("saveSuccess"));
queryClient.invalidateQueries({ queryKey: ["providers"] });
router.refresh();
return true;
}
toast.error(tInline("saveFailed"), { description: res.error || tList("unknownError") });
return false;
} catch (error) {
console.error("更新 costMultiplier 失败:", error);
toast.error(tInline("saveFailed"), { description: tList("deleteError") });
return false;
}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

这三个保存处理函数 handleSavePriorityhandleSaveWeighthandleSaveCostMultiplier 的逻辑几乎完全相同,导致了代码重复。

此外,在 catch 块中,错误提示信息使用了 tList("deleteError"),这可能会误导用户。这里应该使用一个更通用的保存失败提示,例如 tList("unknownError")

为了解决这两个问题并提高代码的可维护性,建议将它们重构为一个通用的工厂函数。这样不仅可以消除重复代码,还能统一错误处理逻辑。

请参考以下建议进行修改:

  const createSaveHandler = (fieldName: "priority" | "weight" | "cost_multiplier") => {
    return async (value: number) => {
      try {
        const res = await editProvider(provider.id, { [fieldName]: value });
        if (res.ok) {
          toast.success(tInline("saveSuccess"));
          queryClient.invalidateQueries({ queryKey: ["providers"] });
          router.refresh();
          return true;
        }
        toast.error(tInline("saveFailed"), { description: res.error || tList("unknownError") });
        return false;
      } catch (error) {
        console.error(`更新 ${fieldName} 失败:`, error);
        toast.error(tInline("saveFailed"), { description: tList("unknownError") });
        return false;
      }
    };
  };

  const handleSavePriority = createSaveHandler("priority");
  const handleSaveWeight = createSaveHandler("weight");
  const handleSaveCostMultiplier = createSaveHandler("cost_multiplier");

YangQing-Lin and others added 2 commits December 30, 2025 22:59
- Consolidate handleSavePriority, handleSaveWeight, handleSaveCostMultiplier
  into a single createSaveHandler factory function
- Fix incorrect error message key in catch block (deleteError → unknownError)
@ding113 ding113 merged commit 80a20ce into ding113:dev Dec 30, 2025
@github-project-automation github-project-automation bot moved this from Backlog to Done in Claude Code Hub Roadmap Dec 30, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:i18n area:UI enhancement New feature or request size/M Medium PR (< 500 lines)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants