Skip to content

Add dashboard time range switcher and custom picker#3383

Closed
MisonL wants to merge 14 commits into
QuantumNous:mainfrom
MisonL:feat/dashboard-time-range-switcher
Closed

Add dashboard time range switcher and custom picker#3383
MisonL wants to merge 14 commits into
QuantumNous:mainfrom
MisonL:feat/dashboard-time-range-switcher

Conversation

@MisonL
Copy link
Copy Markdown

@MisonL MisonL commented Mar 22, 2026

📝 变更描述 / Description

  • 在 dashboard 模型分析卡片内增加快捷时间范围切换与自定义时间区间选择。
  • 将自定义草稿状态与已提交查询状态拆分,避免编辑中的时间范围立即污染线上查询参数。
  • 修正预设时间范围计算逻辑,使起止时间基线一致。
  • 保持范围状态持久化,并补充异常时间戳保护。

🚀 变更类型 / Type of change

  • 🐛 Bug 修复 (Bug fix)
  • ✨ 新功能 (New feature)
  • ⚡ 性能优化 / 重构 (Refactor)
  • 📝 文档更新 (Documentation)

🔗 关联任务 / Related Issue

✅ 提交前检查项 / Checklist

  • 人工确认: 我已亲自撰写此描述,去除了 AI 原始输出的冗余。
  • 深度理解: 我已完全理解这些更改的工作原理及潜在影响。
  • 范围聚焦: 本 PR 未包含任何与当前任务无关的代码改动。
  • 本地验证: 已在本地运行并通过了测试或手动验证。
  • 安全合规: 代码中无敏感凭据,且符合项目代码规范。

📸 运行证明 / Proof of Work

  • cd web && bunx eslint src/components/dashboard/ChartsPanel.jsx src/components/dashboard/index.jsx src/hooks/dashboard/useDashboardData.js
  • cd web && bun run build
  • 浏览器验证 24h / 7d / 30d / 90d / custom 交互与 Apply 行为

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 22, 2026

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 interactive dashboard chart time-range controls (quick presets + custom date-time), threads range state/handlers through Dashboard and useDashboardData, persists/validates chart-range, updates quota loading to accept overrides, and adds i18n keys for range labels.

Changes

Cohort / File(s) Summary
Dashboard UI
web/src/components/dashboard/ChartsPanel.jsx, web/src/components/dashboard/index.jsx
Adds a time-range control bar (quick preset buttons, “自定义范围”, DatePicker, Select, “应用”); ChartsPanel receives new range props and callbacks; Dashboard wires handlers to apply/confirm ranges and triggers quota/chart reloads.
Range State Management (hook)
web/src/hooks/dashboard/useDashboardData.js
Introduces buffered end-time init, activeRangePreset, customRangeDraft, preset detection/validation helpers; adds applyChartRangePreset, activateCustomRange, handleCustomRangeChange, applyCustomRange; refactors loadQuotaData/refresh to accept overrides and improves parsing/error handling.
Helpers & Persistence
web/src/helpers/dashboard.jsx, web/src/constants/dashboard.constants.js
Adds strict timestamp parsing (parseDashboardTimestamp), default-time normalization (normalizeDefaultTime), stored-range getters/setters (getStoredChartRange/setStoredChartRange), getInitialChartRange(endTimestamp), quick-range configs, and a new storage key.
Rendering helper
web/src/helpers/render.jsx
Hardens renderQuota numeric handling: coerce/validate quota and quota_per_unit, guard non-finite/zero cases, and adjust token/USD computations.
i18n locales
web/src/i18n/locales/*.json (en, fr, ja, ru, vi, zh-CN, zh-TW)
Adds translation keys for time-range UI (时间范围, 最近24小时, 最近7天, 最近30天, 最近90天, 自定义范围) and minor fixes to some existing phrases.

Sequence Diagram

sequenceDiagram
    participant User as "User"
    participant ChartsPanel as "ChartsPanel"
    participant Dashboard as "Dashboard"
    participant Hook as "useDashboardData"
    participant API as "Quota API"

    User->>ChartsPanel: select preset / open custom picker / confirm
    ChartsPanel->>Dashboard: handleRangePresetChange(preset) / handleCustomRangeConfirm()
    Dashboard->>Hook: applyChartRangePreset(preset) / applyCustomRange()
    Hook->>Hook: update inputs, dataExportDefaultTime, activeRangePreset, customRangeDraft
    Hook->>API: loadQuotaData(overrideInputs, overrideDefaultTime)
    API-->>Hook: return data
    Hook-->>Dashboard: updated chart data
    Dashboard-->>ChartsPanel: pass activeRangePreset, customRangeDraft, chart data
    ChartsPanel->>User: render updated range UI and charts
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • creamlike1024
  • Calcium-Ion

Poem

🐇 I hop through presets, tick each second's song,

From quick 24 to weeks that stretch along,
I nudge the start, I coax the end to bind,
Charts wake and hum — each metric neatly lined,
A carrot-clicked range, all tidy and timed.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title directly summarizes the main changes: adding a time range switcher UI component and custom date-time picker to the dashboard.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

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

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
Copy Markdown
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: 2

🤖 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/index.jsx`:
- Around line 105-135: The preset and custom-range handlers currently call
dashboardData.refresh() which triggers a full dashboard reload (including
uptime) and races unrelated requests; change both handleRangePresetChange and
handleCustomRangeConfirm to avoid dashboardData.refresh(): after getting
rangeState from dashboardData.applyChartRangePreset(...) or
dashboardData.applyCustomRange(), call the chart-only fetch method (e.g.,
dashboardData.fetchChartData or dashboardData.getChartData) with
rangeState.nextInputs and rangeState.nextDefaultTime, then pass the returned
data to dashboardCharts.updateChartData(...); keep dashboardData.refresh()
reserved only for the explicit dashboard-wide refresh button.

In `@web/src/hooks/dashboard/useDashboardData.js`:
- Around line 325-332: The synthetic empty-state point is being stamped with
created_at: now.getTime() / 1000 which places the fake point at the current time
and stretches the x-axis; change that to a timestamp inside the requested range
(use the existing range bounds used for the query—e.g., rangeStart/rangeEnd or
from/to variables in this hook—such as the midpoint ((start + end)/2) or the
range start) when constructing the fallback object before calling setQuotaData;
also remove the const now = new Date() declaration above if it becomes unused
after this change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4d626e84-1052-4b1a-b8a2-836c66d0db62

📥 Commits

Reviewing files that changed from the base of the PR and between deff59a and 96ae3ba.

⛔ Files ignored due to path filters (1)
  • docs/pr-assets/dashboard-time-range-90d.png is excluded by !**/*.png
📒 Files selected for processing (10)
  • web/src/components/dashboard/ChartsPanel.jsx
  • web/src/components/dashboard/index.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

Comment thread web/src/components/dashboard/index.jsx
Comment thread web/src/hooks/dashboard/useDashboardData.js Outdated
@MisonL
Copy link
Copy Markdown
Author

MisonL commented Mar 22, 2026

Validation screenshot:\n\nDashboard validation

Copy link
Copy Markdown
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.

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

35-38: Document the 1-hour buffer in the end timestamp calculation.

The + 3600 adds 1 hour to the current time, which I assume is intentional buffering to ensure recent data isn't clipped. Consider adding a brief comment explaining this design choice, or extracting it as a named constant.

📝 Suggested improvement
+ const END_TIME_BUFFER_SECONDS = 3600; // Buffer to include recently arrived data
+
  const getCurrentEndTimestamp = useCallback(
-   () => timestamp2string(new Date().getTime() / 1000 + 3600),
+   () => timestamp2string(new Date().getTime() / 1000 + END_TIME_BUFFER_SECONDS),
    [],
  );
🤖 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 35 - 38, The
getCurrentEndTimestamp useCallback currently adds a magic +3600 seconds to Now
(timestamp2string(new Date().getTime() / 1000 + 3600)); document this 1-hour
buffer or extract it as a named constant (e.g., ONE_HOUR_BUFFER_SECONDS = 3600)
and replace the literal, and add a short comment above getCurrentEndTimestamp
explaining that the extra hour is an intentional buffer to avoid clipping recent
data; reference the getCurrentEndTimestamp function and timestamp2string to
locate the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@web/src/hooks/dashboard/useDashboardData.js`:
- Around line 35-38: The getCurrentEndTimestamp useCallback currently adds a
magic +3600 seconds to Now (timestamp2string(new Date().getTime() / 1000 +
3600)); document this 1-hour buffer or extract it as a named constant (e.g.,
ONE_HOUR_BUFFER_SECONDS = 3600) and replace the literal, and add a short comment
above getCurrentEndTimestamp explaining that the extra hour is an intentional
buffer to avoid clipping recent data; reference the getCurrentEndTimestamp
function and timestamp2string to locate the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 06faef02-1d24-41f0-a308-ae7255b40b57

📥 Commits

Reviewing files that changed from the base of the PR and between 96ae3ba and 985cca1.

📒 Files selected for processing (2)
  • web/src/components/dashboard/index.jsx
  • web/src/hooks/dashboard/useDashboardData.js

Copy link
Copy Markdown
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: 2

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

330-339: Normalize data before storing it in state.

The empty-state push() and sort() happen after setQuotaData(data), so the array already stored in React state is mutated outside the setter. Build nextData first, then set and return that value once.

♻️ Proposed fix
         if (success) {
-          setQuotaData(data);
-          if (data.length === 0) {
-            data.push({
-              count: 0,
-              model_name: '无数据',
-              quota: 0,
-              created_at: emptyStateTimestamp,
-            });
-          }
-          data.sort((a, b) => a.created_at - b.created_at);
-          return data;
+          const nextData =
+            data.length === 0
+              ? [
+                  {
+                    count: 0,
+                    model_name: '无数据',
+                    quota: 0,
+                    created_at: emptyStateTimestamp,
+                  },
+                ]
+              : [...data];
+          nextData.sort((a, b) => a.created_at - b.created_at);
+          setQuotaData(nextData);
+          return nextData;
         } else {
🤖 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 330 - 339, The
current code calls setQuotaData(data) before mutating the same array (pushing
empty-state item and sorting), which mutates React state after setting it;
instead, create a new array (e.g., nextData) from data, perform the empty-state
push and sort on nextData, then call setQuotaData(nextData) and return/use
nextData; reference the setQuotaData call and the local data variable in your
fix and ensure you never mutate the passed-in data array in-place.
🤖 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/hooks/dashboard/useDashboardData.js`:
- Around line 277-295: The applyCustomRange function currently only checks for
presence of start_timestamp and end_timestamp but still commits reversed or
zero-length ranges; update applyCustomRange to validate that start_timestamp and
end_timestamp are numeric and that start_timestamp < end_timestamp (and
optionally that end - start > 0) before calling setInputs,
setDataExportDefaultTime and setActiveRangePreset('custom'); if the validation
fails, do not mutate state (return null) and optionally surface an
error/validation flag to the UI. Keep checks tied to customRangeDraft and update
the dependency array accordingly.
- Around line 55-65: The initial range uses a raw now while presets use a
buffered end timestamp, causing a ~3600s mismatch; update the code so
getInitialTimestamp() uses the same buffered end-time that
applyChartRangePreset() uses (either by changing getInitialTimestamp to accept
an endTimestamp parameter or by computing the initial start_timestamp using the
buffered end timestamp before calling getInitialTimestamp), and ensure
detectQuickRangePreset()/getQuickRangeConfig() comparisons still operate on that
aligned start/end pair so the preset detection and initial load produce
identical ranges.

---

Nitpick comments:
In `@web/src/hooks/dashboard/useDashboardData.js`:
- Around line 330-339: The current code calls setQuotaData(data) before mutating
the same array (pushing empty-state item and sorting), which mutates React state
after setting it; instead, create a new array (e.g., nextData) from data,
perform the empty-state push and sort on nextData, then call
setQuotaData(nextData) and return/use nextData; reference the setQuotaData call
and the local data variable in your fix and ensure you never mutate the
passed-in data array in-place.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b8f06c65-608b-44a6-b0dc-3666e5b61164

📥 Commits

Reviewing files that changed from the base of the PR and between 985cca1 and fbb7532.

📒 Files selected for processing (1)
  • web/src/hooks/dashboard/useDashboardData.js

Comment thread web/src/hooks/dashboard/useDashboardData.js
Comment thread web/src/hooks/dashboard/useDashboardData.js Outdated
Copy link
Copy Markdown
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: 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/hooks/dashboard/useDashboardData.js`:
- Around line 79-88: The current logic only persists granularity
(data_export_default_time) causing ambiguity between presets (e.g., 7d vs 30d
both map to "day"); update persistence so the active preset or explicit window
is stored and restored: when applyChartRangePreset() runs, save a clear
identifier for the preset (e.g., preset key like "7d"/"30d"/"90d") or the
concrete start_timestamp and end_timestamp in addition to granularity; then
update the restore path used by initialChartRange (and the similar restore code
at the other locations mentioned) to prefer restoring the saved preset
identifier (or exact start/end) before falling back to
getDefaultTime/getInitialTimestamp, ensuring the dashboard reconstructs the
exact user-selected window rather than inferring from granularity alone.
- Around line 323-367: The loadQuotaData hook currently assumes API.get(url)
always resolves; wrap the await API.get(url) call in a try/catch so
network/non-2xx rejections are handled inside loadQuotaData: on catch call
showError with the caught error (or its message), ensure you return [] and still
call setLoading(false) (the finally can remain) and avoid letting the rejection
bubble to refresh/click handlers; keep updating setQuotaData only on success and
reference loadQuotaData, API.get, showError, setQuotaData and setLoading when
making the change.
- Around line 349-353: The empty-state row is hardcoding the Chinese label '无数据'
inside the nextData.push in useDashboardData.js; import and call the i18n hook
(useTranslation) in this hook/component and replace the literal with t('无数据') so
the label is localized (ensure useTranslation is referenced at top and t is used
where nextData is built, e.g., inside the code path that pushes the empty row).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 71b30141-557c-42f2-9988-e4c1ef17bf85

📥 Commits

Reviewing files that changed from the base of the PR and between fbb7532 and d45aeca.

📒 Files selected for processing (3)
  • web/src/components/dashboard/ChartsPanel.jsx
  • web/src/helpers/dashboard.jsx
  • web/src/hooks/dashboard/useDashboardData.js

Comment thread web/src/hooks/dashboard/useDashboardData.js
Comment thread web/src/hooks/dashboard/useDashboardData.js Outdated
Comment thread web/src/hooks/dashboard/useDashboardData.js Outdated
Copy link
Copy Markdown
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: 2

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

84-86: Validate (or clear) on write, not only on read.

setStoredChartRange currently persists any payload, including invalid/null shapes, and relies on later reads to clean it up. Prefer enforcing the invariant at write-time.

Proposed refactor
 export const setStoredChartRange = (range) => {
-  localStorage.setItem(STORAGE_KEYS.DASHBOARD_CHART_RANGE, JSON.stringify(range));
+  if (!isValidStoredChartRange(range)) {
+    localStorage.removeItem(STORAGE_KEYS.DASHBOARD_CHART_RANGE);
+    return;
+  }
+  localStorage.setItem(
+    STORAGE_KEYS.DASHBOARD_CHART_RANGE,
+    JSON.stringify(range),
+  );
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/helpers/dashboard.jsx` around lines 84 - 86, setStoredChartRange
currently writes any value (including null/invalid shapes) to
STORAGE_KEYS.DASHBOARD_CHART_RANGE; change it to validate the incoming range
before persisting and clear the stored key when invalid. In the
setStoredChartRange function, check that the provided range matches the expected
shape (e.g. required properties and types) and only call localStorage.setItem
when valid; if range is null/invalid, call
localStorage.removeItem(STORAGE_KEYS.DASHBOARD_CHART_RANGE) (or set a safe
default) so subsequent reads don’t need to sanitize bad data.
🤖 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/helpers/dashboard.jsx`:
- Around line 117-122: In getInitialChartRange, don't assign raw
getDefaultTime() to default_time; instead pass the result through the same
normalization/validation used elsewhere (e.g., call the existing
normalizeDefaultTime/validateDefaultTime helper) before returning. Update the
return object so default_time is set to the normalized value
(normalizeDefaultTime(getDefaultTime())), ensuring getInitialChartRange,
getDefaultTime, getInitialTimestamp and the returned default_time use the same
validation path to avoid emitting invalid defaults from stale/corrupt storage.
- Around line 96-99: The code uses Date.parse on endTimestamp (used in
parsedEndTimestamp/baseTimestamp) which can silently fail for the "YYYY-MM-DD
HH:mm:ss" produced by timestamp2string(); normalize or explicitly parse that
format before calling Date.parse. Update the logic around
parsedEndTimestamp/baseTimestamp (and the provider
getCurrentEndTimestamp()/timestamp2string()) to either convert the "YYYY-MM-DD
HH:mm:ss" to strict ISO (replace the space with 'T' and append timezone like 'Z'
or local offset) or implement an explicit parser that extracts
year/month/day/hour/minute/second and builds a UTC timestamp (e.g., via
Date.UTC) so Date.parse won't rely on browser-dependent behavior and won't
silently fall back to new Date().

---

Nitpick comments:
In `@web/src/helpers/dashboard.jsx`:
- Around line 84-86: setStoredChartRange currently writes any value (including
null/invalid shapes) to STORAGE_KEYS.DASHBOARD_CHART_RANGE; change it to
validate the incoming range before persisting and clear the stored key when
invalid. In the setStoredChartRange function, check that the provided range
matches the expected shape (e.g. required properties and types) and only call
localStorage.setItem when valid; if range is null/invalid, call
localStorage.removeItem(STORAGE_KEYS.DASHBOARD_CHART_RANGE) (or set a safe
default) so subsequent reads don’t need to sanitize bad data.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6b16f07a-2957-4237-8528-22f750b9713e

📥 Commits

Reviewing files that changed from the base of the PR and between d45aeca and 541a0a3.

📒 Files selected for processing (3)
  • web/src/constants/dashboard.constants.js
  • web/src/helpers/dashboard.jsx
  • web/src/hooks/dashboard/useDashboardData.js
✅ Files skipped from review due to trivial changes (1)
  • web/src/constants/dashboard.constants.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/src/hooks/dashboard/useDashboardData.js

Comment thread web/src/helpers/dashboard.jsx Outdated
Comment thread web/src/helpers/dashboard.jsx Outdated
@MisonL
Copy link
Copy Markdown
Author

MisonL commented Mar 22, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 22, 2026

✅ 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.

@MisonL
Copy link
Copy Markdown
Author

MisonL commented Mar 22, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 22, 2026

✅ 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.

@MisonL
Copy link
Copy Markdown
Author

MisonL commented Mar 22, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 22, 2026

✅ 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.

Copy link
Copy Markdown
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: 1

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

54-59: Derive valid presets from the config map.

VALID_RANGE_PRESETS now repeats the same preset names already declared in DASHBOARD_QUICK_RANGE_CONFIGS, and the preset loop in web/src/hooks/dashboard/useDashboardData.js carries a third copy. That drift will be easy to miss the next time a preset is added.

♻️ Proposed refactor
-const VALID_RANGE_PRESETS = new Set(['24h', '7d', '30d', '90d', 'custom']);
+const VALID_RANGE_PRESETS = new Set([
+  ...Object.keys(DASHBOARD_QUICK_RANGE_CONFIGS),
+  'custom',
+]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/helpers/dashboard.jsx` around lines 54 - 59, VALID_RANGE_PRESETS is
hardcoded and duplicates presets defined in DASHBOARD_QUICK_RANGE_CONFIGS (and a
third copy exists in useDashboardData.js); replace the hardcoded Set with one
derived from the config map so presets stay in sync: change VALID_RANGE_PRESETS
to new Set(Object.keys(DASHBOARD_QUICK_RANGE_CONFIGS)) in
web/src/helpers/dashboard.jsx, keep getDashboardQuickRangeConfig as-is, and
update any code (e.g., the preset loop in useDashboardData.js) to rely on this
derived VALID_RANGE_PRESETS instead of duplicating preset names.
🤖 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/helpers/dashboard.jsx`:
- Around line 107-112: The predicate currently uses "!preset" which falsely
rejects only null/undefined; update it to only allow an absent preset or a known
preset string by replacing the preset check with a null/undefined check and a
string-type/whitelist check: use "preset == null || (typeof preset === 'string'
&& VALID_RANGE_PRESETS.has(preset))" in the same return expression that
references startTime, endTime, default_time, normalizeDefaultTime and
VALID_RANGE_PRESETS so corrupted values like '', 0, or false won’t be treated as
missing.

---

Nitpick comments:
In `@web/src/helpers/dashboard.jsx`:
- Around line 54-59: VALID_RANGE_PRESETS is hardcoded and duplicates presets
defined in DASHBOARD_QUICK_RANGE_CONFIGS (and a third copy exists in
useDashboardData.js); replace the hardcoded Set with one derived from the config
map so presets stay in sync: change VALID_RANGE_PRESETS to new
Set(Object.keys(DASHBOARD_QUICK_RANGE_CONFIGS)) in
web/src/helpers/dashboard.jsx, keep getDashboardQuickRangeConfig as-is, and
update any code (e.g., the preset loop in useDashboardData.js) to rely on this
derived VALID_RANGE_PRESETS instead of duplicating preset names.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 58f310f8-66f6-47e8-acc8-f2c0e4f4cb54

📥 Commits

Reviewing files that changed from the base of the PR and between 415516e and ce8c9df.

📒 Files selected for processing (10)
  • web/src/components/dashboard/ChartsPanel.jsx
  • web/src/components/dashboard/index.jsx
  • web/src/constants/dashboard.constants.js
  • web/src/helpers/dashboard.jsx
  • web/src/helpers/render.jsx
  • web/src/hooks/dashboard/useDashboardData.js
  • web/src/i18n/locales/fr.json
  • web/src/i18n/locales/ja.json
  • web/src/i18n/locales/ru.json
  • web/src/i18n/locales/vi.json
✅ Files skipped from review due to trivial changes (1)
  • web/src/i18n/locales/vi.json
🚧 Files skipped from review as they are similar to previous changes (5)
  • web/src/components/dashboard/index.jsx
  • web/src/i18n/locales/fr.json
  • web/src/i18n/locales/ja.json
  • web/src/components/dashboard/ChartsPanel.jsx
  • web/src/hooks/dashboard/useDashboardData.js

Comment thread web/src/helpers/dashboard.jsx Outdated
@MisonL
Copy link
Copy Markdown
Author

MisonL commented Mar 23, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 23, 2026

✅ 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.

Copy link
Copy Markdown
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.

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

372-383: Consider validating timestamps before building the API URL.

If parseDashboardTimestamp returns NaN for malformed inputs, the API URL will include start_timestamp=NaN&end_timestamp=NaN. While emptyStateTimestamp has a fallback, the actual API request proceeds with invalid parameters.

Since callers like handleSearchConfirm and applyCustomRange validate ranges before calling refresh, this is likely a minor edge case, but adding a guard here would make loadQuotaData more defensive.

🛡️ Optional defensive check
       let localStartTimestamp =
         parseDashboardTimestamp(start_timestamp) / 1000;
       let localEndTimestamp = parseDashboardTimestamp(end_timestamp) / 1000;
+      if (!Number.isFinite(localStartTimestamp) || !Number.isFinite(localEndTimestamp)) {
+        showError(t('请求参数无效'));
+        setLoading(false);
+        return [];
+      }
       const emptyStateTimestamp = Number.isFinite(localStartTimestamp)
         ? localStartTimestamp
         : 0;
🤖 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 372 - 383, Validate
that parseDashboardTimestamp yielded finite numbers before interpolating into
the API URL: check Number.isFinite(localStartTimestamp) and
Number.isFinite(localEndTimestamp) (computed from parseDashboardTimestamp) and
if either is invalid, replace them with emptyStateTimestamp or omit the
start/end params when building url in the loadQuotaData/refresh flow; update the
conditional that sets url (the branches using isAdminUser) to use these guarded
values so the constructed URL never contains "NaN" for start_timestamp or
end_timestamp.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@web/src/hooks/dashboard/useDashboardData.js`:
- Around line 372-383: Validate that parseDashboardTimestamp yielded finite
numbers before interpolating into the API URL: check
Number.isFinite(localStartTimestamp) and Number.isFinite(localEndTimestamp)
(computed from parseDashboardTimestamp) and if either is invalid, replace them
with emptyStateTimestamp or omit the start/end params when building url in the
loadQuotaData/refresh flow; update the conditional that sets url (the branches
using isAdminUser) to use these guarded values so the constructed URL never
contains "NaN" for start_timestamp or end_timestamp.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b11830ea-4ce2-4a6f-9145-7ae857026ce5

📥 Commits

Reviewing files that changed from the base of the PR and between ce8c9df and 67b43ed.

📒 Files selected for processing (2)
  • web/src/helpers/dashboard.jsx
  • web/src/hooks/dashboard/useDashboardData.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/src/helpers/dashboard.jsx

@MisonL
Copy link
Copy Markdown
Author

MisonL commented Mar 24, 2026

Latest branch head already includes the invalid timestamp guard in loadQuotaData. @coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 24, 2026

@MisonL Thanks for the heads-up on the invalid timestamp guard in loadQuotaData — noted! Triggering a fresh review of the latest changes 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.

@MisonL
Copy link
Copy Markdown
Author

MisonL commented Mar 24, 2026

CodeRabbit follow-up fixes

已调整 dashboard 时间范围切换的数据刷新流:

  • chart range 变更现在走 loadQuotaData(),不再触发 full refresh,也不会额外等待 uptime 请求。
  • 为 quota 数据请求增加顺序号保护,快速切换范围时旧请求结果不会覆盖最新图表状态。

已验证:

  • cd web && bunx eslint src/hooks/dashboard/useDashboardData.js
  • cd web && bun run build

commit: 768bd2e0

@MisonL MisonL force-pushed the feat/dashboard-time-range-switcher branch from e9db5e8 to cbdcb3f Compare March 26, 2026 09:14
@ghost

This comment was marked as spam.

@MisonL MisonL force-pushed the feat/dashboard-time-range-switcher branch 2 times, most recently from f0a3de9 to 015d66a Compare April 3, 2026 08:51
@MisonL MisonL force-pushed the feat/dashboard-time-range-switcher branch from 015d66a to 1c32491 Compare April 7, 2026 06:21
@MisonL
Copy link
Copy Markdown
Author

MisonL commented Apr 7, 2026

同步当前整理结果:

  • 这条 PR 和 #3395 共享 dashboard 图表/范围组件,但没有功能依赖。
  • 哪个先合都可以;后合的那个按最新 main 做一次常规 rebase 即可。

@MisonL
Copy link
Copy Markdown
Author

MisonL commented Apr 7, 2026

这条 PR 先不再按当前形态继续推进。

原因:上游 dashboard 时间范围相关已有持续演进,但没有采用这里的具体实现。当前 PR 体量偏大,不适合继续挂着。

后续处理:如果继续推进,会改成更小的 dashboard feature 补丁再提。当前这条 PR 先关闭归档。

@MisonL
Copy link
Copy Markdown
Author

MisonL commented Apr 7, 2026

关闭当前大 PR,后续按更小补丁重提。

@MisonL MisonL closed this Apr 7, 2026
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