Skip to content

fix: prevent inconsistent demo data display across dashboard charts#1402

Merged
riderx merged 12 commits into
mainfrom
riderx/fix-dashboard-fake-data
Jan 8, 2026
Merged

fix: prevent inconsistent demo data display across dashboard charts#1402
riderx merged 12 commits into
mainfrom
riderx/fix-dashboard-fake-data

Conversation

@riderx
Copy link
Copy Markdown
Member

@riderx riderx commented Jan 8, 2026

Summary

  • Add hasAnyRealData prop to coordinate demo mode across sibling charts - if any chart has real data, none will show demo data (prevents mixed real/fake data display)
  • Create AppNotFoundModal component for consistent UX when app doesn't exist
  • Show blurred demo view with modal when navigating to non-existent app (instead of plain error message)

Test plan

  • Verify that if MAU has real data but Storage/Bandwidth don't, Storage/Bandwidth show 0 instead of demo data
  • Verify that when no charts have real data, all charts show demo data consistently
  • Verify that navigating to /app/non-existent-app-id shows blurred demo charts with "App Not Found" modal
  • Verify that payment failed state still works correctly with blurred demo view

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • "App Not Found" modal with localized messaging and a button to return to apps.
    • Force-demo prop propagation to dashboard widgets to enable consistent demo data.
  • Bug Fixes

    • Improved error handling and overlay/blur behavior when loading app data fails.
    • Consistent day-count/date-range normalization to avoid time-of-day variation.
  • Refactor

    • Centralized demo-mode gating across dashboard charts and cards.
  • UX

    • Improved chart scaling, tooltip positioning, and axis/grid visuals.

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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 8, 2026

Warning

Rate limit exceeded

@riderx has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 12 minutes and 50 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 264b60b and 8df26a0.

📒 Files selected for processing (3)
  • src/components/dashboard/Usage.vue
  • src/services/chartTooltip.ts
  • src/stores/organization.ts
📝 Walkthrough

Walkthrough

Adds a new AppNotFoundModal component and TypeScript declaration; refactors dashboard demo-mode gating to use forceDemo + dashboard apps/store state and introduces effective demo-vs-real data selectors; propagates forceDemo and a new shouldBlurContent (driven by appNotFound) from the app detail page to dashboard components.

Changes

Cohort / File(s) Summary
New Modal Declaration
src/components.d.ts
Adds AppNotFoundModal to GlobalComponents and TSX globals mapping to ./components/AppNotFoundModal.vue.
New Modal Component
src/components/AppNotFoundModal.vue
New Vue SFC (script-setup TS) rendering a centered modal with i18n text, alert icon, and a button exposing goToApps() which calls router.push('/app').
Dashboard demo-mode & effective-data refactor
src/components/dashboard/...
src/components/dashboard/BundleUploadsCard.vue, .../DeploymentStatsCard.vue, .../UpdateStatsCard.vue, .../UsageCard.vue, .../DevicesStats.vue, .../LineChartStats.vue
Replace prior hasRealData logic with unified isDemoMode that: respects forceDemo, disables demo if dashboard has apps, otherwise enables demo when store is loaded. Introduce effective* selectors (data, by-app, names, totals, evolution), compute dataMax and apply to chart scales, update hasData/hasChartData to include demo-mode. Add forceDemo prop on UsageCard and wire is-demo-data/effective data into charts.
Prop forwarding / Usage layout
src/components/dashboard/Usage.vue, src/components/dashboard/UsageCard.vue
Forward :force-demo="forceDemo" to child cards; add forceDemo prop to UsageCard; select demo generators via getDataGenerator and generateConsistentDemoData; compute effective data based on isDemoMode.
App detail page: not-found & blur
src/pages/app/[package].vue
Add appNotFound state and shouldBlurContent computed; set appNotFound on load errors and clear on success; replace inline not-found UI with AppNotFoundModal overlay; use shouldBlurContent to blur main content and propagate blur/force-demo to children.
Chart tooltip & config changes
src/services/chartTooltip.ts, src/services/chartConfig.ts
createCustomTooltip signature expanded (adds hasMultipleDatasets, dateStartOrUseBillingPeriod, clickHandler); tooltip title derives from actual label when possible; positioning reworked to canvas-relative transform/clamping. AxisConfig adds optional grid.drawBorder, grid.borderColor, and border config; axis border display set to false to hide axis border lines.
Small utilities and normalization
src/services/conversion.ts
Normalizes dates to midnight in getDaysBetweenDates to remove time-of-day variance.

Sequence Diagram

sequenceDiagram
    actor User
    participant Page as App Detail Page
    participant Store as DashboardAppsStore
    participant Modal as AppNotFoundModal
    participant Router as Vue Router

    User->>Page: Navigate to app URL
    Page->>Store: ensure apps loaded / check isLoaded & apps.length
    Page->>Page: loadAppInfo()
    alt fetch succeeds
        Page->>User: Render app content (no blur)
    else fetch fails
        Page->>Page: set appNotFound = true, clear app
        Page->>User: Blur content and show AppNotFoundModal
        User->>Modal: Click "Back to Apps"
        Modal->>Router: router.push('/app') via goToApps()
        Router->>User: Navigate to apps list
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 A modal pops up where the app was found no more,
I nudge you gently back to the app-list door.
Demo flags now heed a clearer, steady hand,
Charts will show demo only when the store demands.
Click back, hop along—adventures on the dashboard floor.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately reflects the main objective: preventing inconsistent demo data display across dashboard charts.
Description check ✅ Passed The PR description includes a summary and test plan aligned with the template, though the test plan items are unchecked and documentation/code style checklist items are missing.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.

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


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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b9c53a96e9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 664 to 668
:title="t('active_users_by_version')"
:is-loading="isLoading"
:has-data="hasData"
:is-demo-data="isDemoMode"
>
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 Badge Guard demo rendering when chart data is null

When the devices chart fails to load (e.g., API error or empty response), rawChartData stays null, which makes isDemoMode true once loading finishes, so ChartCard renders the slot as “demo data.” However processedChartData is still null, and the <Line :data="processedChartData!"> call now executes under that demo branch, which can throw or render a broken chart while showing the demo overlay. Consider only passing is-demo-data when demo data was actually loaded (e.g., forceDemo) or guarding the slot render on processedChartData being non-null.

Useful? React with 👍 / 👎.

- Remove auto-demo mode that was incorrectly showing fake data when real data exists
- Demo data now ONLY shows when forceDemo is true (payment failed modal state)
- Users with apps will see their real data (even if empty) instead of fake demo data
- Show blurred demo view with modal when navigating to non-existent app

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@riderx riderx force-pushed the riderx/fix-dashboard-fake-data branch from b9c53a9 to 3135ff9 Compare January 8, 2026 22:07
Demo data now only shows when:
1. forceDemo is true (payment failed), OR
2. User has no apps (dashboardAppsStore.apps.length === 0)

If user has apps, ALWAYS show real data (even if empty).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/dashboard/UpdateStatsCard.vue (1)

351-351: Remove erroneous standalone "text" class.

The class list contains a duplicate "text" token between dark:text-white and text-slate-600. Tailwind has no standalone text utility—it should be removed.

🧹 Proposed fix
-        <h2 class="flex-1 min-w-0 text-2xl font-semibold leading-tight dark:text-white text text-slate-600">
+        <h2 class="flex-1 min-w-0 text-2xl font-semibold leading-tight dark:text-white text-slate-600">
🧹 Nitpick comments (1)
src/components/dashboard/DeploymentStatsCard.vue (1)

107-117: Consider extracting isDemoMode to a shared composable.

The identical isDemoMode logic is duplicated across DeploymentStatsCard.vue, DevicesStats.vue, and UsageCard.vue. Extracting this to a composable like useDemoMode(forceDemo) would improve maintainability and ensure consistency.

♻️ Example composable structure

Create src/composables/useDemoMode.ts:

import { computed } from 'vue'
import { useDashboardAppsStore } from '~/stores/dashboardApps'

export function useDemoMode(forceDemo: Ref<boolean> | boolean) {
  const dashboardAppsStore = useDashboardAppsStore()
  
  return computed(() => {
    const force = unref(forceDemo)
    if (force)
      return true
    if (dashboardAppsStore.apps.length > 0)
      return false
    return dashboardAppsStore.isLoaded
  })
}

Then in components:

-const isDemoMode = computed(() => {
-  if (props.forceDemo)
-    return true
-  if (dashboardAppsStore.apps.length > 0)
-    return false
-  return dashboardAppsStore.isLoaded
-})
+const isDemoMode = useDemoMode(() => props.forceDemo)
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3135ff9 and e196b93.

📒 Files selected for processing (5)
  • src/components/dashboard/BundleUploadsCard.vue
  • src/components/dashboard/DeploymentStatsCard.vue
  • src/components/dashboard/DevicesStats.vue
  • src/components/dashboard/UpdateStatsCard.vue
  • src/components/dashboard/UsageCard.vue
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/dashboard/BundleUploadsCard.vue
🧰 Additional context used
📓 Path-based instructions (10)
src/**/*.vue

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.vue: Use Vue 3 with Composition API and <script setup> syntax for frontend components
Style components using TailwindCSS with DaisyUI components

src/**/*.vue: Use Vue 3 <script setup> syntax exclusively for all Vue component scripts
Use Tailwind utility classes for layout and spacing in Vue components
Use DaisyUI components (d-btn, d-input, d-card) for interactive elements in Vue components
Use Konsta components ONLY for safe area helpers (top/bottom insets) in Vue components; avoid other uses
Use useRoute() from vue-router to access route parameters and useRouter() for programmatic navigation in Vue components

Files:

  • src/components/dashboard/DeploymentStatsCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/DevicesStats.vue
**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

Use single quotes and no semicolons per @antfu/eslint-config

Files:

  • src/components/dashboard/DeploymentStatsCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/DevicesStats.vue
src/components/**/*.vue

📄 CodeRabbit inference engine (CLAUDE.md)

Reusable Vue components should be organized in src/components/ directory

Files:

  • src/components/dashboard/DeploymentStatsCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/DevicesStats.vue
{capacitor.config.{ts,js},src/**/*.{ts,tsx,vue}}

📄 CodeRabbit inference engine (CLAUDE.md)

Mobile apps should use Capacitor with app ID ee.forgr.capacitor_go for native mobile functionality

Files:

  • src/components/dashboard/DeploymentStatsCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/DevicesStats.vue
src/**/*.{ts,tsx,vue,js}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use ~/ alias for imports from src/ directory in frontend TypeScript and Vue components

Files:

  • src/components/dashboard/DeploymentStatsCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/DevicesStats.vue
src/**/*.{vue,ts,js}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Frontend ESLint must pass before commit; run bun lint:fix to auto-fix issues in frontend files

Files:

  • src/components/dashboard/DeploymentStatsCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/DevicesStats.vue
**/{src,app}/{components,views,pages}/**/*.{vue,css,scss}

📄 CodeRabbit inference engine (AGENTS.md)

The web client is built with Vue.js and Tailwind CSS; lean on utility classes and composition-friendly patterns rather than bespoke CSS.

Files:

  • src/components/dashboard/DeploymentStatsCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/DevicesStats.vue
**/{src,app}/{components,views,pages}/**/*.vue

📄 CodeRabbit inference engine (AGENTS.md)

Use DaisyUI (d- prefixed classes) for buttons, inputs, and other interactive primitives to keep behavior and spacing consistent.

Files:

  • src/components/dashboard/DeploymentStatsCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/DevicesStats.vue
**/{src,app}/**/*.{vue,ts,tsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

Konsta components are reserved for the safe area helpers. Avoid importing konsta anywhere else in the app.

Files:

  • src/components/dashboard/DeploymentStatsCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/DevicesStats.vue
**/{src,app}/**/*.{css,scss,vue}

📄 CodeRabbit inference engine (AGENTS.md)

Capgo's design uses deep slate bases with the 'Extract' azure highlight (--color-azure-500: #119eff) and soft radii; mirror the palette from src/styles/style.css (e.g., --color-primary-500: #515271) when introducing new UI.

Files:

  • src/components/dashboard/DeploymentStatsCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/DevicesStats.vue
🧠 Learnings (1)
📚 Learning: 2025-12-23T01:19:04.593Z
Learnt from: riderx
Repo: Cap-go/capgo PR: 1297
File: src/components/dashboard/DeploymentBanner.vue:77-79
Timestamp: 2025-12-23T01:19:04.593Z
Learning: In the Cap-go codebase, ensure that app permission checks never include the role 'owner'. App-level permissions should be based on the user_min_right enum with values: read, upload, write, admin, super_admin (and NOT owner). This pattern applies across Vue components that perform permission checks; if you find a check referencing 'owner' for app-level access, replace it with the appropriate user_min_right value and keep organization-level owner handling in organization.ts.

Applied to files:

  • src/components/dashboard/DeploymentStatsCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/DevicesStats.vue
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Run tests
🔇 Additional comments (12)
src/components/dashboard/UpdateStatsCard.vue (3)

33-36: LGTM! New forceDemo prop enables parent-controlled demo mode.

The prop definition is correct and aligns with the PR objectives to allow Usage.vue to control demo mode for payment failures and non-existent apps.


85-95: Demo mode logic correctly implements the new gating strategy.

The logic properly handles:

  • Force demo when payment fails or app doesn't exist (forceDemo=true)
  • Never show demo when user has apps (even if data is empty)
  • Show demo only when user truly has no apps

During initial load when the store is not yet loaded, isDemoMode returns false (showing empty real data with the loading spinner), then updates once the store loads. This provides better UX than flashing demo data during load.


97-113: LGTM! Effective data computed properties cleanly separate demo and real data.

The computed properties consistently route display values through the isDemoMode gate, ensuring that either demo or real data is shown, never mixed. This directly addresses the PR objective to prevent inconsistent demo data display.

src/components/dashboard/DeploymentStatsCard.vue (2)

107-117: LGTM! Demo mode logic is clear and well-structured.

The new isDemoMode computation cleanly separates the three cases: explicit forceDemo (payment failure), user with apps (show real data only), and new users (show demo). The comments effectively explain the behavior change.


107-117: No issue found. The dashboardAppsStore.apps is always initialized as an array and every code path in the store (lines 8, 46, 47, 66, 68, 75, 103) ensures it remains an array. The pattern apps.value = data || [] in the fetch logic (line 74) provides a fallback to an empty array if the Supabase query returns null or undefined. Accessing .length on apps is safe.

src/components/dashboard/DevicesStats.vue (3)

16-16: LGTM! Store import added correctly.

The import follows the project's conventions with the ~/ alias and integrates properly with the new isDemoMode logic below.


427-438: LGTM! Consistent demo mode logic.

The isDemoMode implementation matches the pattern established in DeploymentStatsCard.vue, ensuring consistent behavior across all dashboard charts. The comments clearly document the three-tier logic.


530-530: LGTM! Helpful contextual comment.

The comment explicitly ties forceDemo to the "payment failed" scenario, which clarifies the intended use case for readers of the code.

src/components/dashboard/UsageCard.vue (4)

13-13: LGTM! Store import added correctly.

The import follows project conventions and enables the new isDemoMode logic.


50-54: LGTM! Well-documented prop addition.

The forceDemo prop is clearly defined with an appropriate default and a helpful comment explaining its purpose in the payment failed scenario.


85-96: LGTM! Consistent demo mode implementation.

The isDemoMode logic matches the pattern in DeploymentStatsCard.vue and DevicesStats.vue, ensuring all dashboard charts behave consistently. The three-tier check (forceDemo → apps.length → isLoaded) provides clear precedence.


97-99: LGTM! Clean separation of demo and real data paths.

The effective computed properties provide a clean abstraction that switches between demo and real data based on isDemoMode, making the downstream logic simpler and easier to reason about.

All charts now show "No Data" text instead of empty charts when:
- total/count is 0 AND
- not in demo mode

This makes the behavior consistent across all dashboard charts.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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: 0

🧹 Nitpick comments (2)
src/components/dashboard/UsageCard.vue (1)

85-96: Consider moving store instantiation to module level for consistency.

The useDashboardAppsStore() is called inside the computed property, which differs from UpdateStatsCard.vue where the store is instantiated at the module level (line 59). While functionally correct (Pinia stores are singletons), moving it to the top level improves consistency and avoids the slight overhead of calling the function on each computed evaluation.

♻️ Suggested refactor
 import { useDashboardAppsStore } from '~/stores/dashboardApps'
 import ChartCard from './ChartCard.vue'
 import LineChartStats from './LineChartStats.vue'
+
+const dashboardAppsStore = useDashboardAppsStore()

 const props = defineProps({

Then update the computed:

 // Demo mode: show demo data only when forceDemo is true OR user has no apps
 // If user has apps, ALWAYS show real data (even if empty)
-const dashboardAppsStore = useDashboardAppsStore()
 const isDemoMode = computed(() => {
   if (props.forceDemo)
     return true
src/components/dashboard/BundleUploadsCard.vue (1)

106-117: Move store instantiation to module level to match UpdateStatsCard.vue pattern.

Same issue as UsageCard.vue: useDashboardAppsStore() is called inside the computed property (line 112), but UpdateStatsCard.vue instantiates it at the module level. Additionally, the store is already being called in calculateStats() (line 183), so having a module-level instance would reduce duplication.

♻️ Suggested refactor
 import { useDashboardAppsStore } from '~/stores/dashboardApps'
 import { useOrganizationStore } from '~/stores/organization'
 import BundleUploadsChart from './BundleUploadsChart.vue'
 import ChartCard from './ChartCard.vue'
+
+const dashboardAppsStore = useDashboardAppsStore()

Then update the computed and function:

 const isDemoMode = computed(() => {
   if (props.forceDemo)
     return true
   // If user has apps, never show demo data
-  const dashboardAppsStore = useDashboardAppsStore()
   if (dashboardAppsStore.apps.length > 0)
     return false
   // No apps and store is loaded = show demo
   return dashboardAppsStore.isLoaded
 })
   else {
     // Multiple apps mode - use store for shared apps data
-    const dashboardAppsStore = useDashboardAppsStore()
     // Force fetch if org changed to ensure we get fresh data
     await dashboardAppsStore.fetchApps(orgChanged)
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e196b93 and 3979e82.

📒 Files selected for processing (4)
  • src/components/dashboard/BundleUploadsCard.vue
  • src/components/dashboard/DevicesStats.vue
  • src/components/dashboard/UpdateStatsCard.vue
  • src/components/dashboard/UsageCard.vue
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/dashboard/DevicesStats.vue
🧰 Additional context used
📓 Path-based instructions (10)
src/**/*.vue

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.vue: Use Vue 3 with Composition API and <script setup> syntax for frontend components
Style components using TailwindCSS with DaisyUI components

src/**/*.vue: Use Vue 3 <script setup> syntax exclusively for all Vue component scripts
Use Tailwind utility classes for layout and spacing in Vue components
Use DaisyUI components (d-btn, d-input, d-card) for interactive elements in Vue components
Use Konsta components ONLY for safe area helpers (top/bottom insets) in Vue components; avoid other uses
Use useRoute() from vue-router to access route parameters and useRouter() for programmatic navigation in Vue components

Files:

  • src/components/dashboard/BundleUploadsCard.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

Use single quotes and no semicolons per @antfu/eslint-config

Files:

  • src/components/dashboard/BundleUploadsCard.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
src/components/**/*.vue

📄 CodeRabbit inference engine (CLAUDE.md)

Reusable Vue components should be organized in src/components/ directory

Files:

  • src/components/dashboard/BundleUploadsCard.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
{capacitor.config.{ts,js},src/**/*.{ts,tsx,vue}}

📄 CodeRabbit inference engine (CLAUDE.md)

Mobile apps should use Capacitor with app ID ee.forgr.capacitor_go for native mobile functionality

Files:

  • src/components/dashboard/BundleUploadsCard.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
src/**/*.{ts,tsx,vue,js}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use ~/ alias for imports from src/ directory in frontend TypeScript and Vue components

Files:

  • src/components/dashboard/BundleUploadsCard.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
src/**/*.{vue,ts,js}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Frontend ESLint must pass before commit; run bun lint:fix to auto-fix issues in frontend files

Files:

  • src/components/dashboard/BundleUploadsCard.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
**/{src,app}/{components,views,pages}/**/*.{vue,css,scss}

📄 CodeRabbit inference engine (AGENTS.md)

The web client is built with Vue.js and Tailwind CSS; lean on utility classes and composition-friendly patterns rather than bespoke CSS.

Files:

  • src/components/dashboard/BundleUploadsCard.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
**/{src,app}/{components,views,pages}/**/*.vue

📄 CodeRabbit inference engine (AGENTS.md)

Use DaisyUI (d- prefixed classes) for buttons, inputs, and other interactive primitives to keep behavior and spacing consistent.

Files:

  • src/components/dashboard/BundleUploadsCard.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
**/{src,app}/**/*.{vue,ts,tsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

Konsta components are reserved for the safe area helpers. Avoid importing konsta anywhere else in the app.

Files:

  • src/components/dashboard/BundleUploadsCard.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
**/{src,app}/**/*.{css,scss,vue}

📄 CodeRabbit inference engine (AGENTS.md)

Capgo's design uses deep slate bases with the 'Extract' azure highlight (--color-azure-500: #119eff) and soft radii; mirror the palette from src/styles/style.css (e.g., --color-primary-500: #515271) when introducing new UI.

Files:

  • src/components/dashboard/BundleUploadsCard.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
🧠 Learnings (1)
📚 Learning: 2025-12-23T01:19:04.593Z
Learnt from: riderx
Repo: Cap-go/capgo PR: 1297
File: src/components/dashboard/DeploymentBanner.vue:77-79
Timestamp: 2025-12-23T01:19:04.593Z
Learning: In the Cap-go codebase, ensure that app permission checks never include the role 'owner'. App-level permissions should be based on the user_min_right enum with values: read, upload, write, admin, super_admin (and NOT owner). This pattern applies across Vue components that perform permission checks; if you find a check referencing 'owner' for app-level access, replace it with the appropriate user_min_right value and keep organization-level owner handling in organization.ts.

Applied to files:

  • src/components/dashboard/BundleUploadsCard.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run tests
  • GitHub Check: Analyze (java-kotlin)
🔇 Additional comments (4)
src/components/dashboard/UpdateStatsCard.vue (2)

82-95: LGTM! Clean demo mode gating logic.

The isDemoMode computed property correctly prioritizes forceDemo, then checks for real apps, and falls back to showing demo data only when the store is loaded with no apps. The store is properly instantiated at module level (line 59).


115-115: LGTM!

The hasData logic correctly ensures charts display content in demo mode or when there's real data, aligning with the PR objective to show consistent "No Data" text when total is 0 and not in demo mode.

src/components/dashboard/UsageCard.vue (1)

141-141: LGTM!

The hasData logic correctly handles both demo mode and real data scenarios.

src/components/dashboard/BundleUploadsCard.vue (1)

126-126: LGTM!

The hasData logic aligns with the other dashboard components.

riderx and others added 6 commits January 8, 2026 22:46
For charts like Storage, the total can be > 0 (cumulative) but there may be
no activity in the current period. Now we:
- Still show the total value in the header (0.04GB)
- Show "No Data" in the chart area when there's no chart activity

This is different from other charts where total = sum of chart data.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When chart data has very small values (e.g., 0.04 GB storage), the Y-axis
now scales based on actual data max + 20% padding, instead of defaulting
to a large range (0-1) that makes the line nearly invisible.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The tooltip was using absolute page coordinates but is appended to the
canvas parent element. Now uses tooltip.caretX/Y directly (relative to
canvas) and bounds-checks against canvas dimensions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The first vertical and horizontal lines of charts had a different color
than other grid lines due to Chart.js axis border styling. Now hiding
the axis border lines so all grid lines have consistent appearance.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The tooltip now uses the actual label value from the chart's X-axis
instead of calculating from cycleStart + dataIndex. This ensures the
tooltip date always matches what's shown on the X-axis at that position.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- getDaysBetweenDates now normalizes both dates to midnight before calculating
- reloadAllCharts now normalizes date range to start of day
- This ensures the first day of the 30-day range includes all data from midnight

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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

🤖 Fix all issues with AI agents
In @src/services/chartTooltip.ts:
- Around line 189-217: The local variable tooltipDate is declared only inside
the if/else branches so the click handler can't access it; to fix, declare let
tooltipDate: Date | undefined (and ensure formattedTitle remains) in the outer
scope before the branch that computes it, assign tooltipDate inside both the
"labelAtIndex !== undefined" branch (where you create and adjust tooltipDate)
and in the fallback branch (using getDateFromIndex), then use tooltipDate in the
click handler after guarding for undefined; update references to
formatDateForTooltip(tooltipDate) to only be called when tooltipDate is set and
keep formattedTitle assignment in the same outer scope so both the tooltip
rendering and the click handler can access the computed values.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3979e82 and 264b60b.

📒 Files selected for processing (6)
  • src/components/dashboard/LineChartStats.vue
  • src/components/dashboard/Usage.vue
  • src/components/dashboard/UsageCard.vue
  • src/services/chartConfig.ts
  • src/services/chartTooltip.ts
  • src/services/conversion.ts
🧰 Additional context used
📓 Path-based instructions (12)
src/**/*.vue

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.vue: Use Vue 3 with Composition API and <script setup> syntax for frontend components
Style components using TailwindCSS with DaisyUI components

src/**/*.vue: Use Vue 3 <script setup> syntax exclusively for all Vue component scripts
Use Tailwind utility classes for layout and spacing in Vue components
Use DaisyUI components (d-btn, d-input, d-card) for interactive elements in Vue components
Use Konsta components ONLY for safe area helpers (top/bottom insets) in Vue components; avoid other uses
Use useRoute() from vue-router to access route parameters and useRouter() for programmatic navigation in Vue components

Files:

  • src/components/dashboard/Usage.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/LineChartStats.vue
**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

Use single quotes and no semicolons per @antfu/eslint-config

Files:

  • src/components/dashboard/Usage.vue
  • src/components/dashboard/UsageCard.vue
  • src/services/chartConfig.ts
  • src/services/chartTooltip.ts
  • src/components/dashboard/LineChartStats.vue
  • src/services/conversion.ts
src/components/**/*.vue

📄 CodeRabbit inference engine (CLAUDE.md)

Reusable Vue components should be organized in src/components/ directory

Files:

  • src/components/dashboard/Usage.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/LineChartStats.vue
{capacitor.config.{ts,js},src/**/*.{ts,tsx,vue}}

📄 CodeRabbit inference engine (CLAUDE.md)

Mobile apps should use Capacitor with app ID ee.forgr.capacitor_go for native mobile functionality

Files:

  • src/components/dashboard/Usage.vue
  • src/components/dashboard/UsageCard.vue
  • src/services/chartConfig.ts
  • src/services/chartTooltip.ts
  • src/components/dashboard/LineChartStats.vue
  • src/services/conversion.ts
src/**/*.{ts,tsx,vue,js}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use ~/ alias for imports from src/ directory in frontend TypeScript and Vue components

Files:

  • src/components/dashboard/Usage.vue
  • src/components/dashboard/UsageCard.vue
  • src/services/chartConfig.ts
  • src/services/chartTooltip.ts
  • src/components/dashboard/LineChartStats.vue
  • src/services/conversion.ts
src/**/*.{vue,ts,js}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Frontend ESLint must pass before commit; run bun lint:fix to auto-fix issues in frontend files

Files:

  • src/components/dashboard/Usage.vue
  • src/components/dashboard/UsageCard.vue
  • src/services/chartConfig.ts
  • src/services/chartTooltip.ts
  • src/components/dashboard/LineChartStats.vue
  • src/services/conversion.ts
**/{src,app}/{components,views,pages}/**/*.{vue,css,scss}

📄 CodeRabbit inference engine (AGENTS.md)

The web client is built with Vue.js and Tailwind CSS; lean on utility classes and composition-friendly patterns rather than bespoke CSS.

Files:

  • src/components/dashboard/Usage.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/LineChartStats.vue
**/{src,app}/{components,views,pages}/**/*.vue

📄 CodeRabbit inference engine (AGENTS.md)

Use DaisyUI (d- prefixed classes) for buttons, inputs, and other interactive primitives to keep behavior and spacing consistent.

Files:

  • src/components/dashboard/Usage.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/LineChartStats.vue
**/{src,app}/**/*.{vue,ts,tsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

Konsta components are reserved for the safe area helpers. Avoid importing konsta anywhere else in the app.

Files:

  • src/components/dashboard/Usage.vue
  • src/components/dashboard/UsageCard.vue
  • src/services/chartConfig.ts
  • src/services/chartTooltip.ts
  • src/components/dashboard/LineChartStats.vue
  • src/services/conversion.ts
**/{src,app}/**/*.{css,scss,vue}

📄 CodeRabbit inference engine (AGENTS.md)

Capgo's design uses deep slate bases with the 'Extract' azure highlight (--color-azure-500: #119eff) and soft radii; mirror the palette from src/styles/style.css (e.g., --color-primary-500: #515271) when introducing new UI.

Files:

  • src/components/dashboard/Usage.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/LineChartStats.vue
src/services/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

API clients and external service integrations should be organized in src/services/ directory

Files:

  • src/services/chartConfig.ts
  • src/services/chartTooltip.ts
  • src/services/conversion.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use TypeScript strict mode with path aliases mapping ~/ to src/

Files:

  • src/services/chartConfig.ts
  • src/services/chartTooltip.ts
  • src/services/conversion.ts
🧠 Learnings (1)
📚 Learning: 2025-12-23T01:19:04.593Z
Learnt from: riderx
Repo: Cap-go/capgo PR: 1297
File: src/components/dashboard/DeploymentBanner.vue:77-79
Timestamp: 2025-12-23T01:19:04.593Z
Learning: In the Cap-go codebase, ensure that app permission checks never include the role 'owner'. App-level permissions should be based on the user_min_right enum with values: read, upload, write, admin, super_admin (and NOT owner). This pattern applies across Vue components that perform permission checks; if you find a check referencing 'owner' for app-level access, replace it with the appropriate user_min_right value and keep organization-level owner handling in organization.ts.

Applied to files:

  • src/components/dashboard/Usage.vue
  • src/components/dashboard/UsageCard.vue
  • src/components/dashboard/LineChartStats.vue
🪛 GitHub Actions: Run tests
src/services/chartTooltip.ts

[error] 318-318: Cannot find name 'tooltipDate'. TS2304. Reported by vue-tsc --noEmit during typecheck.

🔇 Additional comments (10)
src/services/conversion.ts (1)

29-38: LGTM! Date normalization ensures consistent day calculations.

The midnight normalization prevents time-of-day variations from affecting the day count calculation. Since firstDate and secondDate are local copies, mutating them with setHours is safe.

src/services/chartTooltip.ts (1)

337-374: LGTM! Canvas-relative positioning with proper bounds checking.

The positioning logic correctly:

  • Uses tooltip.caretX/Y which are canvas-relative coordinates
  • Clamps horizontal position to keep tooltip within canvas bounds
  • Handles vertical overflow by repositioning above the caret
src/components/dashboard/Usage.vue (2)

183-204: LGTM! Date normalization for consistent chart reloading.

The midnight normalization at lines 186-188 ensures consistent 30-day windows when reloading charts, aligning with the getDaysBetweenDates normalization in conversion.ts.


725-752: LGTM! Consistent forceDemo prop forwarding to all chart components.

The forceDemo prop is correctly passed to all UsageCard instances and other dashboard stat components, ensuring unified demo mode behavior across the dashboard.

src/components/dashboard/LineChartStats.vue (2)

386-412: LGTM! Auto-scaling Y-axis with appropriate padding.

The dataMax computed property correctly:

  • Aggregates values from both main data and per-app data
  • Filters for valid finite numbers
  • Adds 20% padding to prevent data from touching the top edge
  • Returns undefined when no valid data exists, allowing Chart.js defaults

414-439: LGTM! Conditional suggestedMax application.

The scales are created once and suggestedMax is applied only when dataMax is defined, avoiding unnecessary overrides of Chart.js defaults.

src/services/chartConfig.ts (2)

42-64: LGTM! Clean axis configuration with hidden borders.

The border: { display: false } configuration hides the axis border line, creating a cleaner visual appearance that aligns with the grid lines. The gridColor variable extraction improves readability.


69-110: LGTM! Consistent Y-axis configuration.

The Y-axis configuration mirrors the X-axis pattern with hidden borders and extracted gridColor variable.

src/components/dashboard/UsageCard.vue (2)

85-99: LGTM! Well-structured demo mode logic.

The isDemoMode computed property correctly implements the hierarchy:

  1. forceDemo prop takes precedence (for payment-failed/app-not-found states)
  2. Users with apps always see real data (even if empty)
  3. New users without apps see demo data after store is loaded

This prevents the inconsistent mixed real/demo data display mentioned in the PR objectives.


141-149: LGTM! Correct chart data presence check.

The hasChartData computed properly distinguishes between:

  • Demo mode (always has data to show)
  • Real data with actual activity (values > 0)

This handles the edge case mentioned in the summary where Storage total > 0 but no period activity.

Comment thread src/services/chartTooltip.ts
riderx and others added 3 commits January 8, 2026 23:22
Set end date to 23:59:59.999 instead of 00:00:00 so today's data
is included in the query results. Also fixes TypeScript error
where tooltipDate variable was used outside its block scope.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Changed the end date from today (which excludes today's data) to tomorrow
at midnight. This ensures the backend query includes all data from today.

Fixed in three locations:
- organization.ts: initial dashboard load
- Usage.vue reloadAllCharts(): manual refresh
- Usage.vue getUsages(): data fetching

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@riderx riderx merged commit 2bdc787 into main Jan 8, 2026
4 checks passed
@riderx riderx deleted the riderx/fix-dashboard-fake-data branch January 8, 2026 23:28
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Jan 8, 2026

Dalanir pushed a commit that referenced this pull request Jan 12, 2026
…1402)

* fix: only show demo data when payment failed (forceDemo=true)

- Remove auto-demo mode that was incorrectly showing fake data when real data exists
- Demo data now ONLY shows when forceDemo is true (payment failed modal state)
- Users with apps will see their real data (even if empty) instead of fake demo data
- Show blurred demo view with modal when navigating to non-existent app

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: use dashboardAppsStore to determine demo mode for charts

Demo data now only shows when:
1. forceDemo is true (payment failed), OR
2. User has no apps (dashboardAppsStore.apps.length === 0)

If user has apps, ALWAYS show real data (even if empty).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: show 'No Data' consistently when chart has no data

All charts now show "No Data" text instead of empty charts when:
- total/count is 0 AND
- not in demo mode

This makes the behavior consistent across all dashboard charts.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: show 'No Data' in chart when no activity but total exists (Storage)

For charts like Storage, the total can be > 0 (cumulative) but there may be
no activity in the current period. Now we:
- Still show the total value in the header (0.04GB)
- Show "No Data" in the chart area when there's no chart activity

This is different from other charts where total = sum of chart data.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: auto-scale Y-axis to make small values visible in charts

When chart data has very small values (e.g., 0.04 GB storage), the Y-axis
now scales based on actual data max + 20% padding, instead of defaulting
to a large range (0-1) that makes the line nearly invisible.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: correct tooltip positioning to use relative canvas coordinates

The tooltip was using absolute page coordinates but is appended to the
canvas parent element. Now uses tooltip.caretX/Y directly (relative to
canvas) and bounds-checks against canvas dimensions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: hide chart axis border lines for consistent grid appearance

The first vertical and horizontal lines of charts had a different color
than other grid lines due to Chart.js axis border styling. Now hiding
the axis border lines so all grid lines have consistent appearance.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: align tooltip date with actual chart X-axis label

The tooltip now uses the actual label value from the chart's X-axis
instead of calculating from cycleStart + dataIndex. This ensures the
tooltip date always matches what's shown on the X-axis at that position.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: normalize dates to midnight for consistent chart data alignment

- getDaysBetweenDates now normalizes both dates to midnight before calculating
- reloadAllCharts now normalizes date range to start of day
- This ensures the first day of the 30-day range includes all data from midnight

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: include today's data in 30-day chart date range

Set end date to 23:59:59.999 instead of 00:00:00 so today's data
is included in the query results. Also fixes TypeScript error
where tooltipDate variable was used outside its block scope.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: use tomorrow midnight as end date for MAU/storage/bandwidth queries

Changed the end date from today (which excludes today's data) to tomorrow
at midnight. This ensures the backend query includes all data from today.

Fixed in three locations:
- organization.ts: initial dashboard load
- Usage.vue reloadAllCharts(): manual refresh
- Usage.vue getUsages(): data fetching

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
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