Skip to content

feat(dashboard): 在数据看板中的模型消耗分布添加显示Token 功能并支持切换 - #2739

Closed
LinZX1314 wants to merge 5411 commits into
QuantumNous:mainfrom
LinZX1314:feat/dashboard-consumption-mode
Closed

feat(dashboard): 在数据看板中的模型消耗分布添加显示Token 功能并支持切换#2739
LinZX1314 wants to merge 5411 commits into
QuantumNous:mainfrom
LinZX1314:feat/dashboard-consumption-mode

Conversation

@LinZX1314

@LinZX1314 LinZX1314 commented Jan 24, 2026

Copy link
Copy Markdown
  • 在数据看板中的模型消耗分布标签页添加 $/Token 切换按钮
  • 在系统设置的仪表盘设置的数据看板设置中新增默认显示模式配置项
  • Token 模式数据使用数据库quota_data表的token_used 字段

Summary by CodeRabbit

  • New Features

    • Added a display mode toggle on dashboard charts to switch between Amount (quota) and Tokens, with mode-aware totals and tooltips.
    • Added a "Default Display Mode" setting in dashboard settings; choice persists locally and initializes the dashboard view.
  • Documentation

    • Added translation strings and labels for display mode controls and related UI text.

@coderabbitai

coderabbitai Bot commented Jan 24, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a display-mode feature to the Data Dashboard: a QUOTA/TOKENS toggle (UI, settings, i18n), persistence to localStorage and new option key, and mode-aware chart aggregation/tooltip logic including token usage tracking.

Changes

Cohort / File(s) Summary
Backend option
model/option.go
Introduce DataExportDefaultDisplayMode option with default QUOTA and no-op DB handling (stored for frontend).
Dashboard components
web/src/components/dashboard/ChartsPanel.jsx, web/src/components/dashboard/index.jsx
Add displayMode prop and onDisplayModeChange callback; UI toggle (DollarSign/Coins) in ChartsPanel; parent manages displayMode state with localStorage persistence and triggers chart updates.
Settings UI
web/src/components/settings/DashboardSetting.jsx, web/src/pages/Setting/Dashboard/SettingsDataDashboard.jsx
Add DataExportDefaultDisplayMode input, render Form.Select to choose QUOTA/TOKENS, persist default to localStorage.
Chart/helpers/hooks
web/src/helpers/dashboard.jsx, web/src/hooks/dashboard/useDashboardCharts.jsx, web/src/hooks/dashboard/useDashboardData.js
Make chart spec accept tooltipConfig; add tokenUsed aggregation and rawTokenUsed/rawQuota fields; introduce mode-aware valueField, sorting, timeSum, display formatting, and dynamic tooltip generation; add fallback token_used on empty API responses.
Internationalization
web/src/i18n/locales/en.json, web/src/i18n/locales/zh-CN.json
Add English keys for display mode and toggle labels; simplify/normalize many Chinese locale entries.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI as ChartsPanel
    participant Parent as Dashboard (state)
    participant Storage as localStorage
    participant Hooks as useDashboardCharts
    participant Charts as Chart

    User->>UI: click display-mode toggle
    UI->>Parent: onDisplayModeChange(newMode)
    Parent->>Parent: setDisplayMode(newMode)
    Parent->>Storage: save data_export_default_display_mode
    Parent->>Hooks: pass displayMode
    Hooks->>Hooks: createTooltipConfig(displayMode)
    Hooks->>Hooks: aggregate data (rawTokenUsed/rawQuota, tokenUsed)
    Hooks->>Charts: updateChartData(spec, tooltipConfig)
    Charts->>UI: render updated charts
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • Calcium-Ion
  • seefs001

Poem

🐰 A toggle floppy-eared and spry,
QUOTA or TOKENS—give it a try!
Charts hum softly, tooltips sing,
LocalStorage keeps each tiny thing.
Hop—data dances, numbers fly ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main feature: adding Token display functionality and toggle capability in the model consumption distribution section of the data dashboard.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/src/pages/Setting/Dashboard/SettingsDataDashboard.jsx (1)

98-105: Bug: localStorage is written with stale state values.

The useEffect writes to localStorage using inputs.DataExportDefaultTime and inputs.DataExportDefaultDisplayMode, but at this point inputs still holds the previous state values. The new values from props.options are set via setInputs(currentInputs) but React state updates are asynchronous, so the subsequent localStorage writes use stale data.

🐛 Proposed fix
   useEffect(() => {
     const currentInputs = {};
     for (let key in props.options) {
       if (Object.keys(inputs).includes(key)) {
         currentInputs[key] = props.options[key];
       }
     }
     setInputs(currentInputs);
     setInputsRow(structuredClone(currentInputs));
     refForm.current.setValues(currentInputs);
     localStorage.setItem(
       'data_export_default_time',
-      String(inputs.DataExportDefaultTime),
+      String(currentInputs.DataExportDefaultTime || 'hour'),
     );
     localStorage.setItem(
       'data_export_default_display_mode',
-      String(inputs.DataExportDefaultDisplayMode || 'QUOTA'),
+      String(currentInputs.DataExportDefaultDisplayMode || 'QUOTA'),
     );
   }, [props.options]);
🧹 Nitpick comments (5)
web/src/components/dashboard/ChartsPanel.jsx (1)

50-63: Consider adding accessibility attributes to the toggle.

The display mode toggle has a title for hover tooltip but lacks an aria-label for screen reader users. Adding role="button" and aria-label would improve accessibility.

♿ Suggested improvement
             <div
               className='flex items-center gap-1 bg-[var(--semi-color-fill-0)] rounded px-1.5 py-1 cursor-pointer hover:bg-[var(--semi-color-fill-1)] transition-colors'
               onClick={() => onDisplayModeChange(displayMode === 'QUOTA' ? 'TOKENS' : 'QUOTA')}
               title={displayMode === 'QUOTA' ? t('切换为 Token') : t('切换为金额')}
+              role='button'
+              aria-label={displayMode === 'QUOTA' ? t('切换为 Token') : t('切换为金额')}
+              tabIndex={0}
+              onKeyDown={(e) => {
+                if (e.key === 'Enter' || e.key === ' ') {
+                  e.preventDefault();
+                  onDisplayModeChange(displayMode === 'QUOTA' ? 'TOKENS' : 'QUOTA');
+                }
+              }}
             >
web/src/hooks/dashboard/useDashboardCharts.jsx (4)

301-302: Questionable data field assignments for rawQuota and rawTokenUsed.

When displayMode === 'TOKENS', both rawQuota and rawTokenUsed are set to aggregated?.tokenUsed || 0. This means rawQuota loses the actual quota value, and rawTokenUsed is 0 when in QUOTA mode. This could cause issues if the display mode is toggled without refetching data, as the underlying raw values would be mode-dependent rather than preserving both values.

Consider always storing both raw values:

♻️ Suggested fix
           return {
             Time: time,
             Model: model,
-            rawQuota: displayMode === 'TOKENS' ? (aggregated?.tokenUsed || 0) : (aggregated?.quota || 0),
-            rawTokenUsed: displayMode === 'TOKENS' ? (aggregated?.tokenUsed || 0) : 0,
+            rawQuota: aggregated?.quota || 0,
+            rawTokenUsed: aggregated?.tokenUsed || 0,
             Usage: displayValue,
           };

307-321: Duplicate valueField declaration.

valueField is declared identically at lines 307 and 321. While valid due to block scoping, this duplicates the mode-switching logic. Consider extracting it to a single declaration at the beginning of the data processing block.

♻️ Suggested refactor
+      const valueField = displayMode === 'TOKENS' ? 'rawTokenUsed' : 'rawQuota';
+      const renderValue = displayMode === 'TOKENS' ? renderNumber : (v) => renderQuota(v, 4);
+
       chartTimePoints.forEach((time) => {
         // ... existing code ...
       });
 
-      const valueField = displayMode === 'TOKENS' ? 'rawTokenUsed' : 'rawQuota';
       const timeSum = timeData.reduce((sum, item) => sum + item[valueField], 0);
       // ...
 
       const totalValue = displayMode === 'TOKENS' ? totalTokens : totalQuota;
       const totalDisplay = displayMode === 'TOKENS' ? renderNumber(totalValue) : renderQuota(totalValue, 2);
 
-      // 生成 tooltip 配置
-      const renderValue = displayMode === 'TOKENS' ? renderNumber : (v) => renderQuota(v, 4);
-      const valueField = displayMode === 'TOKENS' ? 'rawTokenUsed' : 'rawQuota';
+      // 生成 tooltip 配置 (uses valueField and renderValue from above)

448-500: Duplicated tooltip configuration logic.

The tooltip configuration at lines 454-493 is nearly identical to lines 322-361 within updateChartData. This duplication increases maintenance burden and risk of divergence.

Consider extracting the tooltip generation into a shared helper function:

♻️ Suggested refactor
// Extract as a helper function within the hook or in helpers/dashboard.jsx
const createTooltipConfig = (displayMode, t) => {
  const renderValue = displayMode === 'TOKENS' ? renderNumber : (v) => renderQuota(v, 4);
  const valueField = displayMode === 'TOKENS' ? 'rawTokenUsed' : 'rawQuota';
  
  return {
    mark: {
      content: [
        {
          key: (datum) => datum['Model'],
          value: (datum) => renderValue(datum[valueField] || 0),
        },
      ],
    },
    dimension: {
      content: [
        {
          key: (datum) => datum['Model'],
          value: (datum) => datum[valueField] || 0,
        },
      ],
      updateContent: (array) => {
        array.sort((a, b) => b.value - a.value);
        let sum = 0;
        for (let i = 0; i < array.length; i++) {
          if (array[i].key == '其他') continue;
          let value = parseFloat(array[i].value);
          if (isNaN(value)) value = 0;
          if (array[i].datum && array[i].datum.TimeSum) {
            sum = array[i].datum.TimeSum;
          }
          array[i].value = renderValue(value);
        }
        array.unshift({ key: t('总计'), value: renderValue(sum) });
        return array;
      },
    },
  };
};

342-344: Use strict equality for string comparison.

Line 342 uses == for comparing array[i].key with '其他'. Prefer === for strict equality.

-              if (array[i].key == '其他') {
+              if (array[i].key === '其他') {

Calcium-Ion and others added 28 commits February 6, 2026 23:14
将散落在多个文件中的预扣费/结算/退款逻辑抽象为统一的 BillingSession 生命周期管理:

- 新增 BillingSettler 接口 (relay/common/billing.go) 避免循环引用
- 新增 FundingSource 接口 + WalletFunding / SubscriptionFunding 实现 (service/funding_source.go)
- 新增 BillingSession 封装预扣/结算/退款原子操作 (service/billing_session.go)
- 新增 SettleBilling 统一结算辅助函数,替换各 handler 中的 quotaDelta 模式
- 重写 PreConsumeBilling 为 BillingSession 工厂入口
- controller/relay.go 退款守卫改用 BillingSession.Refund()

修复的 Bug:
- 令牌额度泄漏:PreConsumeTokenQuota 成功但 DecreaseUserQuota 失败时未回滚
- 订阅退款遗漏:FinalPreConsumedQuota=0 但 SubscriptionPreConsumed>0 时跳过退款
- 订阅多扣费:subConsume 强制为 1 但 FinalPreConsumedQuota 不同步
- 退款路径不统一:钱包/订阅退款逻辑现统一由 FundingSource.Refund 分派
- Settle 部分失败保护:新增 fundingSettled 标记,资金来源提交后
  令牌调整失败不再导致 Refund 误退已结算的资金
- 订阅多扣费修复:trySubscription 传 subConsume 而非 preConsumedQuota
  给 preConsume,保证三者(amount/preConsume/FinalPreConsumedQuota)一致
- 令牌回滚错误记录:preConsume 中 funding 失败时令牌回滚错误不再丢弃
- 移除钱包路径死代码:用户额度不足的 strings.Contains 匹配不可能命中
- WalletFunding.Refund 不重试:IncreaseUserQuota 非幂等,重试会多退
…e recharge card tabs

- Defaulting to subscriptions when available and avoiding initial flash when no plans exist.
- Adjust the wide-screen layout to place wallet and invite sections side by side, simplify the subscription header and controls, and add padding to prevent card borders from clipping.
- Update related i18n strings by adding the new tab label and removing the obsolete subscription blurb.
…iption-card-when-no-plans

✨ refactor(wallet): Top-up layout to embed subscription plans into the recharge card tabs
…-session

refactor: 抽象统一计费会话 BillingSession
Add a lightweight active-subscription check to skip subscription pre-consume when none exist, reducing unnecessary transactions and locks. In the subscription UI, disable subscription-first options when no active plan is available, show the effective fallback to wallet with a clear notice, and distinguish “invalidated” from “expired” states. Update i18n strings across supported locales to reflect the new messages and status labels.
Aligns the error variable types in the subscription-first path so that quota fallback checks use the correct NewAPIError.
This prevents build failures and preserves the intended wallet fallback when subscription pre-consume returns an insufficient quota error.
Routes quota alerts through a subscription-specific check when billing from subscriptions, preventing wallet-based thresholds from triggering false warnings.
Updates the notification settings description and localization keys to clarify that both wallet and subscription balances are monitored.
…n-quota-notify

🔔 feat: Add subscription-aware quota notifications and update UI copy
…-preference-fallback

✨ chore: Improve subscription billing fallback and UI states
…tumNous#2881)

当上游为 AWS Bedrock 时,message_delta 的 usage 可能缺少 input_tokens、
cache_creation_input_tokens、cache_read_input_tokens 等字段,导致与原生
Anthropic 格式不一致。从 message_start 积累的 claudeInfo 中补全这些字段后
重新序列化,确保客户端收到一致的 usage 格式。
Modified the formatUserLogs function to include a startIdx parameter, allowing for more flexible log ID assignment. Updated calls to this function in GetLogByTokenId and GetUserLogs to pass the appropriate starting index.
feat: add Codex channel disclaimer (i18n, OpenAI terms)
feat: Force beta=true parameter for Anthropic channel
feat(oauth): implement custom OAuth provider
fix: Claude stream block index/type transitions
fix: add paragraph breaks between reasoning summary chunks
# Conflicts:
#	service/openaicompat/chat_to_responses.go
…t-stream

feat: channel test with stream=true
…fo-input-token

fix: 使用openai兼容接口调用部分渠道在最终端点为claude原生端点下还是走了openai扣减input_token的逻辑
fix: 补全 streaming message_delta 事件缺失的 input_tokens 和 cache 相关字段
…sponses

feat: /v1/messages -> /v1/responses
…ion-configurable

feat: make 5m cache-creation ratio configurable
Calcium-Ion and others added 20 commits March 9, 2026 22:19
…4f8a4248b0ab3b03ba703796ea3

fix: kling risk fail return openAIVideo error
…ride-beta-header-append

feat:support $keep_only_declared and deduped $append for header override
chore: update model lists for frequently used channels
@LinZX1314 LinZX1314 changed the title feat(dashboard): 添加数据看板模型消耗分布显示模式切换功能 feat(dashboard): 在数据看板中的模型消耗分布添加显示Token 功能并支持切换 Mar 15, 2026

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
web/src/i18n/locales/zh-CN.json (1)

2868-2885: Consider consolidating near-duplicate billing keys

There are many punctuation/format variants for the same billing phrase (with/without , spacing differences). This will increase translator workload and long-term drift. Prefer one canonical key per phrase pattern.

Also applies to: 2911-2918

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/i18n/locales/zh-CN.json` around lines 2868 - 2885, Several billing
translation keys are duplicated with minor punctuation/spacing differences
(e.g., "模型价格 {{symbol}}{{price}} / 次" vs "模型价格:{{symbol}}{{price}} / 次",
"缓存读取价格:..." vs "缓存读取价格 ..."); pick a single canonical key format for each
phrase group (recommend using the colon variant like "模型价格:{{symbol}}{{price}} /
次", "缓存读取价格:{{symbol}}{{price}} / 1M tokens", etc.), remove the duplicate
alternatives, and update all code references to use the chosen keys (search for
the exact keys present in the diff such as "模型价格 {{symbol}}{{price}} / 次", "按次
{{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", "缓存读取价格
{{symbol}}{{price}} / 1M tokens", etc.); repeat the same consolidation for the
other duplicates noted around lines 2911-2918 so translators only maintain one
canonical key per phrase.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/i18n/locales/zh-CN.json`:
- Around line 77-78: Several zh-CN JSON entries are left in English (e.g., the
keys/values "Creem Setting Tips", "Creem 介绍" and other entries like
"Configuration", "Prompt tokens", "Completion tokens", "Total tokens",
"Discovery claims/scopes"); update the values to proper Chinese translations so
the locale file is fully localized. Locate the entries in the zh-CN JSON (search
for the exact string literals "Creem Setting Tips", "Creem 介绍" and the other
listed English phrases) and replace their English values with appropriate
Chinese phrases, ensuring consistency with existing Chinese phrasing and other
translations in the file. Ensure plural/special terms match project terminology
and run a quick pass over the block around the mentioned keys (rows ~2923-2977)
to translate any remaining English strings.

---

Nitpick comments:
In `@web/src/i18n/locales/zh-CN.json`:
- Around line 2868-2885: Several billing translation keys are duplicated with
minor punctuation/spacing differences (e.g., "模型价格 {{symbol}}{{price}} / 次" vs
"模型价格:{{symbol}}{{price}} / 次", "缓存读取价格:..." vs "缓存读取价格 ..."); pick a single
canonical key format for each phrase group (recommend using the colon variant
like "模型价格:{{symbol}}{{price}} / 次", "缓存读取价格:{{symbol}}{{price}} / 1M tokens",
etc.), remove the duplicate alternatives, and update all code references to use
the chosen keys (search for the exact keys present in the diff such as "模型价格
{{symbol}}{{price}} / 次", "按次 {{symbol}}{{price}} * {{ratioType}} {{ratio}} =
{{symbol}}{{total}}", "缓存读取价格 {{symbol}}{{price}} / 1M tokens", etc.); repeat
the same consolidation for the other duplicates noted around lines 2911-2918 so
translators only maintain one canonical key per phrase.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9cd53cbf-d223-4347-a41a-44607bd4cc7d

📥 Commits

Reviewing files that changed from the base of the PR and between b3a63e9 and 81b4473.

📒 Files selected for processing (2)
  • web/src/i18n/locales/zh-CN.json
  • web/src/i18n/locales/zh.json

Comment thread web/src/i18n/locales/zh-CN.json
@LinZX1314
LinZX1314 force-pushed the feat/dashboard-consumption-mode branch from 81b4473 to 622ba01 Compare March 15, 2026 11:58

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
web/src/hooks/dashboard/useDashboardData.js (1)

177-185: Use t() for the hardcoded Chinese string.

The token_used: 0 addition is correct. However, '无数据' on line 180 should use the translation function for i18n compliance, as the t function is already available in this hook.

🌐 Proposed fix
         if (data.length === 0) {
           data.push({
             count: 0,
-            model_name: '无数据',
+            model_name: t('无数据'),
             quota: 0,
             token_used: 0,
             created_at: now.getTime() / 1000,
           });
         }

As per coding guidelines, web/src/**/*.{ts,tsx,js,jsx}: "Use useTranslation() hook and call t('中文key') in components."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/hooks/dashboard/useDashboardData.js` around lines 177 - 185, In the
useDashboardData hook, replace the hardcoded Chinese literal '无数据' in the
data.push object (property model_name) with the translation call using the
existing t function (e.g., model_name: t('无数据') or the appropriate i18n key such
as t('dashboard.noData')); ensure the replacement occurs inside the same
data.length === 0 block so the fallback row is localized.
web/src/components/dashboard/index.jsx (1)

153-158: Missing dependency in useEffect, but intentional design.

The effect omits dashboardCharts.updateChartData from the dependency array. Since updateChartData is memoized with displayMode as a dependency, adding it would cause double-execution when displayMode changes.

Consider adding an ESLint disable comment to document this intentional omission:

📝 Suggested documentation
   // 切换显示模式时重新计算图表
   useEffect(() => {
     if (dashboardData.quotaData && dashboardData.quotaData.length > 0) {
       dashboardCharts.updateChartData(dashboardData.quotaData);
     }
+    // eslint-disable-next-line react-hooks/exhaustive-deps -- updateChartData intentionally omitted to avoid double-execution when displayMode changes
   }, [displayMode, dashboardData.quotaData]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboard/index.jsx` around lines 153 - 158, The useEffect
that calls dashboardCharts.updateChartData omits updateChartData from
dependencies intentionally to avoid double execution when displayMode changes;
add a brief inline ESLint directive and explanatory comment above this effect to
document the decision (use the react-hooks/exhaustive-deps disable comment),
referencing the useEffect, dashboardCharts.updateChartData, displayMode and
dashboardData.quotaData so future readers know the omission is deliberate and
that updateChartData is memoized based on displayMode.
web/src/hooks/dashboard/useDashboardCharts.jsx (1)

466-476: Consider if this effect is necessary given the data update flow.

This effect updates the tooltip when displayMode changes. However, when displayMode changes, the parent component (index.jsx lines 153-158) also triggers updateChartData, which already applies the new tooltip configuration via updateChartSpec.

This creates some redundancy, though it's not harmful. If the intent is to provide immediate visual feedback before data reprocessing completes, this is fine. Otherwise, consider removing this effect to simplify the update flow.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/hooks/dashboard/useDashboardCharts.jsx` around lines 466 - 476, The
useEffect that recomputes tooltip on displayMode change is redundant because the
parent triggers updateChartData which already calls updateChartSpec to apply the
new tooltip; remove the useEffect block (the useEffect that calls
createTooltipConfig and setSpecLine) from useDashboardCharts.jsx and rely on
updateChartData/updateChartSpec to set the tooltip based on displayMode and t;
if immediate visual feedback is required instead of removal, replace the effect
with a brief comment explaining the intentional redundancy.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/i18n/locales/en.json`:
- Line 1521: Remove the duplicate JSON translation entry for the key "金额":
"Amount" in the en.json file; locate the repeated key ("金额": "Amount") shown in
the diff and delete this duplicate occurrence so only the original entry remains
(the other instance already present later in the file), ensuring the JSON stays
valid and no duplicate keys exist.

---

Nitpick comments:
In `@web/src/components/dashboard/index.jsx`:
- Around line 153-158: The useEffect that calls dashboardCharts.updateChartData
omits updateChartData from dependencies intentionally to avoid double execution
when displayMode changes; add a brief inline ESLint directive and explanatory
comment above this effect to document the decision (use the
react-hooks/exhaustive-deps disable comment), referencing the useEffect,
dashboardCharts.updateChartData, displayMode and dashboardData.quotaData so
future readers know the omission is deliberate and that updateChartData is
memoized based on displayMode.

In `@web/src/hooks/dashboard/useDashboardCharts.jsx`:
- Around line 466-476: The useEffect that recomputes tooltip on displayMode
change is redundant because the parent triggers updateChartData which already
calls updateChartSpec to apply the new tooltip; remove the useEffect block (the
useEffect that calls createTooltipConfig and setSpecLine) from
useDashboardCharts.jsx and rely on updateChartData/updateChartSpec to set the
tooltip based on displayMode and t; if immediate visual feedback is required
instead of removal, replace the effect with a brief comment explaining the
intentional redundancy.

In `@web/src/hooks/dashboard/useDashboardData.js`:
- Around line 177-185: In the useDashboardData hook, replace the hardcoded
Chinese literal '无数据' in the data.push object (property model_name) with the
translation call using the existing t function (e.g., model_name: t('无数据') or
the appropriate i18n key such as t('dashboard.noData')); ensure the replacement
occurs inside the same data.length === 0 block so the fallback row is localized.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 640bc80b-a9c8-4449-97e5-0e2998d38420

📥 Commits

Reviewing files that changed from the base of the PR and between 81b4473 and 622ba01.

📒 Files selected for processing (10)
  • model/option.go
  • web/src/components/dashboard/ChartsPanel.jsx
  • web/src/components/dashboard/index.jsx
  • web/src/components/settings/DashboardSetting.jsx
  • web/src/helpers/dashboard.jsx
  • web/src/hooks/dashboard/useDashboardCharts.jsx
  • web/src/hooks/dashboard/useDashboardData.js
  • web/src/i18n/locales/en.json
  • web/src/i18n/locales/zh-CN.json
  • web/src/pages/Setting/Dashboard/SettingsDataDashboard.jsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • model/option.go
  • web/src/pages/Setting/Dashboard/SettingsDataDashboard.jsx
  • web/src/helpers/dashboard.jsx

"默认显示模式": "Default Display Mode",
"显示金额": "Show Amount",
"显示 Token": "Show Tokens",
"金额": "Amount",

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.

⚠️ Potential issue | 🟡 Minor

Remove duplicate translation key "金额".

The key "金额": "Amount" already exists at line 3100. This duplicate entry should be removed to avoid JSON key conflicts.

🔧 Proposed fix
     "显示金额": "Show Amount",
     "显示 Token": "Show Tokens",
-    "金额": "Amount",
     "Token": "Token",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"金额": "Amount",
"显示金额": "Show Amount",
"显示 Token": "Show Tokens",
"Token": "Token",
🧰 Tools
🪛 Biome (2.4.6)

[error] 1521-1521: The key 金额 was already declared.

(lint/suspicious/noDuplicateObjectKeys)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/i18n/locales/en.json` at line 1521, Remove the duplicate JSON
translation entry for the key "金额": "Amount" in the en.json file; locate the
repeated key ("金额": "Amount") shown in the diff and delete this duplicate
occurrence so only the original entry remains (the other instance already
present later in the file), ensuring the JSON stays valid and no duplicate keys
exist.

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/i18n/locales/zh-CN.json`:
- Around line 151-153: The zh-CN locale currently uses placeholder values
identical to the keys for SSRF helper text; replace those placeholder values in
web/src/i18n/locales/zh-CN.json for the keys "SSRF防护开关详细说明", "SSRF防护设置", and
"SSRF防护详细说明" (and the other occurrences noted) with the full translated guidance
text used as the original security helper copy (match the English source helper
strings used by SystemSetting.jsx's extraText), so that SystemSetting.jsx (the
SSRF/network protection extraText) renders the complete, actionable instructions
rather than bare keys.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3943d7ba-0484-4b58-8d23-38587b65830c

📥 Commits

Reviewing files that changed from the base of the PR and between 622ba01 and be40f3b.

📒 Files selected for processing (1)
  • web/src/i18n/locales/zh-CN.json

Comment on lines +151 to +153
"SSRF防护开关详细说明": "SSRF防护开关详细说明",
"SSRF防护设置": "SSRF防护设置",
"SSRF防护详细说明": "SSRF防护可防止恶意用户利用您的服务器访问内网资源。您可以配置受信任域名/IP的白名单,并限制允许的端口。适用于文件下载、Webhook回调和通知功能。",
"SSRF防护详细说明": "SSRF防护详细说明",

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.

⚠️ Potential issue | 🟠 Major

Restore full security helper copy instead of self-label placeholders.

These keys are rendered as form extraText help content in web/src/components/settings/SystemSetting.jsx (Line 787-851). Keeping values equal to keys removes critical SSRF/network security guidance for zh-CN admins.

🌐 Suggested fix
-    "SSRF防护开关详细说明": "SSRF防护开关详细说明",
+    "SSRF防护开关详细说明": "总开关用于控制是否启用 SSRF 防护。关闭后将跳过所有 SSRF 检查,允许访问任意 URL。⚠️ 仅在完全可信环境下关闭。",
-    "SSRF防护详细说明": "SSRF防护详细说明",
+    "SSRF防护详细说明": "SSRF 防护可防止恶意用户利用服务器访问内网资源。请配置受信任域名/IP 白名单并限制允许端口。适用于文件下载、Webhook 和通知等外部请求。",
-    "域名IP过滤详细说明": "域名IP过滤详细说明",
+    "域名IP过滤详细说明": "⚠️ 这是实验性选项。域名可能解析到多个 IPv4/IPv6 地址。启用后请确保 IP 过滤列表覆盖这些地址,否则可能访问失败。",
-    "私有IP访问详细说明": "私有IP访问详细说明",
+    "私有IP访问详细说明": "⚠️ 安全警告:开启后可访问内网资源(localhost、私有网段)。仅在确有内网访问需求且理解风险时启用。",
-    "端口配置详细说明": "端口配置详细说明",
+    "端口配置详细说明": "限制外部请求可访问的端口。支持单端口(80、443)或范围(8000-8999)。留空表示允许全部端口。默认包含常见 Web 端口。",

Also applies to: 734-734, 1783-1783, 1794-1794

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/i18n/locales/zh-CN.json` around lines 151 - 153, The zh-CN locale
currently uses placeholder values identical to the keys for SSRF helper text;
replace those placeholder values in web/src/i18n/locales/zh-CN.json for the keys
"SSRF防护开关详细说明", "SSRF防护设置", and "SSRF防护详细说明" (and the other occurrences noted)
with the full translated guidance text used as the original security helper copy
(match the English source helper strings used by SystemSetting.jsx's extraText),
so that SystemSetting.jsx (the SSRF/network protection extraText) renders the
complete, actionable instructions rather than bare keys.

@LinZX1314
LinZX1314 force-pushed the feat/dashboard-consumption-mode branch from be40f3b to b3a63e9 Compare March 15, 2026 12:21
- Merge zh.json into zh-CN.json (keep local additions)
- Remove zh.json (split to zh-CN/zh-TW in upstream)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.