Skip to content

fix(frontend): UTC day boundaries so dashboard today matches everywhere - #2931

Open
riderx wants to merge 8 commits into
mainfrom
cursor/dashboard-utc-timezone-1e2d
Open

fix(frontend): UTC day boundaries so dashboard today matches everywhere#2931
riderx wants to merge 8 commits into
mainfrom
cursor/dashboard-utc-timezone-1e2d

Conversation

@riderx

@riderx riderx commented Aug 7, 2026

Copy link
Copy Markdown
Member

Summary (AI generated)

  • Dashboard daily charts and API date params use UTC calendar day boundaries so “today” matches for all viewers of the same org.
  • Review follow-up: exclusive UTC upper bound for deployments, elapsed-UTC billing-day math (incl. 31-day cycles), UTC log-nav day bounds, Chart.js Legend registration, reactive cycle ends for expired subscriptions.

Motivation (AI generated)

Brazil customers saw today’s device/stats after a release; Capgo (Europe) viewing the same org only saw through yesterday. Local midnight + toISOString() shifted the UTC day for positive offsets. Review bots also flagged remaining UTC edge cases that could drop today’s deployments or mis-size billing charts.

Business Impact (AI generated)

Dashboard “today” and billing-period charts stay consistent worldwide for the same org, avoiding false “missing data” support issues around timezone boundaries.

Test Plan (AI generated)

  • Unit tests for UTC date params, billing boundaries, day bounds, billing-window math
  • Same org from UTC+2 and UTC−3: network from/to end on the same UTC day
  • Deployments later on the current UTC day appear in Deployment stats
  • Chart day click opens logs for that UTC calendar day in US timezones
  • Billing period starting on the 30th/31st keeps day-1 and today’s buckets

Generated with AI

Open in Web Open in Cursor 

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • Improved dashboard chart and billing-period accuracy across time zones with consistent UTC day boundaries.
    • Corrected date ranges, tooltip dates, “today” indicators, and rolling 30-day calculations.
    • Improved handling of missing daily data and chart color consistency.
    • Added consistent minimum loading delays for dashboard statistics.
  • Refactor

    • Standardized chart configuration, billing-cycle handling, and date processing across dashboard views.
    • Improved billing-period filtering and daily-series calculations.

Dashboard charts derived "today" from browser-local midnight then
converted with toISOString(), so Europe (east of UTC) requested
yesterday while Brazil kept today. Align all dashboard date ranges
and API params with UTC days to match backend daily_* buckets.

Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR centralizes UTC date handling and dashboard chart behavior. It adds shared billing-cycle, series transformation, Chart.js registration, color, today-line, filtering, and loading-delay utilities.

Changes

UTC date and range foundation

Layer / File(s) Summary
UTC date and range foundation
src/services/date.ts, src/services/buildCharts.ts, src/services/chartDataService.ts, src/services/chartTooltip.ts, src/services/supabase.ts, src/services/conversion.ts, src/stores/*, src/composables/useBuildChartConfig.ts, tests/*
Date normalization, date arithmetic, API parameters, chart ranges, tooltip dates, store calculations, fallback ranges, and tests now use UTC calendar boundaries.

Shared chart contracts and services

Layer / File(s) Summary
Shared chart contracts and services
src/composables/useOrgBillingCycleChart.ts, src/services/dailyChartProps.ts, src/services/dashboardChartRegister.ts, src/services/chartTodayLine.ts, src/utils/chartOptimizations.ts, src/utils/minDelay.ts, tests/chart-optimizations.unit.test.ts
Shared utilities now provide chart props, Chart.js registration, billing-cycle transformations, today-line options, safe colors, billing-period filtering, dynamic series windows, and minimum loading delays.

Dashboard data and loading flows

Layer / File(s) Summary
Dashboard data and loading flows
src/components/dashboard/BundleUploadsCard.vue, src/components/dashboard/DeploymentStatsCard.vue, src/components/dashboard/DevicesStats.vue, src/components/dashboard/UpdateStatsCard.vue, src/components/dashboard/Usage.vue
Dashboard queries, series sizing, billing-period filtering, statistic indexing, date labels, evolution calculations, and loading delays now use shared UTC-aware logic.

Dashboard chart integrations

Layer / File(s) Summary
Dashboard chart integrations
src/components/dashboard/BundleUploadsChart.vue, src/components/dashboard/DeploymentStatsChart.vue, src/components/dashboard/LineChartStats.vue, src/components/dashboard/UpdateStatsChart.vue
Dashboard charts now use shared props, registration, billing-cycle state, series transformation, color generation, tooltip routing, and today-line configuration.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DashboardComponent
  participant BillingCycleComposable
  participant DailySeriesService
  participant ChartTodayLine
  DashboardComponent->>BillingCycleComposable: resolve UTC cycle and today limits
  DashboardComponent->>DailySeriesService: transform daily series
  DashboardComponent->>ChartTodayLine: create today-line options
  ChartTodayLine-->>DashboardComponent: return chart configuration
Loading

Possibly related PRs

  • Cap-go/capgo.app#2755 — Both changes use UTC-normalized billing-period calculations and half-open date ranges.

Suggested labels: codex

Suggested reviewers: cursor

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 52.63% 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
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely summarizes the main UTC dashboard date-boundary change.
Description check ✅ Passed The description covers the change, motivation, impact, and tests, but it omits the template checklist and frontend screenshots or manual test steps.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

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

@codspeed-hq

codspeed-hq Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 43 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing cursor/dashboard-utc-timezone-1e2d (71e1ce8) with main (e82dd01)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Remove unused normalizeToStartOfDay and share billing-period filter
plus chart today/transform helpers to drop new-code duplication.

Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
@cursor
cursor Bot temporarily deployed to deepsec-pr August 7, 2026 19:58 Inactive
Move billing-cycle chart helpers into useOrgBillingCycleChart and
reuse computeLastDayEvolution / ensureMinDelay so new-code duplication
stays under the Sonar quality gate.

Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
@cursor
cursor Bot temporarily deployed to deepsec-pr August 7, 2026 20:06 Inactive
Extract createTodayLineOptions and hue helpers so Sonar no longer
counts duplicated new-code blocks across dashboard charts.

Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
@cursor
cursor Bot temporarily deployed to deepsec-pr August 7, 2026 20:21 Inactive
Register dashboard charts once and use useCurrentOrgBillingCycleChart
so file preambles no longer share long duplicated new-code blocks.

Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
@cursor
cursor Bot temporarily deployed to deepsec-pr August 7, 2026 20:31 Inactive
Extract dailyChartBaseProps so dashboard charts no longer share long
identical defineProps blocks counted as new duplication.

Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
@cursor
cursor Bot temporarily deployed to deepsec-pr August 7, 2026 20:40 Inactive
Share useDashboardDailyChartCycle and diversify chart file preambles
so identical new-code blocks stay under the duplication threshold.

Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
@cursor
cursor Bot temporarily deployed to deepsec-pr August 7, 2026 20:41 Inactive
@riderx
riderx marked this pull request as ready for review August 7, 2026 21:21
@cursor

cursor Bot commented Aug 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_e12b6610-73ad-498d-abfa-a036f67453e7)

@coderabbitai coderabbitai Bot added the codex label Aug 7, 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: 8

🤖 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 `@src/components/dashboard/BundleUploadsChart.vue`:
- Around line 16-17: Replace the relative imports in
src/components/dashboard/BundleUploadsChart.vue lines 16-17 and
src/components/dashboard/DeploymentStatsChart.vue lines 16-17 with
~/components/dashboard/... alias paths for createChartLegendItems and
ChartLegend. Replace the relative chartTooltip import in
src/components/dashboard/UpdateStatsChart.vue line 17 with
~/services/chartTooltip.

In `@src/components/dashboard/DeploymentStatsCard.vue`:
- Around line 145-146: Update the DeploymentStatsCard date-range query to
compute the next UTC day after last30DaysEnd, format it with formatUtcDateParam,
and filter deployed_at with an exclusive .lt() bound instead of .lte(). Add a
boundary test covering a deployment later on the current UTC day.

In `@src/services/chartTooltip.ts`:
- Around line 64-77: Update the log-navigation handler in UpdateStatsChart.vue
to derive both start and end query bounds from the selected UTC calendar date
using Date.UTC, preserving the day represented by clickContext.date across local
time zones. Remove the unused dayjs import, and add a non-UTC timezone test
covering the generated start and end parameters.

In `@src/services/dashboardChartRegister.ts`:
- Around line 1-30: Update registerDashboardCharts to import the Chart.js Legend
component and include Legend in the Chart.register call, ensuring
UpdateStatsChart’s enabled plugins.legend renders on direct page loads.

In `@src/services/date.ts`:
- Around line 186-190: Update getChartDateRange to parse date-only billingStart
and billingEnd values with a dedicated UTC range-boundary parser before
normalizeToUtcStartOfDay, while preserving parseDatePreservingUtc for localized
display paths. Add a regression test covering getChartDateRange(true,
'2026-08-07', ...) in a positive-offset timezone and verify the UTC date remains
August 7.
- Around line 159-165: Replace the nested ternary in formatUtcDateParam with an
if-based branch that selects the DATE_ONLY_RE UTC parsing path versus the
regular string parsing path, while preserving the existing Date handling,
invalid-date empty-string return, and ISO date formatting.

In `@src/stores/organization.ts`:
- Around line 360-363: Normalize the UTC start-of-day once before calculating
the billing range, then derive both last30DaysEnd and last30DaysStart from that
single anchor. Preserve the existing offsets so the request consistently covers
the intended 30-day window.

In `@src/utils/chartOptimizations.ts`:
- Around line 27-49: Update filterDailySeriesToBillingPeriod to normalize
billingStart to the UTC start of its day, derive the active billing-cycle start
using calendar-month arithmetic, and calculate currentBillingDay as the UTC
elapsed-day difference from that boundary plus one. Use the normalized
billingStart for date comparisons and billingIndex calculations so cycles
spanning months of different lengths retain every day.
🪄 Autofix

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

Plan: Pro

Run ID: 047a23cf-49a1-4fac-9b4f-33925a2164b4

📥 Commits

Reviewing files that changed from the base of the PR and between 12b8d22 and 7da548b.

📒 Files selected for processing (26)
  • src/components/dashboard/BundleUploadsCard.vue
  • src/components/dashboard/BundleUploadsChart.vue
  • src/components/dashboard/DeploymentStatsCard.vue
  • src/components/dashboard/DeploymentStatsChart.vue
  • src/components/dashboard/DevicesStats.vue
  • src/components/dashboard/LineChartStats.vue
  • src/components/dashboard/UpdateStatsCard.vue
  • src/components/dashboard/UpdateStatsChart.vue
  • src/components/dashboard/Usage.vue
  • src/composables/useBuildChartConfig.ts
  • src/composables/useOrgBillingCycleChart.ts
  • src/services/buildCharts.ts
  • src/services/chartDataService.ts
  • src/services/chartTodayLine.ts
  • src/services/chartTooltip.ts
  • src/services/conversion.ts
  • src/services/dailyChartProps.ts
  • src/services/dashboardChartRegister.ts
  • src/services/date.ts
  • src/services/supabase.ts
  • src/stores/main.ts
  • src/stores/organization.ts
  • src/utils/chartOptimizations.ts
  • src/utils/minDelay.ts
  • tests/dashboard-date-range.unit.test.ts
  • tests/date.unit.test.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Cap-go/capacitor-updater (manual)

Comment on lines 16 to 17
import { createChartLegendItems } from './chartLegend'
import ChartLegend from './ChartLegend.vue'

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.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the frontend alias for all new src/ imports.

  • src/components/dashboard/BundleUploadsChart.vue#L16-L17: Replace ./chartLegend and ./ChartLegend.vue with their ~/components/dashboard/... paths.
  • src/components/dashboard/DeploymentStatsChart.vue#L16-L17: Replace ./chartLegend and ./ChartLegend.vue with their ~/components/dashboard/... paths.
  • src/components/dashboard/UpdateStatsChart.vue#L17-L17: Replace ../../services/chartTooltip with ~/services/chartTooltip.

As per coding guidelines, import using the ~/ alias for the src/ directory in frontend code instead of relative paths.

📍 Affects 3 files
  • src/components/dashboard/BundleUploadsChart.vue#L16-L17 (this comment)
  • src/components/dashboard/DeploymentStatsChart.vue#L16-L17
  • src/components/dashboard/UpdateStatsChart.vue#L17-L17
🤖 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 `@src/components/dashboard/BundleUploadsChart.vue` around lines 16 - 17,
Replace the relative imports in src/components/dashboard/BundleUploadsChart.vue
lines 16-17 and src/components/dashboard/DeploymentStatsChart.vue lines 16-17
with ~/components/dashboard/... alias paths for createChartLegendItems and
ChartLegend. Replace the relative chartTooltip import in
src/components/dashboard/UpdateStatsChart.vue line 17 with
~/services/chartTooltip.

Source: Coding guidelines

Comment thread src/components/dashboard/DeploymentStatsCard.vue Outdated
Comment thread src/services/chartTooltip.ts
Comment thread src/services/dashboardChartRegister.ts
Comment thread src/services/date.ts
Comment thread src/services/date.ts
Comment thread src/stores/organization.ts Outdated
Comment thread src/utils/chartOptimizations.ts Outdated
Comment on lines +27 to +49
export function filterDailySeriesToBillingPeriod(fullData: number[], last30DaysStart: Date, billingStart: Date) {
const currentDate = normalizeToUtcStartOfDay()

let currentBillingDay: number
if (billingStart.getUTCDate() === 1) {
currentBillingDay = currentDate.getUTCDate()
}
else {
const billingStartDay = billingStart.getUTCDate()
const daysInMonth = new Date(Date.UTC(currentDate.getUTCFullYear(), currentDate.getUTCMonth() + 1, 0)).getUTCDate()
currentBillingDay = (currentDate.getUTCDate() - billingStartDay + 1 + daysInMonth) % daysInMonth
if (currentBillingDay === 0)
currentBillingDay = daysInMonth
}

const billingData = Array.from({ length: currentBillingDay }).fill(0) as number[]
for (let i = 0; i < 30; i++) {
const dataDate = addUtcDays(last30DaysStart, i)
if (dataDate >= billingStart && dataDate <= currentDate) {
const billingIndex = Math.floor((dataDate.getTime() - billingStart.getTime()) / (1000 * 60 * 60 * 24))
if (billingIndex >= 0 && billingIndex < currentBillingDay)
billingData[billingIndex] = fullData[i]
}

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.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Calculate the billing-day count from the active cycle boundary.

The day-of-month formula fails when the cycle crosses months with different lengths. For a cycle starting March 30 and a current date of April 1, Lines 36-39 calculate 2. The cycle contains three UTC days. Line 47 then drops the April 1 value at billing index 2.

Derive the active billing-cycle start with calendar-month arithmetic. Then calculate currentBillingDay from the UTC elapsed-day difference plus one. Normalize billingStart in this helper before comparisons.

🤖 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 `@src/utils/chartOptimizations.ts` around lines 27 - 49, Update
filterDailySeriesToBillingPeriod to normalize billingStart to the UTC start of
its day, derive the active billing-cycle start using calendar-month arithmetic,
and calculate currentBillingDay as the UTC elapsed-day difference from that
boundary plus one. Use the normalized billingStart for date comparisons and
billingIndex calculations so cycles spanning months of different lengths retain
every day.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6 issues found across 26 files

Confidence score: 2/5

  • src/components/dashboard/LineChartStats.vue still mixes UTC cycle/today indexing with local getCurrentDayMonth() in accumulateData and the per-app reducer, so day cutoffs can drift by browser timezone and show the wrong cumulative point/day boundary; align all day-bucketing and "today" logic to one timezone basis (preferably UTC) end-to-end.
  • src/services/chartTooltip.ts passes a UTC-midnight date into clickContext, but UpdateStatsChart derives local-day bounds, so clicking a day can open logs for a different date for some users; use the same normalized day reference for tooltip click payload and log-query range construction.
  • src/components/dashboard/LineChartStats.vue appears to extend expired billing ranges to the current day, producing empty trailing labels and marking a synthetic final point as "today"; cap chart range/today-marker logic at subscription_end for expired periods.
  • src/services/buildCharts.ts and dedup changes are lower risk but worth cleanup: removing end-of-day handling changes created_at upper-bound semantics to "now," and src/utils/minDelay.ts is not yet adopted by the duplicated callers; restore explicit end-bound behavior if intended and migrate callers to the shared helper to avoid drift.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/services/chartTooltip.ts">

<violation number="1" location="src/services/chartTooltip.ts:64">
P2: Clicking an update-chart day can open logs for a different day depending on the viewer's timezone. The UTC-midnight `Date` introduced here is passed through `clickContext`, but `UpdateStatsChart` still derives local-day bounds; the click path should preserve the UTC calendar day when constructing the log query (or pass a calendar-day-safe local `Date` separately from the UTC tooltip date).</violation>
</file>

<file name="src/utils/minDelay.ts">

<violation number="1" location="src/utils/minDelay.ts:2">
P3: This helper duplicates the identical `elapsed < 300 → setTimeout` spinner-delay block already inlined in useBuildCardStats.ts, Usage.vue, and UpdateStatsCard.vue. Since the PR's goal is deduplication, migrate those three inline spots to call `ensureMinDelay(startTime)`; otherwise refreshData/usage/update paths keep two parallel implementations of the same logic.</violation>
</file>

<file name="src/components/dashboard/LineChartStats.vue">

<violation number="1" location="src/components/dashboard/LineChartStats.vue:48">
P1: Cumulative usage can still shift its cutoff by the browser timezone: this line switches the chart cycle/today index to UTC, but `accumulateData` and the per-app reducer continue using local `getCurrentDayMonth()`. Reusing the composable’s UTC `transformDailySeries` for both total and per-app data would keep the cumulative today bucket aligned with the UTC marker.</violation>

<violation number="2" location="src/components/dashboard/LineChartStats.vue:48">
P2: Expired billing periods now render empty labels through the current day and mark that synthetic final day as “today” instead of stopping at `subscription_end`. Keeping a past subscription end for the billing range (and using today only when no end exists) would preserve the existing billing-period contract.</violation>
</file>

<file name="src/composables/useBuildChartConfig.ts">

<violation number="1" location="src/composables/useBuildChartConfig.ts:7">
P3: The new `createTodayLineOptions` helper centralizes the today-line styling, but `useBuildChartConfig` still returns the same inline today-line config with the identical hardcoded colors rather than delegating to the helper. This leaves two copies of the today-line look in the codebase, defeating the PR's dedup goal: future styling changes have to be updated in both places. Consider calling `createTodayLineOptions` here too (it already replicates the same `useBillingPeriod`/index guard).</violation>
</file>

<file name="src/services/buildCharts.ts">

<violation number="1" location="src/services/buildCharts.ts:62">
P3: The build chart's created_at upper bound changed behavior: the old `endOfToday.setHours(23,59,59,999)` (end of local day) was removed, so `endISO` is now the instant the window is built (`new Date()`), while `todayStart` moved to UTC midnight. This makes `endISO` time-of-fetch dependent and no longer 'end of today' as the `BuildChartWindow.endISO` comment documents, and it isn't aligned with the UTC day boundary that `todayStart` now uses. Consider restoring the intent by normalizing to the UTC end of day (e.g. `endOfToday.setUTCHours(23,59,59,999)`) or updating the comment/name to reflect that `endISO` is now 'fetch time' rather than end of today.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/utils/chartOptimizations.ts Outdated
Comment thread src/components/dashboard/DeploymentStatsCard.vue Outdated
})
return mapping
})
const { cycleStart, cycleEnd, todayLimit } = useDashboardDailyChartCycle(() => props.useBillingPeriod)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Cumulative usage can still shift its cutoff by the browser timezone: this line switches the chart cycle/today index to UTC, but accumulateData and the per-app reducer continue using local getCurrentDayMonth(). Reusing the composable’s UTC transformDailySeries for both total and per-app data would keep the cumulative today bucket aligned with the UTC marker.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/components/dashboard/LineChartStats.vue, line 48:

<comment>Cumulative usage can still shift its cutoff by the browser timezone: this line switches the chart cycle/today index to UTC, but `accumulateData` and the per-app reducer continue using local `getCurrentDayMonth()`. Reusing the composable’s UTC `transformDailySeries` for both total and per-app data would keep the cumulative today bucket aligned with the UTC marker.</comment>

<file context>
@@ -49,48 +39,29 @@ const props = defineProps({
-  })
-  return mapping
-})
+const { cycleStart, cycleEnd, todayLimit } = useDashboardDailyChartCycle(() => props.useBillingPeriod)
 
-// Click handler for tooltip items - navigates to app detail page
</file context>

Comment thread src/composables/useOrgBillingCycleChart.ts Outdated
Comment thread src/services/date.ts Outdated
Comment thread src/stores/organization.ts Outdated
Comment thread src/services/date.ts Outdated
Comment thread src/utils/minDelay.ts
@@ -0,0 +1,6 @@
/** Ensure at least `minMs` elapsed since `startTime` before continuing (spinner UX). */
export async function ensureMinDelay(startTime: number, minMs = 300) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: This helper duplicates the identical elapsed < 300 → setTimeout spinner-delay block already inlined in useBuildCardStats.ts, Usage.vue, and UpdateStatsCard.vue. Since the PR's goal is deduplication, migrate those three inline spots to call ensureMinDelay(startTime); otherwise refreshData/usage/update paths keep two parallel implementations of the same logic.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/utils/minDelay.ts, line 2:

<comment>This helper duplicates the identical `elapsed < 300 → setTimeout` spinner-delay block already inlined in useBuildCardStats.ts, Usage.vue, and UpdateStatsCard.vue. Since the PR's goal is deduplication, migrate those three inline spots to call `ensureMinDelay(startTime)`; otherwise refreshData/usage/update paths keep two parallel implementations of the same logic.</comment>

<file context>
@@ -0,0 +1,6 @@
+/** Ensure at least `minMs` elapsed since `startTime` before continuing (spinner UX). */
+export async function ensureMinDelay(startTime: number, minMs = 300) {
+  const elapsed = Date.now() - startTime
+  if (elapsed < minMs)
</file context>

import { createLegendConfig, createStackedChartScales } from '~/services/chartConfig'
import { createTooltipConfig } from '~/services/chartTooltip'
import { generateMonthDays } from '~/services/date'
import { generateMonthDays, normalizeToUtcStartOfDay } from '~/services/date'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The new createTodayLineOptions helper centralizes the today-line styling, but useBuildChartConfig still returns the same inline today-line config with the identical hardcoded colors rather than delegating to the helper. This leaves two copies of the today-line look in the codebase, defeating the PR's dedup goal: future styling changes have to be updated in both places. Consider calling createTodayLineOptions here too (it already replicates the same useBillingPeriod/index guard).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/composables/useBuildChartConfig.ts, line 7:

<comment>The new `createTodayLineOptions` helper centralizes the today-line styling, but `useBuildChartConfig` still returns the same inline today-line config with the identical hardcoded colors rather than delegating to the helper. This leaves two copies of the today-line look in the codebase, defeating the PR's dedup goal: future styling changes have to be updated in both places. Consider calling `createTodayLineOptions` here too (it already replicates the same `useBillingPeriod`/index guard).</comment>

<file context>
@@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n'
 import { createLegendConfig, createStackedChartScales } from '~/services/chartConfig'
 import { createTooltipConfig } from '~/services/chartTooltip'
-import { generateMonthDays } from '~/services/date'
+import { generateMonthDays, normalizeToUtcStartOfDay } from '~/services/date'
 import { useOrganizationStore } from '~/stores/organization'
 
</file context>

export function getBuildChartWindow(useBillingPeriod: boolean, subscriptionStart?: string | null): BuildChartWindow {
const todayStart = new Date()
todayStart.setHours(0, 0, 0, 0)
const todayStart = normalizeToUtcStartOfDay()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The build chart's created_at upper bound changed behavior: the old endOfToday.setHours(23,59,59,999) (end of local day) was removed, so endISO is now the instant the window is built (new Date()), while todayStart moved to UTC midnight. This makes endISO time-of-fetch dependent and no longer 'end of today' as the BuildChartWindow.endISO comment documents, and it isn't aligned with the UTC day boundary that todayStart now uses. Consider restoring the intent by normalizing to the UTC end of day (e.g. endOfToday.setUTCHours(23,59,59,999)) or updating the comment/name to reflect that endISO is now 'fetch time' rather than end of today.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/services/buildCharts.ts, line 62:

<comment>The build chart's created_at upper bound changed behavior: the old `endOfToday.setHours(23,59,59,999)` (end of local day) was removed, so `endISO` is now the instant the window is built (`new Date()`), while `todayStart` moved to UTC midnight. This makes `endISO` time-of-fetch dependent and no longer 'end of today' as the `BuildChartWindow.endISO` comment documents, and it isn't aligned with the UTC day boundary that `todayStart` now uses. Consider restoring the intent by normalizing to the UTC end of day (e.g. `endOfToday.setUTCHours(23,59,59,999)`) or updating the comment/name to reflect that `endISO` is now 'fetch time' rather than end of today.</comment>

<file context>
@@ -57,25 +59,22 @@ export interface BuildChartWindow {
 export function getBuildChartWindow(useBillingPeriod: boolean, subscriptionStart?: string | null): BuildChartWindow {
-  const todayStart = new Date()
-  todayStart.setHours(0, 0, 0, 0)
+  const todayStart = normalizeToUtcStartOfDay()
   const endOfToday = new Date()
-  endOfToday.setHours(23, 59, 59, 999)
</file context>

Include today's deployments with an exclusive UTC upper bound, keep
billing-cycle math on elapsed UTC days, and preserve UTC calendar days
for chart click log navigation and date-only billing boundaries.

Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
@cursor
cursor Bot temporarily deployed to deepsec-pr August 8, 2026 07:15 Inactive
@cursor

cursor Bot commented Aug 8, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_f38c1f75-ed55-4c5f-85cd-fefb8b73bbf8)

@sonarqubecloud

sonarqubecloud Bot commented Aug 8, 2026

Copy link
Copy Markdown

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

🤖 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 `@src/components/dashboard/BundleUploadsCard.vue`:
- Around line 181-186: Update the raw-row caching used by the series-window
query in BundleUploadsCard.vue (lines 181-186) and DeploymentStatsCard.vue
(lines 200-201) so each cache entry is keyed by organization plus the resolved
UTC start and exclusive-end bounds derived from useBillingPeriod, seriesStart,
and exclusiveEnd; ensure calculateStats(false) never reuses rows from a
different window.

In `@src/components/dashboard/LineChartStats.vue`:
- Line 14: In LineChartStats, replace both unresolved getDaysInCurrentMonth
calls with the imported getDaysInCurrentUtcMonth helper, preserving the existing
call arguments and behavior.

In `@src/services/date.ts`:
- Around line 206-213: Update formatUtcDateParam to validate date-only strings
against a real calendar date before constructing the UTC Date, so values such as
2026-02-30 follow the existing empty invalid-date fallback instead of being
normalized. Add a regression test covering 2026-02-30 and preserve valid
date-only formatting.

In `@tests/date.unit.test.ts`:
- Around line 143-145: Update the zone-less ISO regression test for
formatUtcDateParam to run under TZ=America/Los_Angeles, use the
timezone-sensitive input 2026-08-07T23:30:00, and continue expecting 2026-08-07.
Configure the test command or runner environment rather than mutating
process.env.TZ within the suite.
🪄 Autofix

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

Plan: Pro

Run ID: eeca0302-617a-4478-8383-3a1b242e6532

📥 Commits

Reviewing files that changed from the base of the PR and between 7da548b and 71e1ce8.

📒 Files selected for processing (15)
  • src/components/dashboard/BundleUploadsCard.vue
  • src/components/dashboard/BundleUploadsChart.vue
  • src/components/dashboard/DeploymentStatsCard.vue
  • src/components/dashboard/DeploymentStatsChart.vue
  • src/components/dashboard/LineChartStats.vue
  • src/components/dashboard/UpdateStatsChart.vue
  • src/components/dashboard/Usage.vue
  • src/composables/useOrgBillingCycleChart.ts
  • src/services/dailyChartProps.ts
  • src/services/dashboardChartRegister.ts
  • src/services/date.ts
  • src/stores/organization.ts
  • src/utils/chartOptimizations.ts
  • tests/chart-optimizations.unit.test.ts
  • tests/date.unit.test.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Cap-go/capacitor-updater (manual)

Comment on lines +181 to +186
// Fetch series window (billing cycle or last 30 UTC days)
const query = useSupabase()
.from('app_versions')
.select('created_at, app_id, deleted, r2_path, external_url, user_id')
.gte('created_at', last30DaysStart.toISOString())
.lte('created_at', last30DaysEnd.toISOString())
.lt('created_at', last30DaysEnd.toISOString())

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Key cached raw data by the resolved series window.

Both components cache raw rows only by organization, but the query now depends on useBillingPeriod, seriesStart, and exclusiveEnd. When a user switches views, calculateStats(false) can reuse rows from a different window. This produces missing billing-cycle history or stale out-of-range data.

  • src/components/dashboard/BundleUploadsCard.vue#L181-L186: Include the resolved UTC bounds in the cache key, or cache a verified superset before filtering.
  • src/components/dashboard/DeploymentStatsCard.vue#L200-L201: Include the resolved UTC bounds in the cache key, or cache a verified superset before filtering.
📍 Affects 2 files
  • src/components/dashboard/BundleUploadsCard.vue#L181-L186 (this comment)
  • src/components/dashboard/DeploymentStatsCard.vue#L200-L201
🤖 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 `@src/components/dashboard/BundleUploadsCard.vue` around lines 181 - 186,
Update the raw-row caching used by the series-window query in
BundleUploadsCard.vue (lines 181-186) and DeploymentStatsCard.vue (lines
200-201) so each cache entry is keyed by organization plus the resolved UTC
start and exclusive-end bounds derived from useBillingPeriod, seriesStart, and
exclusiveEnd; ensure calculateStats(false) never reuses rows from a different
window.

import { useOrganizationStore } from '~/stores/organization'
import { createTodayLineOptions, getSafeChartHue } from '~/services/chartTodayLine'
import { registerDashboardCharts } from '~/services/dashboardChartRegister'
import { generateMonthDays, getCurrentDayMonth, getDaysInCurrentUtcMonth } from '~/services/date'

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.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline src/components/dashboard/LineChartStats.vue --items all
rg -n -C 2 'getDaysInCurrent(?:Utc)?Month\s*\(' src/components/dashboard/LineChartStats.vue

Repository: Cap-go/capgo.app

Length of output: 977


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- LineChartStats.vue ---'
sed -n '1,170p' src/components/dashboard/LineChartStats.vue

printf '%s\n' '--- date-service declarations and usages ---'
rg -n -C 3 'getDaysInCurrent(?:Utc)?Month|export .*getCurrentDayMonth|generateMonthDays' src

Repository: Cap-go/capgo.app

Length of output: 18274


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- date helpers ---'
sed -n '105,148p' src/services/date.ts

printf '%s\n' '--- focused diff ---'
git diff -- src/components/dashboard/LineChartStats.vue src/services/date.ts

printf '%s\n' '--- exact identifier/import check ---'
python3 - <<'PY'
from pathlib import Path
import re

path = Path('src/components/dashboard/LineChartStats.vue')
text = path.read_text()
imports = set(re.findall(r'\b(?:generateMonthDays|getCurrentDayMonth|getDaysInCurrentUtcMonth|getDaysInCurrentMonth)\b', text.split('</script>', 1)[0]))
calls = re.findall(r'\b(getDaysInCurrent(?:Utc)?Month)\s*\(', text)
print('identifiers in script:', sorted(imports))
print('month-length calls:', calls)
print('getDaysInCurrentMonth imported:', bool(re.search(r'import\s*\{[^}]*\bgetDaysInCurrentMonth\b[^}]*\}\s*from', text, re.S)))
PY

Repository: Cap-go/capgo.app

Length of output: 1921


Replace the unresolved month-length helper.

getDaysInCurrentMonth is not imported. Replace both calls with the already imported getDaysInCurrentUtcMonth so the component compiles.

🤖 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 `@src/components/dashboard/LineChartStats.vue` at line 14, In LineChartStats,
replace both unresolved getDaysInCurrentMonth calls with the imported
getDaysInCurrentUtcMonth helper, preserving the existing call arguments and
behavior.

Comment thread src/services/date.ts
Comment on lines +206 to +213
export function formatUtcDateParam(date: Date | string = new Date()) {
let parsed: Date
if (typeof date === 'string' && DATE_ONLY_RE.test(date))
parsed = new Date(`${date}T00:00:00.000Z`)
else if (typeof date === 'string' && ZONELESS_ISO_DATETIME_RE.test(date))
parsed = new Date(`${date}Z`)
else
parsed = new Date(date)

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject calendar-invalid date-only parameters.

new Date('2026-02-30T00:00:00.000Z') normalizes to March 2. This function returns 2026-03-02 instead of its empty invalid-date fallback. Validate date-only fields before formatting. Add a regression test for 2026-02-30.

Proposed fix
 export function formatUtcDateParam(date: Date | string = new Date()) {
+  if (typeof date === 'string' && DATE_ONLY_RE.test(date)) {
+    const parsed = parseUtcRangeBoundary(date)
+    return parsed ? parsed.toISOString().slice(0, 10) : ''
+  }
+
   let parsed: Date
-  if (typeof date === 'string' && DATE_ONLY_RE.test(date))
-    parsed = new Date(`${date}T00:00:00.000Z`)
-  else if (typeof date === 'string' && ZONELESS_ISO_DATETIME_RE.test(date))
+  if (typeof date === 'string' && ZONELESS_ISO_DATETIME_RE.test(date))
     parsed = new Date(`${date}Z`)
   else
     parsed = new Date(date)
📝 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
export function formatUtcDateParam(date: Date | string = new Date()) {
let parsed: Date
if (typeof date === 'string' && DATE_ONLY_RE.test(date))
parsed = new Date(`${date}T00:00:00.000Z`)
else if (typeof date === 'string' && ZONELESS_ISO_DATETIME_RE.test(date))
parsed = new Date(`${date}Z`)
else
parsed = new Date(date)
export function formatUtcDateParam(date: Date | string = new Date()) {
if (typeof date === 'string' && DATE_ONLY_RE.test(date)) {
const parsed = parseUtcRangeBoundary(date)
return parsed ? parsed.toISOString().slice(0, 10) : ''
}
let parsed: Date
if (typeof date === 'string' && ZONELESS_ISO_DATETIME_RE.test(date))
parsed = new Date(`${date}Z`)
else
parsed = new Date(date)
🤖 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 `@src/services/date.ts` around lines 206 - 213, Update formatUtcDateParam to
validate date-only strings against a real calendar date before constructing the
UTC Date, so values such as 2026-02-30 follow the existing empty invalid-date
fallback instead of being normalized. Add a regression test covering 2026-02-30
and preserve valid date-only formatting.

Comment thread tests/date.unit.test.ts
Comment on lines +143 to +145
it('treats zone-less ISO datetimes as UTC in formatUtcDateParam', () => {
expect(formatUtcDateParam('2026-08-07T15:30:00')).toBe('2026-08-07')
})

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

fd -HI -t f '^(package\.json|vitest\.config\..*|vite\.config\..*)$' . \
  -x sh -c 'printf "\n### %s\n" "$1"; rg -n -C 3 "vitest|test|TZ" "$1" || true' sh {}

Repository: Cap-go/capgo.app

Length of output: 28473


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '### tests/date.unit.test.ts'
sed -n '1,190p' tests/date.unit.test.ts

printf '%s\n' '### date helpers and usages'
rg -n -C 5 "formatUtcDateParam|formatUtc|DateParam" src tests --glob '*.{ts,tsx,vue}' || true

printf '%s\n' '### timezone-sensitive runtime probe'
TZ=America/Los_Angeles node - <<'JS'
const input = '2026-08-07T23:30:00'
const local = new Date(input)
const utc = new Date(`${input}Z`)
const day = date => date.toISOString().slice(0, 10)
console.log(JSON.stringify({
  timezone: process.env.TZ,
  localISO: local.toISOString(),
  utcISO: utc.toISOString(),
  localDay: day(local),
  utcDay: day(utc),
}))
JS

Repository: Cap-go/capgo.app

Length of output: 24651


Make the zone-less ISO regression test timezone-sensitive.

The current 15:30:00 input does not distinguish UTC parsing from local parsing. Run an equivalent assertion with TZ=America/Los_Angeles and 2026-08-07T23:30:00, expecting 2026-08-07. Do not mutate process.env.TZ inside this suite.

🤖 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 `@tests/date.unit.test.ts` around lines 143 - 145, Update the zone-less ISO
regression test for formatUtcDateParam to run under TZ=America/Los_Angeles, use
the timezone-sensitive input 2026-08-07T23:30:00, and continue expecting
2026-08-07. Configure the test command or runner environment rather than
mutating process.env.TZ within the suite.

Source: Coding guidelines

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 issues found across 15 files (changes from recent commits).

Confidence score: 2/5

  • src/components/dashboard/LineChartStats.vue still calls getDaysInCurrentMonth without importing it, which can break the dashboard build immediately and block release validation—restore the import or finish migrating those call sites to the UTC helper.
  • src/components/dashboard/DeploymentStatsCard.vue, src/components/dashboard/BundleUploadsCard.vue, and src/utils/chartOptimizations.ts share a cache-window mismatch: rows fetched for one date range are reused for another, which can drop early-cycle days or shift daily counts when users switch billing modes—key/invalidate cache entries by start/end window (and app scope) before reindexing.
  • src/utils/chartOptimizations.ts reindexes cached rows with a new seriesStart after mode switches, so edge days can be added/dropped and per-day totals misaligned in charts—persist fetched window metadata and force refetch when the active range changes.
  • src/components/dashboard/UpdateStatsChart.vue sends an end timestamp that the backend treats as exclusive, so log drill-downs can miss events at the end of the selected UTC day—send the next midnight as end to include the full day reliably.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/components/dashboard/LineChartStats.vue">

<violation number="1" location="src/components/dashboard/LineChartStats.vue:14">
P1: Dashboard build fails because `getDaysInCurrentMonth` is still used but no longer imported. Keep that import (or migrate both remaining call sites) with the UTC helper.</violation>
</file>

<file name="src/components/dashboard/DeploymentStatsCard.vue">

<violation number="1" location="src/components/dashboard/DeploymentStatsCard.vue:140">
P1: Switching from the default 30-day view to a billing cycle longer than 30 days omits early-cycle deployments because the per-org cache is reused despite its narrower query window. Store/query cache coverage with the range or force a refetch when billing mode needs an earlier `seriesStart`.</violation>
</file>

<file name="src/components/dashboard/BundleUploadsCard.vue">

<violation number="1" location="src/components/dashboard/BundleUploadsCard.vue:126">
P1: Switching from billing-cycle to 30-day mode can omit earlier days because cache entries are keyed only by org although this new window changes the query range. Key or invalidate cached rows by series window (and app set), or force a refetch when `useBillingPeriod` changes.</violation>
</file>

<file name="src/components/dashboard/UpdateStatsChart.vue">

<violation number="1" location="src/components/dashboard/UpdateStatsChart.vue:89">
P2: Logs clicked from a chart can omit events in the selected UTC day’s final millisecond because the backend treats `rangeEnd` as exclusive. Send the following midnight as `end` instead.</violation>
</file>

<file name="src/utils/chartOptimizations.ts">

<violation number="1" location="src/utils/chartOptimizations.ts:52">
P1: Switching billing mode reuses rows fetched for the prior window but reindexes them using this new `seriesStart`, shifting daily counts (and dropping/adding edge days). Cache the fetched window with its start/end or force a refetch when `useBillingPeriod` changes.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

import { useOrganizationStore } from '~/stores/organization'
import { createTodayLineOptions, getSafeChartHue } from '~/services/chartTodayLine'
import { registerDashboardCharts } from '~/services/dashboardChartRegister'
import { generateMonthDays, getCurrentDayMonth, getDaysInCurrentUtcMonth } from '~/services/date'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Dashboard build fails because getDaysInCurrentMonth is still used but no longer imported. Keep that import (or migrate both remaining call sites) with the UTC helper.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/components/dashboard/LineChartStats.vue, line 14:

<comment>Dashboard build fails because `getDaysInCurrentMonth` is still used but no longer imported. Keep that import (or migrate both remaining call sites) with the UTC helper.</comment>

<file context>
@@ -11,7 +11,7 @@ import { useDashboardDailyChartCycle } from '~/composables/useOrgBillingCycleCha
 import { createTodayLineOptions, getSafeChartHue } from '~/services/chartTodayLine'
 import { registerDashboardCharts } from '~/services/dashboardChartRegister'
-import { generateMonthDays, getCurrentDayMonth, getDaysInCurrentMonth } from '~/services/date'
+import { generateMonthDays, getCurrentDayMonth, getDaysInCurrentUtcMonth } from '~/services/date'
 import { inlineAnnotationPlugin } from '../../services/chartAnnotations'
 import { createTooltipConfig, todayLinePlugin, verticalLinePlugin } from '../../services/chartTooltip'
</file context>
Suggested change
import { generateMonthDays, getCurrentDayMonth, getDaysInCurrentUtcMonth } from '~/services/date'
import { generateMonthDays, getCurrentDayMonth, getDaysInCurrentMonth, getDaysInCurrentUtcMonth } from '~/services/date'

const billingStart = new Date(targetOrganization.subscription_start ?? new Date())
billingStart.setHours(0, 0, 0, 0)
const billingStart = normalizeToUtcStartOfDay(new Date(targetOrganization.subscription_start ?? new Date()))
const { seriesStart: last30DaysStart, exclusiveEnd, dayCount } = resolveDashboardDailySeriesWindow(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Switching from the default 30-day view to a billing cycle longer than 30 days omits early-cycle deployments because the per-org cache is reused despite its narrower query window. Store/query cache coverage with the range or force a refetch when billing mode needs an earlier seriesStart.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/components/dashboard/DeploymentStatsCard.vue, line 140:

<comment>Switching from the default 30-day view to a billing cycle longer than 30 days omits early-cycle deployments because the per-org cache is reused despite its narrower query window. Store/query cache coverage with the range or force a refetch when billing mode needs an earlier `seriesStart`.</comment>

<file context>
@@ -135,15 +135,16 @@ async function calculateStats(forceRefetch = false) {
-
     // Get billing period dates for filtering
     const billingStart = normalizeToUtcStartOfDay(new Date(targetOrganization.subscription_start ?? new Date()))
+    const { seriesStart: last30DaysStart, exclusiveEnd, dayCount } = resolveDashboardDailySeriesWindow(
+      props.useBillingPeriod,
+      billingStart,
</file context>

billingStart.setHours(0, 0, 0, 0)
const billingStart = normalizeToUtcStartOfDay(new Date(organizationStore.currentOrganization?.subscription_start ?? new Date()))
const { seriesStart: last30DaysStart, exclusiveEnd: last30DaysEnd, dayCount } = resolveDashboardDailySeriesWindow(
props.useBillingPeriod,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Switching from billing-cycle to 30-day mode can omit earlier days because cache entries are keyed only by org although this new window changes the query range. Key or invalidate cached rows by series window (and app set), or force a refetch when useBillingPeriod changes.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/components/dashboard/BundleUploadsCard.vue, line 126:

<comment>Switching from billing-cycle to 30-day mode can omit earlier days because cache entries are keyed only by org although this new window changes the query range. Key or invalidate cached rows by series window (and app set), or force a refetch when `useBillingPeriod` changes.</comment>

<file context>
@@ -120,12 +120,12 @@ async function calculateStats(forceRefetch = false) {
     // Get billing period dates for filtering
     const billingStart = normalizeToUtcStartOfDay(new Date(organizationStore.currentOrganization?.subscription_start ?? new Date()))
+    const { seriesStart: last30DaysStart, exclusiveEnd: last30DaysEnd, dayCount } = resolveDashboardDailySeriesWindow(
+      props.useBillingPeriod,
+      billingStart,
+    )
</file context>

if (useBillingPeriod && !Number.isNaN(cycleStart.getTime())) {
const elapsedDays = Math.floor((todayUtc.getTime() - cycleStart.getTime()) / DAY_MS)
if (elapsedDays >= 0 && elapsedDays <= 366)
seriesStart = cycleStart

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Switching billing mode reuses rows fetched for the prior window but reindexes them using this new seriesStart, shifting daily counts (and dropping/adding edge days). Cache the fetched window with its start/end or force a refetch when useBillingPeriod changes.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/utils/chartOptimizations.ts, line 52:

<comment>Switching billing mode reuses rows fetched for the prior window but reindexes them using this new `seriesStart`, shifting daily counts (and dropping/adding edge days). Cache the fetched window with its start/end or force a refetch when `useBillingPeriod` changes.</comment>

<file context>
@@ -20,33 +22,62 @@ export function incrementArrayValue(arr: (number | undefined)[], index: number,
+  if (useBillingPeriod && !Number.isNaN(cycleStart.getTime())) {
+    const elapsedDays = Math.floor((todayUtc.getTime() - cycleStart.getTime()) / DAY_MS)
+    if (elapsedDays >= 0 && elapsedDays <= 366)
+      seriesStart = cycleStart
   }
 
</file context>

params.set('end', endOfDay.toISOString())
const { start, end } = getUtcDayBounds(clickContext.date)
params.set('start', start.toISOString())
params.set('end', end.toISOString())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Logs clicked from a chart can omit events in the selected UTC day’s final millisecond because the backend treats rangeEnd as exclusive. Send the following midnight as end instead.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/components/dashboard/UpdateStatsChart.vue, line 89:

<comment>Logs clicked from a chart can omit events in the selected UTC day’s final millisecond because the backend treats `rangeEnd` as exclusive. Send the following midnight as `end` instead.</comment>

<file context>
@@ -83,12 +82,11 @@ const tooltipClickHandler = computed<TooltipClickHandler | undefined>(() => {
-        params.set('end', endOfDay.toISOString())
+        const { start, end } = getUtcDayBounds(clickContext.date)
+        params.set('start', start.toISOString())
+        params.set('end', end.toISOString())
       }
 
</file context>
Suggested change
params.set('end', end.toISOString())
params.set('end', new Date(end.getTime() + 1).toISOString())

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants