Skip to content

feat: add payment failure blur modal UI with demo data#1399

Merged
riderx merged 3 commits into
mainfrom
riderx/payment-blur-modal
Jan 8, 2026
Merged

feat: add payment failure blur modal UI with demo data#1399
riderx merged 3 commits into
mainfrom
riderx/payment-blur-modal

Conversation

@riderx
Copy link
Copy Markdown
Member

@riderx riderx commented Jan 8, 2026

Summary

Improve payment failure/subscription required UI across dashboard, app list, and app detail pages by showing blurred demo data behind a centered modal instead of inline red alert cards. The new design follows the elegant "no apps" pattern with amber/orange urgency styling.

Test plan

  • Navigate to dashboard with payment failed status → verify blur effect and centered payment modal appear
  • Navigate to app list with payment failed status → verify demo apps show behind blur
  • Navigate to app detail page with payment failed status → verify charts show demo data behind blur
  • Click "Upgrade Plan" button on any payment modal → verify navigation to plans page
  • Verify all buttons have proper cursor pointer styling

Changes

  • New PaymentRequiredModal.vue component with amber/orange design
  • Updated Usage.vue with forceDemo prop to show demo charts
  • Updated all chart cards (BundleUploads, UpdateStats, DeploymentStats, DevicesStats) to support forceDemo
  • Updated dashboard, app list, and app detail pages to show blur + modal on payment failure
  • Added demo apps data for app list blur state
  • Added cursor-pointer styling to payment buttons in Plans page

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added payment-required modal that appears when subscription status fails, with option to navigate to plans page
    • Dashboard and app pages automatically display demo data when payment is declined
    • Content blur and interaction disabling when payment failure is detected
  • Style

    • Improved cursor styling on plan selection buttons to indicate interactivity

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

Improve payment failure state across dashboard, app list, and app detail pages by showing blurred demo data behind a centered modal modal instead of the current red alert card. The new design is more visually elegant and uses amber/orange accent colors for urgency without being alarming. Includes demo apps for the app list and realistic demo charts for the dashboard.

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

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
@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 52 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 638ffa8 and 9814445.

📒 Files selected for processing (2)
  • src/components/dashboard/DevicesStats.vue
  • src/pages/app/index.vue
📝 Walkthrough

Walkthrough

This PR implements a payment-gating system that prevents access to full dashboard features when subscription payment fails. It introduces a PaymentRequiredModal component, adds a forceDemo prop across dashboard cards to display demo data, detects payment failure based on organization subscription status and security access, and conditionally blurs content while showing placeholder data when payment fails.

Changes

Cohort / File(s) Summary
New Component Type Declaration
src/components.d.ts
Added PaymentRequiredModal to global Vue component types for import-based typing and global usage.
New Modal Component
src/components/PaymentRequiredModal.vue
Created new Vue 3 modal component using script setup with TypeScript, i18n, and vue-router integration. Renders centered modal with amber icon, translated messaging, and navigation button to /settings/organization/plans.
Dashboard Card Demo Mode
src/components/dashboard/BundleUploadsCard.vue, DeploymentStatsCard.vue, DevicesStats.vue, UpdateStatsCard.vue
Added forceDemo boolean prop (default false) to each component. Updated demo-mode detection to treat forceDemo as an override condition for displaying demo data instead of real data.
Dashboard Usage Component
src/components/dashboard/Usage.vue
Added forceDemo prop and loadDemoData() method that generates 30 days of sample data. Updated onMounted() and loadData() to conditionally load demo data when forceDemo is true. Propagates force-demo binding to all child dashboard card components.
App Detail Page Payment Gating
src/pages/app/[package].vue
Introduced lacksSecurityAccess and paymentFailed computed properties. Conditionally renders FailedCard, DeploymentBanner, and Usage based on security and payment state. Blurs main content and shows PaymentRequiredModal when payment fails. Passes force-demo prop to Usage and chart components when payment is failed.
Apps List Page Payment Gating
src/pages/app/index.vue
Added paymentFailed computed property with demo apps fallback. Conditionally renders StepsApp and blurs table with disabled interactions when payment fails. Switches data source to demo apps when payment is failed. Shows PaymentRequiredModal overlay on payment failure.
Dashboard Page Payment Gating
src/pages/dashboard.vue
Introduced paymentFailed and shouldBlurContent computed properties. Conditionally renders FailedCard and blurs Usage component based on payment state. Passes force-demo prop to Usage when payment fails. Displays PaymentRequiredModal and empty-state overlay when appropriate.
Plans Page Styling
src/pages/settings/organization/Plans.vue
Updated buttonStyle() function to add cursor-pointer class for recommended and default plan buttons.

Sequence Diagram

sequenceDiagram
    participant User
    participant Page as App Page
    participant OrgStore as Organization<br/>Store
    participant Content as Dashboard<br/>Content
    participant Modal as PaymentRequired<br/>Modal
    participant Cards as Dashboard<br/>Cards

    User->>Page: Access dashboard/apps page
    Page->>OrgStore: Check currentOrganizationFailed<br/>and security access
    OrgStore-->>Page: Return payment<br/>and security state
    
    alt Payment Failed
        Page->>Content: Render with blur effect
        Page->>Cards: Pass force-demo=true
        Cards->>Cards: Load and display demo data
        Page->>Modal: Show PaymentRequiredModal
        User->>Modal: Click "View Plans" button
        Modal->>Page: Navigate to /settings/organization/plans
    else Payment OK & Security Cleared
        Page->>Content: Render normally (no blur)
        Page->>Cards: Pass force-demo=false
        Cards->>Cards: Load real data from server
    end
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly Related PRs

Poem

🐰 A modal springs forth with amber glow,
When payments falter, demos flow,
Cards blur softly, access denied,
Until the plans are satisfied! 💳✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding payment failure UI with a blur/modal design and demo data support.
Description check ✅ Passed The description provides a comprehensive summary, detailed test plan with specific navigation steps, and explicit list of changes across multiple components.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ 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: 8ab548257d

ℹ️ 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 +32 to +35
forceDemo: {
type: Boolean,
default: false,
},
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 Wire forceDemo into DevicesStats behavior

In the payment-failed flow you pass :force-demo="paymentFailed", but this component never reads props.forceDemo, so it will continue to fetch and render real device stats (and enable tooltip navigation) instead of demo/placeholder data. That means a failed-payment org still sees real stats behind the blur, which is inconsistent with the intended “demo data” state used by the other cards. Consider gating the data fetch/rendering or supplying demo data when forceDemo is true, similar to the other chart cards.

Useful? React with 👍 / 👎.

riderx and others added 2 commits January 8, 2026 17:43
Address PR feedback - DevicesStats now properly uses demo data when
forceDemo prop is true instead of fetching real device stats.

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

🤖 Fix all issues with AI agents
In @src/components/dashboard/DevicesStats.vue:
- Around line 32-35: The forceDemo prop is declared but unused; add an
isDemoMode computed property (mirroring other cards like
BundleUploadsCard/DeploymentStatsCard) that returns true when forceDemo is true
or when the app is in demo mode, then use isDemoMode throughout DevicesStats
(template conditional rendering and any data/logic that chooses demo data) so
the component displays demo device stats when forceDemo is set. Ensure the
computed property name is isDemoMode and update any places that currently always
use real data to switch based on isDemoMode.

In @src/components/PaymentRequiredModal.vue:
- Line 14: The overlay div in PaymentRequiredModal.vue currently uses the class
token "z-10" (the div with classes "flex absolute inset-0 z-10 ..."); update
that token to "z-50" so the modal's z-index matches other full-screen modals
(WebhookForm, WebhookDeliveryLog, DialogV2) and prevents the sidebar/navbars
from stacking above it.
🧹 Nitpick comments (1)
src/pages/settings/organization/Plans.vue (1)

249-257: Optional: cursor-pointer is redundant on button elements.

Native HTML <button> elements display a pointer cursor by default. The explicit cursor-pointer class is redundant but harmless.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f1c9dac and 638ffa8.

📒 Files selected for processing (11)
  • src/components.d.ts
  • src/components/PaymentRequiredModal.vue
  • 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
  • src/pages/app/[package].vue
  • src/pages/app/index.vue
  • src/pages/dashboard.vue
  • src/pages/settings/organization/Plans.vue
🧰 Additional context used
📓 Path-based instructions (12)
**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

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

Files:

  • src/components.d.ts
  • src/components/dashboard/DevicesStats.vue
  • src/components/dashboard/DeploymentStatsCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
  • src/components/dashboard/BundleUploadsCard.vue
  • src/pages/dashboard.vue
  • src/pages/app/index.vue
  • src/pages/app/[package].vue
  • src/pages/settings/organization/Plans.vue
  • src/components/dashboard/Usage.vue
  • src/components/PaymentRequiredModal.vue
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

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

Files:

  • src/components.d.ts
{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.d.ts
  • src/components/dashboard/DevicesStats.vue
  • src/components/dashboard/DeploymentStatsCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
  • src/components/dashboard/BundleUploadsCard.vue
  • src/pages/dashboard.vue
  • src/pages/app/index.vue
  • src/pages/app/[package].vue
  • src/pages/settings/organization/Plans.vue
  • src/components/dashboard/Usage.vue
  • src/components/PaymentRequiredModal.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.d.ts
  • src/components/dashboard/DevicesStats.vue
  • src/components/dashboard/DeploymentStatsCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
  • src/components/dashboard/BundleUploadsCard.vue
  • src/pages/dashboard.vue
  • src/pages/app/index.vue
  • src/pages/app/[package].vue
  • src/pages/settings/organization/Plans.vue
  • src/components/dashboard/Usage.vue
  • src/components/PaymentRequiredModal.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.d.ts
  • src/components/dashboard/DevicesStats.vue
  • src/components/dashboard/DeploymentStatsCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
  • src/components/dashboard/BundleUploadsCard.vue
  • src/pages/dashboard.vue
  • src/pages/app/index.vue
  • src/pages/app/[package].vue
  • src/pages/settings/organization/Plans.vue
  • src/components/dashboard/Usage.vue
  • src/components/PaymentRequiredModal.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.d.ts
  • src/components/dashboard/DevicesStats.vue
  • src/components/dashboard/DeploymentStatsCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
  • src/components/dashboard/BundleUploadsCard.vue
  • src/pages/dashboard.vue
  • src/pages/app/index.vue
  • src/pages/app/[package].vue
  • src/pages/settings/organization/Plans.vue
  • src/components/dashboard/Usage.vue
  • src/components/PaymentRequiredModal.vue
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/DevicesStats.vue
  • src/components/dashboard/DeploymentStatsCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
  • src/components/dashboard/BundleUploadsCard.vue
  • src/pages/dashboard.vue
  • src/pages/app/index.vue
  • src/pages/app/[package].vue
  • src/pages/settings/organization/Plans.vue
  • src/components/dashboard/Usage.vue
  • src/components/PaymentRequiredModal.vue
src/components/**/*.vue

📄 CodeRabbit inference engine (CLAUDE.md)

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

Files:

  • src/components/dashboard/DevicesStats.vue
  • src/components/dashboard/DeploymentStatsCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
  • src/components/dashboard/BundleUploadsCard.vue
  • src/components/dashboard/Usage.vue
  • src/components/PaymentRequiredModal.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/DevicesStats.vue
  • src/components/dashboard/DeploymentStatsCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
  • src/components/dashboard/BundleUploadsCard.vue
  • src/pages/dashboard.vue
  • src/pages/app/index.vue
  • src/pages/app/[package].vue
  • src/pages/settings/organization/Plans.vue
  • src/components/dashboard/Usage.vue
  • src/components/PaymentRequiredModal.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/DevicesStats.vue
  • src/components/dashboard/DeploymentStatsCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
  • src/components/dashboard/BundleUploadsCard.vue
  • src/pages/dashboard.vue
  • src/pages/app/index.vue
  • src/pages/app/[package].vue
  • src/pages/settings/organization/Plans.vue
  • src/components/dashboard/Usage.vue
  • src/components/PaymentRequiredModal.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/DevicesStats.vue
  • src/components/dashboard/DeploymentStatsCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
  • src/components/dashboard/BundleUploadsCard.vue
  • src/pages/dashboard.vue
  • src/pages/app/index.vue
  • src/pages/app/[package].vue
  • src/pages/settings/organization/Plans.vue
  • src/components/dashboard/Usage.vue
  • src/components/PaymentRequiredModal.vue
src/pages/**/*.vue

📄 CodeRabbit inference engine (CLAUDE.md)

Use file-based routing with unplugin-vue-router for frontend pages

Frontend file-based routing uses src/pages/ directory structure; routes auto-generate with unplugin-vue-router and types are available in src/typed-router.d.ts

Files:

  • src/pages/dashboard.vue
  • src/pages/app/index.vue
  • src/pages/app/[package].vue
  • src/pages/settings/organization/Plans.vue
🧠 Learnings (7)
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to src/**/*.vue : Use `useRoute()` from `vue-router` to access route parameters and `useRouter()` for programmatic navigation in Vue components

Applied to files:

  • src/components.d.ts
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to src/**/*.vue : Use DaisyUI components (`d-btn`, `d-input`, `d-card`) for interactive elements in Vue components

Applied to files:

  • src/components.d.ts
📚 Learning: 2026-01-08T00:40:00.524Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T00:40:00.524Z
Learning: Applies to **/{src,app}/**/*.{vue,ts,tsx,js} : Konsta components are reserved for the safe area helpers. Avoid importing `konsta` anywhere else in the app.

Applied to files:

  • src/components.d.ts
📚 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/DevicesStats.vue
  • src/components/dashboard/DeploymentStatsCard.vue
  • src/components/dashboard/UpdateStatsCard.vue
  • src/components/dashboard/BundleUploadsCard.vue
  • src/pages/dashboard.vue
  • src/pages/app/index.vue
  • src/pages/app/[package].vue
  • src/pages/settings/organization/Plans.vue
  • src/components/dashboard/Usage.vue
  • src/components/PaymentRequiredModal.vue
📚 Learning: 2025-12-05T17:34:25.556Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-05T17:34:25.556Z
Learning: Applies to src/**/*.vue : Use Vue 3 with Composition API and `<script setup>` syntax for frontend components

Applied to files:

  • src/pages/app/[package].vue
  • src/components/PaymentRequiredModal.vue
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to src/**/*.vue : Use Vue 3 `<script setup>` syntax exclusively for all Vue component scripts

Applied to files:

  • src/pages/app/[package].vue
📚 Learning: 2026-01-08T00:40:00.524Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T00:40:00.524Z
Learning: Applies to **/{src,app}/{components,views,pages}/**/*.vue : Use DaisyUI (`d-` prefixed classes) for buttons, inputs, and other interactive primitives to keep behavior and spacing consistent.

Applied to files:

  • src/pages/settings/organization/Plans.vue
🪛 GitHub Actions: Run tests
src/pages/app/index.vue

[error] 73-73: vue-tsc --noEmit: Type instantiation is excessively deep and possibly infinite. TS2589

⏰ 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: Analyze (java-kotlin)
🔇 Additional comments (14)
src/components/dashboard/BundleUploadsCard.vue (1)

36-39: LGTM! forceDemo prop correctly integrated.

The prop is properly wired into the demo mode detection logic, enabling forced demo data display when payment fails.

Also applies to: 109-110

src/components/PaymentRequiredModal.vue (1)

13-45: Verify no-dismiss behavior is intentional.

The modal has no close button, no backdrop-click-to-dismiss, and no escape key handler. Users can only proceed by clicking "Upgrade Plan." If this is intentional (forcing payment resolution), consider adding an accessibility note. Otherwise, users may expect a way to dismiss the modal.

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

36-39: LGTM! forceDemo prop correctly integrated.

The implementation matches the pattern used in BundleUploadsCard, properly enabling forced demo mode when payment fails.

Also applies to: 110-111

src/components.d.ts (1)

52-52: LGTM!

The type declarations for PaymentRequiredModal follow the established pattern in this auto-generated file and correctly reference the new component.

Also applies to: 116-116

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

33-36: LGTM!

The forceDemo prop implementation is clean and follows Vue 3 conventions. The short-circuit evaluation in isDemoMode correctly prioritizes forceDemo before checking for real data, ensuring consistent behavior when payment has failed.

Also applies to: 90-91

src/pages/dashboard.vue (2)

37-43: LGTM!

The computed properties correctly separate security access failures from payment failures. The logic ensures:

  • paymentFailed only triggers when there's a subscription issue without a security compliance problem
  • shouldBlurContent properly aggregates both empty state and payment failure scenarios

90-96: LGTM!

The conditional rendering correctly shows FailedCard only for security access issues while applying blur and showing the PaymentRequiredModal for payment failures. The :force-demo prop is correctly passed to the Usage component.

Also applies to: 119-121

src/pages/app/[package].vue (2)

34-45: LGTM!

The security and payment state computed properties follow the same pattern as the dashboard page, ensuring consistent behavior across the application.


116-152: LGTM!

The payment failure handling is well-implemented:

  • DeploymentBanner is hidden during payment failure to prevent deploy actions
  • All chart cards receive :force-demo="paymentFailed" for consistent demo data display
  • The blur overlay and modal are correctly positioned
src/pages/app/index.vue (2)

170-196: LGTM!

The conditional rendering logic is sound:

  • StepsApp is hidden during payment failure to prevent onboarding actions
  • The blur and disabled state correctly prevent interactions with demo data
  • PaymentRequiredModal is properly positioned within the relative container

41-74: The code has already been addressed with the as any[] workaround shown on line 71. The comment on line 42 explicitly documents this approach to avoid type instantiation depth issues with Database types. The Database type assertion occurs only in the displayApps computed property (line 74) where it's applied to the union of demoApps and apps.value, not on the demoApps definition itself. No changes are needed.

Likely an incorrect or invalid review comment.

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

25-28: LGTM!

The forceDemo prop is correctly typed as an optional boolean in the props interface.


509-516: LGTM!

The conditional loading logic correctly branches based on forceDemo:

  • loadData() fetches plans regardless, then conditionally loads demo or real usage data
  • onMounted() correctly triggers immediate load when forceDemo is true, bypassing the dashboardFetched check

Also applies to: 595-603


744-747: LGTM!

The :force-demo="forceDemo" prop is correctly passed to all child chart components using kebab-case, which is the standard convention for Vue templates.

Comment on lines +32 to +35
forceDemo: {
type: Boolean,
default: false,
},
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.

⚠️ Potential issue | 🔴 Critical

Critical: forceDemo prop is defined but never used.

The forceDemo prop is declared but never referenced in the component logic. Other dashboard components (BundleUploadsCard, DeploymentStatsCard) integrate this prop into their isDemoMode logic, but DevicesStats does not. This breaks the forced-demo functionality for device statistics when payment fails.

🔧 Suggested fix

The component already has demo data infrastructure but no isDemoMode computed property. Add demo mode detection and wire up the prop. For example, add after line 104:

 const tooltipClickHandler = computed<TooltipClickHandler>(() => ({
   onAppClick: navigateToBundle,
   appIdByLabel: versionByLabel.value,
 }))
+
+const isDemoMode = computed(() => props.forceDemo || (!hasData.value && !isLoading.value))
+
 const isLoading = ref(true)

Then use isDemoMode to conditionally render demo data similar to the other dashboard cards.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In @src/components/dashboard/DevicesStats.vue around lines 32 - 35, The
forceDemo prop is declared but unused; add an isDemoMode computed property
(mirroring other cards like BundleUploadsCard/DeploymentStatsCard) that returns
true when forceDemo is true or when the app is in demo mode, then use isDemoMode
throughout DevicesStats (template conditional rendering and any data/logic that
chooses demo data) so the component displays demo device stats when forceDemo is
set. Ensure the computed property name is isDemoMode and update any places that
currently always use real data to switch based on isDemoMode.

</script>

<template>
<div class="flex absolute inset-0 z-10 flex-col justify-center items-center bg-white/60 dark:bg-gray-900/60">
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for z-index usage across Vue components to identify potential conflicts
rg -nP --type=vue 'z-\d+' -C2

Repository: Cap-go/capgo

Length of output: 83


🏁 Script executed:

#!/bin/bash
# Search for z-index usage in Vue files without specifying file type
rg 'z-\d+' src/ --include='*.vue' -C2

Repository: Cap-go/capgo

Length of output: 497


🏁 Script executed:

#!/bin/bash
# Find all .vue files and search for z-index patterns
fd '\.vue$' src/ | xargs rg 'z-\d+' -C2

Repository: Cap-go/capgo

Length of output: 14738


Align z-index with other modal overlays in the codebase.

The modal uses z-10, but other full-screen modal components (WebhookForm, WebhookDeliveryLog, DialogV2) use z-50. This creates stacking conflicts: the sidebar (z-60) and navigation bars (z-40) would appear above this modal. Change to z-50 to maintain consistent modal layering.

🤖 Prompt for AI Agents
In @src/components/PaymentRequiredModal.vue at line 14, The overlay div in
PaymentRequiredModal.vue currently uses the class token "z-10" (the div with
classes "flex absolute inset-0 z-10 ..."); update that token to "z-50" so the
modal's z-index matches other full-screen modals (WebhookForm,
WebhookDeliveryLog, DialogV2) and prevents the sidebar/navbars from stacking
above it.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Jan 8, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
2 Security Hotspots

See analysis details on SonarQube Cloud

@riderx riderx merged commit 1654aaa into main Jan 8, 2026
9 of 11 checks passed
@riderx riderx deleted the riderx/payment-blur-modal branch January 8, 2026 17:01
Dalanir pushed a commit that referenced this pull request Jan 12, 2026
* feat: add payment failure blur modal UI with demo data

Improve payment failure state across dashboard, app list, and app detail pages by showing blurred demo data behind a centered modal modal instead of the current red alert card. The new design is more visually elegant and uses amber/orange accent colors for urgency without being alarming. Includes demo apps for the app list and realistic demo charts for the dashboard.

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

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

* fix: wire forceDemo prop in DevicesStats to use demo data

Address PR feedback - DevicesStats now properly uses demo data when
forceDemo prop is true instead of fetching real device stats.

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

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

---------

Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
@coderabbitai coderabbitai Bot mentioned this pull request Jan 25, 2026
5 tasks
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