Fix Estimated API value custom date range - #173
Conversation
Custom ranges now fall back to the scanned daily dollar series when the window key is empty, always send real since/until to the backend, keep the ring layout while loading, and show a cleaner custom date bar with a range pill.
📝 WalkthroughWalkthroughThe change adds daily API-value series to local chart data, derives inclusive custom-range totals from those series, and updates the API value card to render custom-period totals, trends, loading states, errors, and range-specific UI. ChangesCustom API value ranges
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant TotalApiValueCard
participant getLocalApiValueTotals
participant get_local_api_value_totals
participant LocalApiValueProvider
TotalApiValueCard->>getLocalApiValueTotals: request selected date range
getLocalApiValueTotals->>get_local_api_value_totals: forward complete range
get_local_api_value_totals->>LocalApiValueProvider: return daily_series and periods
TotalApiValueCard->>TotalApiValueCard: aggregate custom period and build trends
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
ceiling | 0c30551 | Commit Preview URL Branch Preview URL |
Jul 28 2026, 01:54 AM |
There was a problem hiding this comment.
Pull request overview
Fixes “Estimated API value” custom date ranges showing No data by ensuring scanned daily series is available and by making custom-range selection more robust across the frontend ↔ Tauri command boundary.
Changes:
- Backend: ensure custom-window totals aren’t dropped when a daily bucket is missing; expose a scanned
daily_seriesand use it as a fallback for empty custom windows. - Frontend: only send
since/untilwhen both are present; synthesize custom totals fromdailySerieswhen needed; update card UI for custom range controls and loading behavior. - Tests/CSS: add unit tests for the new fallback behavior and update styling for the custom date range bar.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| rust/src/cost_scanner.rs | Fixes window aggregation so custom/reset windows are credited even if a daily bucket is absent. |
| apps/desktop-tauri/src-tauri/src/commands/chart.rs | Adds daily_series, increases default scan horizon, and falls back to summing daily series when custom window is empty. |
| apps/desktop-tauri/src/types/bridge.ts | Extends the bridge type with dailySeries for UI fallback/trend building. |
| apps/desktop-tauri/src/lib/tauri.ts | Omits since/until args unless both are set (avoids null/empty range calls). |
| apps/desktop-tauri/src/lib/apiValueCard.ts | Adds a dailySeries→period fallback for custom ranges in the model builder. |
| apps/desktop-tauri/src/lib/apiValueCard.test.ts | Adds unit test coverage for dailySeries fallback when custom is empty. |
| apps/desktop-tauri/src/components/TotalApiValueCard.tsx | Uses the new fallback/synth logic, refines custom range UI, and adjusts loading/error presentation. |
| apps/desktop-tauri/src/components/TotalApiValueCard.test.tsx | Updates tests for custom-range loading behavior and dailySeries presence. |
| apps/desktop-tauri/src/styles.css | Styles the custom range bar (pill, separator, background). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| let Some(day_summary) = self.daily.get_mut(&record.day_key) else { | ||
| continue; | ||
| }; | ||
| if let Some(cost) = add_codex_record_to_summary(day_summary, record) { | ||
| day_summary.total_cost_usd += cost; |
| const rows = providers | ||
| .map((provider) => ({ provider, period: periodOf(provider, periodKey) })) | ||
| .map((provider) => ({ | ||
| provider, | ||
| period: pickPeriod(provider, periodKey, customRange), | ||
| })) |
| )} | ||
|
|
||
| {safeModel.isEmpty || rangeError ? ( | ||
| {loading ? ( |
Prefer daily-series fallback when the custom window is empty, add unit tests that sum inclusive day ranges, and satisfy clippy::redundant_closure.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
apps/desktop-tauri/src/components/TotalApiValueCard.tsx (1)
176-198: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winThe "dailySeries else lastSevenDays" custom fallback is implemented three times. The same selection rule lives in the load effect, in
periodOf, and inbuildTrend; sincebuildApiValueCardis already called withcustomRange, the effect's copy is redundant and the three must now be kept in sync by hand.
apps/desktop-tauri/src/components/TotalApiValueCard.tsx#L176-L198: drop the synthesis block and justsetProviders(rows)—buildApiValueCard(providers, period, metric, customRange)already fills the custom period fromdailySeries.apps/desktop-tauri/src/lib/apiValueCard.ts#L106-L117: extract the series pick into an exported helper (e.g.customSeriesOf(provider)) and use it here.apps/desktop-tauri/src/components/TotalApiValueCard.tsx#L113-L119: consume that same helper inbuildTrendinstead of repeating the ternary.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/desktop-tauri/src/components/TotalApiValueCard.tsx` around lines 176 - 198, The custom-series fallback is duplicated across the load effect, periodOf, and buildTrend; centralize it in an exported customSeriesOf(provider) helper. In apps/desktop-tauri/src/components/TotalApiValueCard.tsx lines 176-198, remove the synthesis block and always call setProviders(rows); in apps/desktop-tauri/src/lib/apiValueCard.ts lines 106-117, extract and use the helper; in apps/desktop-tauri/src/components/TotalApiValueCard.tsx lines 113-119, replace the local ternary with that helper.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/desktop-tauri/src-tauri/src/commands/chart.rs`:
- Around line 948-993: Fix the daily-derived custom-period path so it does not
report fabricated zero token counts: update daily_series_from_report and its
callers to use per-day token data from the report, or preserve the window’s
token figures when unavailable and mark pricing coverage as unknown. Ensure
period_from_daily_series does not set tokens, priced_tokens, or total_tokens to
zero for dollar-only data.
In `@apps/desktop-tauri/src/components/TotalApiValueCard.tsx`:
- Around line 99-103: Update dayLabel to accept whether the range is custom and
return a month/day label for custom periods, while preserving weekday labels for
other periods and the existing invalid-date fallback. Pass period === "custom"
from buildTrend when constructing each TrendDay so custom-range axis labels
distinguish dates across multiple weeks.
- Around line 243-249: Update the loading branch in TotalApiValueCard so
subsequent loading states preserve the existing ring, legend, and trend layout
by rendering safeModel inside a dimmed or aria-busy wrapper instead of replacing
the body with a status paragraph. Remove the now-redundant initial loading guard
around the component’s early return, while retaining an appropriate first-load
empty state when no model exists.
---
Nitpick comments:
In `@apps/desktop-tauri/src/components/TotalApiValueCard.tsx`:
- Around line 176-198: The custom-series fallback is duplicated across the load
effect, periodOf, and buildTrend; centralize it in an exported
customSeriesOf(provider) helper. In
apps/desktop-tauri/src/components/TotalApiValueCard.tsx lines 176-198, remove
the synthesis block and always call setProviders(rows); in
apps/desktop-tauri/src/lib/apiValueCard.ts lines 106-117, extract and use the
helper; in apps/desktop-tauri/src/components/TotalApiValueCard.tsx lines
113-119, replace the local ternary with that helper.
🪄 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: eaf2b3fd-21a3-465d-8412-bd25526b2ed6
📒 Files selected for processing (9)
apps/desktop-tauri/src-tauri/src/commands/chart.rsapps/desktop-tauri/src/components/TotalApiValueCard.test.tsxapps/desktop-tauri/src/components/TotalApiValueCard.tsxapps/desktop-tauri/src/lib/apiValueCard.test.tsapps/desktop-tauri/src/lib/apiValueCard.tsapps/desktop-tauri/src/lib/tauri.tsapps/desktop-tauri/src/styles.cssapps/desktop-tauri/src/types/bridge.tsrust/src/cost_scanner.rs
| fn daily_series_from_report(daily_costs: &[(String, f64)]) -> Vec<LocalApiValueDay> { | ||
| let mut days: Vec<LocalApiValueDay> = daily_costs | ||
| .iter() | ||
| .map(|(date, api_value_usd)| LocalApiValueDay { | ||
| date: date.clone(), | ||
| api_value_usd: *api_value_usd, | ||
| tokens: 0, | ||
| }) | ||
| .collect(); | ||
| days.sort_by(|left, right| left.date.cmp(&right.date)); | ||
| days | ||
| } | ||
|
|
||
| /// Inclusive local-calendar sum from the daily dollar series. | ||
| fn period_from_daily_series( | ||
| daily_series: &[LocalApiValueDay], | ||
| start: NaiveDate, | ||
| end_inclusive: NaiveDate, | ||
| ) -> LocalApiValuePeriod { | ||
| if start > end_inclusive { | ||
| return empty_api_value_period(); | ||
| } | ||
| let mut api_value_usd = 0.0; | ||
| let mut tokens: u64 = 0; | ||
| let mut has_data = false; | ||
| for day in daily_series { | ||
| let Ok(date) = NaiveDate::parse_from_str(&day.date, "%Y-%m-%d") else { | ||
| continue; | ||
| }; | ||
| if date < start || date > end_inclusive { | ||
| continue; | ||
| } | ||
| if day.api_value_usd > 0.0 || day.tokens > 0 { | ||
| has_data = true; | ||
| } | ||
| api_value_usd += day.api_value_usd; | ||
| tokens = tokens.saturating_add(day.tokens); | ||
| } | ||
| LocalApiValuePeriod { | ||
| api_value_usd, | ||
| tokens, | ||
| priced_tokens: tokens, | ||
| total_tokens: tokens, | ||
| has_data: has_data || api_value_usd > 0.0, | ||
| } | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Daily-derived custom periods report zero tokens.
daily_series_from_report hardcodes tokens: 0 because report.daily_costs only carries dollars, so any custom range that falls back to period_from_daily_series (line 886/892) yields tokens = priced_tokens = total_tokens = 0 while api_value_usd > 0. Switching the card's metric to Tokens then renders 0 for a range that visibly has dollars, and pricing coverage silently disappears. Either extend the report to expose per-day token counts, or make the fallback keep the window's token figures / mark coverage as unknown instead of 0.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/desktop-tauri/src-tauri/src/commands/chart.rs` around lines 948 - 993,
Fix the daily-derived custom-period path so it does not report fabricated zero
token counts: update daily_series_from_report and its callers to use per-day
token data from the report, or preserve the window’s token figures when
unavailable and mark pricing coverage as unknown. Ensure
period_from_daily_series does not set tokens, priced_tokens, or total_tokens to
zero for dollar-only data.
| function dayLabel(date: string): string { | ||
| const parsed = new Date(`${date}T12:00:00`); | ||
| if (Number.isNaN(parsed.getTime())) return date.slice(5); | ||
| return WEEKDAYS[parsed.getDay()]; | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Weekday labels repeat across multi-week custom ranges.
dayLabel always renders a weekday name, so a custom range spanning more than a week produces an axis of repeated "Mon/Tue/…" with no way to tell the weeks apart (only the bar title disambiguates). Consider a month/day label when period === "custom".
Proposed fix
-function dayLabel(date: string): string {
+function dayLabel(date: string, compact = false): string {
const parsed = new Date(`${date}T12:00:00`);
if (Number.isNaN(parsed.getTime())) return date.slice(5);
- return WEEKDAYS[parsed.getDay()];
+ return compact ? date.slice(5) : WEEKDAYS[parsed.getDay()];
}Then pass period === "custom" through buildTrend when building each TrendDay.
Also applies to: 120-132
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/desktop-tauri/src/components/TotalApiValueCard.tsx` around lines 99 -
103, Update dayLabel to accept whether the range is custom and return a
month/day label for custom periods, while preserving weekday labels for other
periods and the existing invalid-date fallback. Pass period === "custom" from
buildTrend when constructing each TrendDay so custom-range axis labels
distinguish dates across multiple weeks.
| if (!model && loading) { | ||
| return ( | ||
| <section className="api-value-card" aria-label="Total API value"> | ||
| <p className="api-value-card__status">Reading local usage…</p> | ||
| </section> | ||
| ); | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Body still collapses on every reload, so the ring layout isn't preserved.
The early return at Line 243 correctly limits the bare status to the first load, but Line 355 replaces the whole body (ring, legend, trend) with a single paragraph on every subsequent loading pass — switching periods or editing a date collapses the card and then re-expands it. If the intent is to keep the layout while refreshing, render safeModel with a dimmed/aria-busy wrapper instead of swapping the body out. This also makes the Line 243 guard redundant.
Also applies to: 355-359
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/desktop-tauri/src/components/TotalApiValueCard.tsx` around lines 243 -
249, Update the loading branch in TotalApiValueCard so subsequent loading states
preserve the existing ring, legend, and trend layout by rendering safeModel
inside a dimmed or aria-busy wrapper instead of replacing the body with a status
paragraph. Remove the now-redundant initial loading guard around the component’s
early return, while retaining an appropriate first-load empty state when no
model exists.
## Summary Version bump to **1.5.16** (build 118) for the custom date range fix already on main via #173. ## Release plan 1. Merge this PR 2. Tag `v1.5.16` on the merge commit 3. Publish signed draft when `release.yml` finishes Co-authored-by: tsouth89 <tsouth89@users.noreply.github.com>
Summary
Custom date range on Estimated API value was showing No data even when local logs had spend for that window (reproduced on a machine where the scanner returned thousands of dollars for the same dates).
Fixes
dailySeriesand fall back to summing those days when the custom window is emptysince/untilwhen both dates are setTest plan
Summary by CodeRabbit
New Features
Bug Fixes
Style