Skip to content

Conversation

@LinZX1314
Copy link

@LinZX1314 LinZX1314 commented Jan 24, 2026

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

Summary by CodeRabbit

Release Notes

  • New Features

    • Added display mode toggle for dashboard charts to switch between Amount and Token viewing metrics
    • Introduced settings option to set the default display mode for model consumption charts
  • Chores

    • Added internationalization strings for new display mode options

✏️ Tip: You can customize this high-level summary in your review settings.

- 在模型消耗分布标签页添加 $/Token 切换按钮
- 在数据看板设置中新增默认显示模式配置项
- Token 模式使用数据库 token_used 字段
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 24, 2026

Walkthrough

The PR adds a display mode feature allowing users to toggle between quota/amount and token views in data dashboard charts. Changes include backend option handling, frontend settings UI with localStorage persistence, component-level display mode toggle, and data processing logic to support both modes with conditional rendering.

Changes

Cohort / File(s) Summary
Backend Option Configuration
model/option.go
Added DataExportDefaultDisplayMode option with default value "QUOTA"; integrated handling in updateOptionMap with database documentation.
Settings & State Management
web/src/pages/Setting/Dashboard/SettingsDataDashboard.jsx, web/src/components/settings/DashboardSetting.jsx
Added new settings UI field for default display mode selection; persists to localStorage as data_export_default_display_mode; integrated with existing defaults layout.
Dashboard UI Components
web/src/components/dashboard/ChartsPanel.jsx, web/src/components/dashboard/index.jsx
Added display mode toggle in ChartsPanel with DollarSign/Coins icons for QUOTA/TOKENS modes; threaded displayMode state and callback through component hierarchy with localStorage persistence.
Data Processing & Hook Logic
web/src/helpers/dashboard.jsx, web/src/hooks/dashboard/useDashboardCharts.jsx, web/src/hooks/dashboard/useDashboardData.js
Enhanced aggregation to track tokenUsed field; refactored useDashboardCharts to conditionally compute quota vs. token values based on displayMode; added dynamic tooltip configuration; extended dependency arrays for reactivity to mode changes.
Internationalization
web/src/i18n/locales/en.json, web/src/i18n/locales/zh.json
Added 8 translation keys in both English and Chinese covering display mode labels, toggle actions, and setting descriptions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • creamlike1024
  • Calcium-Ion

Poem

🐰 A toggle appears, so shiny and bright,
With quotas and tokens—the dashboards take flight!
From settings to charts, the mode dances free,
Storage remembers your preference, you see!
Fuzzy logic hops through each data array,
Display modes arranged in a cottontail way! 🥕

🚥 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 accurately describes the main change: adding a display mode toggle feature for the consumption distribution chart in the dashboard.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

Copy link
Contributor

@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: 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 === '其他') {

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.

1 participant