Skip to content

feat(dashboard): add token usage by model & user dimension with tab visibility control - #4329

Open
rangerTao wants to merge 8 commits into
QuantumNous:mainfrom
rangerTao:feat/dashboard-token-usage-by-model
Open

feat(dashboard): add token usage by model & user dimension with tab visibility control#4329
rangerTao wants to merge 8 commits into
QuantumNous:mainfrom
rangerTao:feat/dashboard-token-usage-by-model

Conversation

@rangerTao

@rangerTao rangerTao commented Apr 18, 2026

Copy link
Copy Markdown

Closes #4328

Summary

Enhance the data dashboard with model-dimension and user-dimension token usage
statistics, plus admin/user controllable chart tab visibility.

Changes

Backend

  • controller/usedata.go, model/usedata.go: add model_name filter to data
    endpoints; extend aggregation to include token_used
  • model/option.go, common/constants.go: add DataDashboardChartTabs option
  • controller/misc.go: expose the new option in status API

Frontend

  • components/dashboard/ChartsPanel.jsx: add Token Usage Distribution,
    User Token Ranking, and User Token Trend chart tabs
  • components/dashboard/modals/SearchModal.jsx: add model name filter input
  • hooks/dashboard/useDashboardCharts.jsx, useDashboardData.js:
    per-model/per-user aggregation logic
  • components/settings/DashboardSetting.jsx,
    Setting/Dashboard/SettingsDataDashboard.jsx: admin tab visibility config
  • constants/dashboard.constants.js, helpers/dashboard.jsx, helpers/data.js:
    shared definitions & helpers
  • Per-user tab preference via Popover settings button (localStorage)
  • Priority: user preference > admin global > show all

i18n

  • Translations added for zh-CN / zh-TW / en / fr / ja / ru / vi (7 keys each)

Test plan

  • Verify dashboard with model_name filter returns correctly scoped data
  • Verify new chart tabs render and respond to date range changes
  • Verify admin-set tab visibility takes effect, user override works
  • Verify i18n renders correctly across locales

Summary by CodeRabbit

Release Notes

New Features

  • Added model name filtering for quota data queries
  • Dashboard chart tabs are now customizable—users can toggle visibility and administrators can configure global defaults
  • Added interface controls to manage chart display preferences with reset-to-defaults option

taoliang1 and others added 4 commits April 15, 2026 15:53
- Add model_name filter parameter to all /api/data/ endpoints (backend)
- Add model name input field to dashboard SearchModal (frontend)
- Add new "Token Usage Distribution" stacked bar chart tab
- Extend data aggregation to include token_used field
- Add i18n translations for 7 locales
- Add "User Token Ranking" horizontal bar chart (admin)
- Add "User Token Trend" area chart (admin)
- Extend processUserData to aggregate token_used per user
- Add i18n translations for 7 locales
- Add DataDashboardChartTabs global setting (admin, comma-separated keys)
- Add CheckboxGroup in dashboard settings for admin to select visible tabs
- Add per-user tab preference via Popover settings button (localStorage)
- Priority: user preference > admin global > show all
- Add i18n translations for 7 locales
@coderabbitai

coderabbitai Bot commented Apr 18, 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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 151251f5-6f53-4dbd-b5ed-356c8f05eeef

📥 Commits

Reviewing files that changed from the base of the PR and between b88a8ba and 19346aa.

📒 Files selected for processing (1)
  • controller/misc.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • controller/misc.go

Walkthrough

This PR introduces model-dimension filtering and chart tab visibility controls to the data dashboard. It adds a global DataDashboardChartTabs setting, extends quota data endpoints to filter by model_name, implements per-user tab preferences via localStorage, and rewrites the ChartsPanel component to dynamically render visible tabs based on admin settings and user selection.

Changes

Dashboard Model Filtering & Tab Visibility

Layer / File(s) Summary
Configuration
common/constants.go
New global variable DataDashboardChartTabs (empty string default) stores comma-separated chart tab keys to display.
Backend Option Mapping
model/option.go
InitOptionMap registers DataDashboardChartTabs from the constant; updateOptionMap adds a branch to sync database-driven changes back to the in-memory constant.
Status Endpoint
controller/misc.go
GetStatus response includes data_dashboard_chart_tabs field sourced from common.DataDashboardChartTabs.
Data Layer Filtering
model/usedata.go
Four quota query functions (GetAllQuotaDates, GetQuotaDataByUsername, GetQuotaDataByUserId, GetQuotaDataGroupByUser) accept new modelName parameter and conditionally filter results by WHERE model_name = ? when non-empty.
Controller API Handlers
controller/usedata.go
Three handlers (GetAllQuotaDates, GetQuotaDatesByUser, GetUserQuotaDates) extract model_name query parameter and pass it to updated model layer functions.
Search UI
web/classic/src/components/dashboard/modals/SearchModal.jsx
Add model_name input field to search form, positioned before the username field.
Settings State
web/classic/src/components/settings/DashboardSetting.jsx
Extend inputs state with DataDashboardChartTabs key initialized to empty string.
Dashboard Specs
web/classic/src/components/dashboard/index.jsx
Forward additional chart specification props (spec_token_bar, spec_user_token_rank, spec_user_token_trend) to ChartsPanel.
Tab Visibility & Rendering
web/classic/src/components/dashboard/ChartsPanel.jsx
Compute dynamic visibleTabs by merging user localStorage preference (CHART_TABS_USER) with global admin setting (CHART_TABS_GLOBAL), render data-driven tabs using SPEC_MAP lookup instead of hard-coded TabPane conditionals, add Popover UI for tab toggle checkboxes with localStorage persistence, and auto-switch activeChartTab if it becomes unavailable.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • QuantumNous/new-api#1698: Both PRs modify model/option.go to register new configuration keys and add updateOptionMap handling branches for runtime updates.
  • QuantumNous/new-api#2780: Both PRs extend the GetStatus response in controller/misc.go with new dashboard-related fields.

Poem

🐰 Charts now shimmer with flexible tabs,
Admin sets defaults, users pick their maps,
Model names filter the quota streams,
Visible preferences fulfill dashboard dreams! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 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 feature additions: model-dimension token usage filtering and chart tab visibility control, matching the core changes across backend and frontend.
Linked Issues check ✅ Passed The code changes implement all primary objectives from #4328: model_name filtering in endpoints, three new chart tabs (Token Distribution, User Ranking, User Trend), chart visibility controls with localStorage persistence, and dashboard SearchModal enhancements.
Out of Scope Changes check ✅ Passed All changes are directly aligned with #4328 requirements: backend filtering/aggregation, frontend chart tabs, visibility controls, SearchModal enhancement, and settings integration—no unrelated modifications detected.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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: 3

🤖 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/components/dashboard/ChartsPanel.jsx`:
- Around line 127-161: Replace the bare Settings SVG used as the Popover trigger
with the existing Semi Button so keyboard and screen-reader users can open the
chart settings; specifically, wrap or render a <Button> (use size='small'
type='tertiary' and keep the current classes) as the Popover children trigger
instead of the plain Settings icon, pass the Settings icon via the Button's icon
prop, and add an accessible label such as aria-label={t('图表显示设置')} (or title) so
assistive tech and keyboard focus can activate the Popover while preserving
behavior tied to checkedUserTabs, handleUserTabsChange and handleResetUserTabs.

In `@web/src/i18n/locales/zh-TW.json`:
- Around line 2092-2093: The new translations were added under a nested
"translation" object instead of the required flat JSON shape; move the keys
"图表显示设置" and "可见图表" out of any nested "translation" object and place them as
top-level keys in zh-TW.json with their values ("圖表顯示設定", "可見圖表"), then run the
project's i18n sync/extraction script (the same tooling used for other locale
updates) to ensure the file shape and key ordering match the project's flat JSON
guideline and update any related locale entries mentioned (3206-3208).

In `@web/src/pages/Setting/Dashboard/SettingsDataDashboard.jsx`:
- Around line 167-180: The current logic treats an empty string as “all charts”
because DataDashboardChartTabs === '' is used for the all-selected case; change
the encoding so empty selection is distinct (e.g. use a sentinel like '__NONE__'
or null) and update both the value and onChange handling for
DataDashboardChartTabs: when building the value for the checkbox group, map the
sentinel to an empty array instead of ALL_CHART_TABS, and in setInputs (inside
the onChange handler) store the sentinel when checkedValues.length === 0, store
'' (or the existing all-sentinel) when checkedValues length equals
ALL_CHART_TABS.length, otherwise store checkedValues.join(','); update any
consumers that interpret DataDashboardChartTabs to respect the new
empty-selection sentinel.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 001b54f5-1eee-4ea5-8e70-5d627d072255

📥 Commits

Reviewing files that changed from the base of the PR and between f995a86 and 14178a1.

📒 Files selected for processing (22)
  • common/constants.go
  • controller/misc.go
  • controller/usedata.go
  • model/option.go
  • model/usedata.go
  • web/src/components/dashboard/ChartsPanel.jsx
  • web/src/components/dashboard/index.jsx
  • web/src/components/dashboard/modals/SearchModal.jsx
  • web/src/components/settings/DashboardSetting.jsx
  • web/src/constants/dashboard.constants.js
  • web/src/helpers/dashboard.jsx
  • web/src/helpers/data.js
  • web/src/hooks/dashboard/useDashboardCharts.jsx
  • web/src/hooks/dashboard/useDashboardData.js
  • web/src/i18n/locales/en.json
  • web/src/i18n/locales/fr.json
  • web/src/i18n/locales/ja.json
  • web/src/i18n/locales/ru.json
  • web/src/i18n/locales/vi.json
  • web/src/i18n/locales/zh-CN.json
  • web/src/i18n/locales/zh-TW.json
  • web/src/pages/Setting/Dashboard/SettingsDataDashboard.jsx

Comment thread web/src/components/dashboard/ChartsPanel.jsx
Comment thread web/src/i18n/locales/zh-TW.json
Comment thread web/src/pages/Setting/Dashboard/SettingsDataDashboard.jsx
- ChartsPanel: replace bare Settings SVG trigger with Semi Button +
  aria-label so keyboard and screen-reader users can open the chart
  visibility popover
- Introduce CHART_TABS_NONE ('__none__') sentinel so admin can persist
  "hide all charts"; '' keeps meaning "show all" for backward compat
- SettingsDataDashboard: encode empty vs all selection distinctly
- ChartsPanel: extract parseGlobalChartTabs helper and handle the new
  sentinel in both visibleTabs and availableTabs
@rangerTao

Copy link
Copy Markdown
Author

Review feedback addressed

Pushed 575f10b addressing @coderabbitai review:

  • ChartsPanel a11y — Popover trigger replaced with Semi Button + aria-label
  • Empty-selection bug — added CHART_TABS_NONE = '__none__' sentinel; encoding is now:
    '' = show all · '__none__' = hide all · 'k1,k2' = whitelist. Extracted parseGlobalChartTabs helper so visibleTabs and availableTabs in ChartsPanel.jsx both honor the sentinel.
  • ⚠️ zh-TW.json locale shape — respectfully disagree, this looks like a false positive. All 7 locale files (en/zh-CN/zh-TW/fr/ja/ru/vi) use the i18next default-namespace shape {"translation": { ... }}, and the new keys are placed as top-level entries inside translation, consistent with every existing entry. Details in the inline reply.

Ready for another look 🙏

@coderabbitai

coderabbitai Bot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor
🧩 Analysis chain

Let me check the updated code to verify the fixes.


🏁 Script executed:

#!/bin/bash
# Check ChartsPanel for Button + aria-label fix
rg -n "aria-label\|Button\|Popover" web/src/components/dashboard/ChartsPanel.jsx | head -40

echo "--- dashboard.constants.js ---"
cat web/src/constants/dashboard.constants.js

echo "--- parseGlobalChartTabs helper ---"
rg -n "parseGlobalChartTabs\|CHART_TABS_NONE\|__none__" web/src/constants/dashboard.constants.js web/src/helpers/dashboard.jsx web/src/components/dashboard/ChartsPanel.jsx

echo "--- zh-TW.json snippet ---"
python3 -c "
import json
with open('web/src/i18n/locales/zh-TW.json') as f:
    data = json.load(f)
keys = list(data.keys())
print('Top-level keys:', keys)
if 'translation' in data:
    new_keys = [k for k in data['translation'] if k in ['图表显示设置','可见图表','Token消耗分布','用户Token排行','用户Token趋势']]
    print('New keys in translation:', new_keys)
"

echo "--- ChartsPanel Popover/Button section ---"
grep -n -A5 -B5 "aria-label\|<Button\|Popover" web/src/components/dashboard/ChartsPanel.jsx | head -80

Length of output: 6819


@rangerTao: Thanks for the detailed update and for pushing the fixes! Here's what I verified:

✅ ChartsPanel a11y (Line 167–174) — Confirmed. The Popover trigger is now a proper Semi <Button> with icon={<Settings />}, theme='borderless', and aria-label={t('图表显示设置')}. Clean fix.

✅ Empty-selection sentinel — Confirmed. CHART_TABS_NONE = '__none__' is exported from dashboard.constants.js with a clear comment block documenting the three-state encoding ('' = all, '__none__' = none, 'k1,k2' = whitelist). The semantics are now unambiguous for both the admin setting and the user override path.

⚠️ zh-TW.json locale shape — You are correct, and I apologize for the false positive. I verified the file programmatically: the only top-level key is "translation", and all five new keys (图表显示设置, 可见图表, Token消耗分布, 用户Token排行, 用户Token趋势) are properly placed inside it — consistent with every other locale file. That comment was an error on my end.

Triggering a fresh review pass now.

ᓚᘏᗢ

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 2

🧹 Nitpick comments (1)
web/src/components/dashboard/ChartsPanel.jsx (1)

36-46: Add guard for missing or undefined specs to prevent silent rendering errors.

SPEC_MAP keys currently align with ALL_CHART_TABS keys ('1'–'9'), so no drift issue exists today. However, the render block at lines 195–201 passes spec={specs[specKey]} without checking if specKey or specs[specKey] is defined. If a future tab is added to ALL_CHART_TABS without a corresponding SPEC_MAP entry, or vice versa, VChart will silently receive undefined, leading to a blank or broken chart. A defensive guard ensures robustness:

{visibleTabs.map((tab) => {
  if (activeChartTab !== tab.key) return null;
  const specKey = SPEC_MAP[tab.key];
+ if (!specKey || !specs[specKey]) return null;
  return (
    <VChart key={tab.key} spec={specs[specKey]} option={CHART_CONFIG} />
  );
})}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboard/ChartsPanel.jsx` around lines 36 - 46, Add a
defensive guard where the component renders VChart so it never receives an
undefined spec: before passing spec={specs[specKey]} (in the render that uses
SPEC_MAP, ALL_CHART_TABS, specKey, specs and VChart) verify that specKey exists
in SPEC_MAP and that specs[specKey] is defined; if missing, skip rendering that
VChart or pass a safe fallback (e.g., an empty spec or a placeholder component)
and log or warn so missing mappings are visible at runtime.
🤖 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/components/dashboard/ChartsPanel.jsx`:
- Around line 103-107: handleUserTabsChange currently returns early when
checkedValues.length === 0 causing a silent no-op; instead, detect the empty
array and call the existing reset logic (reuse handleResetUserTabs semantics) so
the UI falls back to the global/default set and localStorage is updated.
Concretely, inside handleUserTabsChange (referencing setUserTabs and
STORAGE_KEYS.CHART_TABS_USER), replace the early return with a branch that calls
handleResetUserTabs() (or sets user tabs to the default array and writes that
value to localStorage) so the user sees the reset state rather than an
untickable checkbox.
- Around line 85-124: visibleTabs and availableTabs read
STORAGE_KEYS.CHART_TABS_GLOBAL directly inside useMemo but it isn't in their
dependency arrays, so updates to the admin global setting (localStorage) won't
recompute; fix by making the global setting reactive (e.g., create a small hook
that subscribes to window "storage" events or useSyncExternalStore to expose the
parsed global tabs) and replace the inline localStorage reads in visibleTabs and
availableTabs with that reactive value (use the hook to obtain parsedGlobalTabs
and include it in the useMemo dependencies), keeping parseGlobalChartTabs,
userTabs, isAdminUser, visibleTabs, and availableTabs as the identifying symbols
to modify.

---

Nitpick comments:
In `@web/src/components/dashboard/ChartsPanel.jsx`:
- Around line 36-46: Add a defensive guard where the component renders VChart so
it never receives an undefined spec: before passing spec={specs[specKey]} (in
the render that uses SPEC_MAP, ALL_CHART_TABS, specKey, specs and VChart) verify
that specKey exists in SPEC_MAP and that specs[specKey] is defined; if missing,
skip rendering that VChart or pass a safe fallback (e.g., an empty spec or a
placeholder component) and log or warn so missing mappings are visible at
runtime.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3e2ddf2e-e7e1-474d-836d-284226e21881

📥 Commits

Reviewing files that changed from the base of the PR and between 14178a1 and 575f10b.

📒 Files selected for processing (3)
  • web/src/components/dashboard/ChartsPanel.jsx
  • web/src/constants/dashboard.constants.js
  • web/src/pages/Setting/Dashboard/SettingsDataDashboard.jsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • web/src/pages/Setting/Dashboard/SettingsDataDashboard.jsx
  • web/src/constants/dashboard.constants.js

Comment thread web/src/components/dashboard/ChartsPanel.jsx
Comment thread web/src/components/dashboard/ChartsPanel.jsx

@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


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4ee0930e-0dc8-4874-83e3-275f07704cb5

📥 Commits

Reviewing files that changed from the base of the PR and between 575f10b and 50e44b6.

📒 Files selected for processing (7)
  • common/constants.go
  • controller/misc.go
  • model/option.go
  • web/classic/src/components/dashboard/ChartsPanel.jsx
  • web/classic/src/components/dashboard/index.jsx
  • web/classic/src/components/dashboard/modals/SearchModal.jsx
  • web/classic/src/components/settings/DashboardSetting.jsx
✅ Files skipped from review due to trivial changes (3)
  • common/constants.go
  • controller/misc.go
  • web/classic/src/components/settings/DashboardSetting.jsx

Comment thread web/classic/src/components/dashboard/ChartsPanel.jsx
…tion

availableTabs was filtered by admin's globalTabs, which prevented users
from selecting tabs outside the admin's allow-list. This contradicted
the stated precedence (user preference > admin global > show all) and
deadlocked the Popover when admin set __none__ (no checkboxes offered).

Removing the globalTabs filter keeps admin's setting as a default for
users who have not customized their preference, while allowing users to
opt any permitted tab back on. adminOnly permission filter is retained.

Flagged by CodeRabbit review on PR QuantumNous#4329.
@Calcium-Ion
Calcium-Ion force-pushed the main branch 2 times, most recently from 51fdfc5 to 2b6f1df Compare August 30, 2026 15:03
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.

feat(dashboard): support token usage statistics by model dimension

1 participant