feat: add payment failure blur modal UI with demo data#1399
Conversation
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>
|
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 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. 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThis PR implements a payment-gating system that prevents access to full dashboard features when subscription payment fails. It introduces a PaymentRequiredModal component, adds a Changes
Sequence DiagramsequenceDiagram
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
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly Related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ 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. Comment |
There was a problem hiding this comment.
💡 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".
| forceDemo: { | ||
| type: Boolean, | ||
| default: false, | ||
| }, |
There was a problem hiding this comment.
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 👍 / 👎.
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>
There was a problem hiding this comment.
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 explicitcursor-pointerclass is redundant but harmless.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
src/components.d.tssrc/components/PaymentRequiredModal.vuesrc/components/dashboard/BundleUploadsCard.vuesrc/components/dashboard/DeploymentStatsCard.vuesrc/components/dashboard/DevicesStats.vuesrc/components/dashboard/UpdateStatsCard.vuesrc/components/dashboard/Usage.vuesrc/pages/app/[package].vuesrc/pages/app/index.vuesrc/pages/dashboard.vuesrc/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.tssrc/components/dashboard/DevicesStats.vuesrc/components/dashboard/DeploymentStatsCard.vuesrc/components/dashboard/UpdateStatsCard.vuesrc/components/dashboard/BundleUploadsCard.vuesrc/pages/dashboard.vuesrc/pages/app/index.vuesrc/pages/app/[package].vuesrc/pages/settings/organization/Plans.vuesrc/components/dashboard/Usage.vuesrc/components/PaymentRequiredModal.vue
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use TypeScript strict mode with path aliases mapping
~/tosrc/
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_gofor native mobile functionality
Files:
src/components.d.tssrc/components/dashboard/DevicesStats.vuesrc/components/dashboard/DeploymentStatsCard.vuesrc/components/dashboard/UpdateStatsCard.vuesrc/components/dashboard/BundleUploadsCard.vuesrc/pages/dashboard.vuesrc/pages/app/index.vuesrc/pages/app/[package].vuesrc/pages/settings/organization/Plans.vuesrc/components/dashboard/Usage.vuesrc/components/PaymentRequiredModal.vue
src/**/*.{ts,tsx,vue,js}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use
~/alias for imports fromsrc/directory in frontend TypeScript and Vue components
Files:
src/components.d.tssrc/components/dashboard/DevicesStats.vuesrc/components/dashboard/DeploymentStatsCard.vuesrc/components/dashboard/UpdateStatsCard.vuesrc/components/dashboard/BundleUploadsCard.vuesrc/pages/dashboard.vuesrc/pages/app/index.vuesrc/pages/app/[package].vuesrc/pages/settings/organization/Plans.vuesrc/components/dashboard/Usage.vuesrc/components/PaymentRequiredModal.vue
src/**/*.{vue,ts,js}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Frontend ESLint must pass before commit; run
bun lint:fixto auto-fix issues in frontend files
Files:
src/components.d.tssrc/components/dashboard/DevicesStats.vuesrc/components/dashboard/DeploymentStatsCard.vuesrc/components/dashboard/UpdateStatsCard.vuesrc/components/dashboard/BundleUploadsCard.vuesrc/pages/dashboard.vuesrc/pages/app/index.vuesrc/pages/app/[package].vuesrc/pages/settings/organization/Plans.vuesrc/components/dashboard/Usage.vuesrc/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
konstaanywhere else in the app.
Files:
src/components.d.tssrc/components/dashboard/DevicesStats.vuesrc/components/dashboard/DeploymentStatsCard.vuesrc/components/dashboard/UpdateStatsCard.vuesrc/components/dashboard/BundleUploadsCard.vuesrc/pages/dashboard.vuesrc/pages/app/index.vuesrc/pages/app/[package].vuesrc/pages/settings/organization/Plans.vuesrc/components/dashboard/Usage.vuesrc/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
UseuseRoute()fromvue-routerto access route parameters anduseRouter()for programmatic navigation in Vue components
Files:
src/components/dashboard/DevicesStats.vuesrc/components/dashboard/DeploymentStatsCard.vuesrc/components/dashboard/UpdateStatsCard.vuesrc/components/dashboard/BundleUploadsCard.vuesrc/pages/dashboard.vuesrc/pages/app/index.vuesrc/pages/app/[package].vuesrc/pages/settings/organization/Plans.vuesrc/components/dashboard/Usage.vuesrc/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.vuesrc/components/dashboard/DeploymentStatsCard.vuesrc/components/dashboard/UpdateStatsCard.vuesrc/components/dashboard/BundleUploadsCard.vuesrc/components/dashboard/Usage.vuesrc/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.vuesrc/components/dashboard/DeploymentStatsCard.vuesrc/components/dashboard/UpdateStatsCard.vuesrc/components/dashboard/BundleUploadsCard.vuesrc/pages/dashboard.vuesrc/pages/app/index.vuesrc/pages/app/[package].vuesrc/pages/settings/organization/Plans.vuesrc/components/dashboard/Usage.vuesrc/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.vuesrc/components/dashboard/DeploymentStatsCard.vuesrc/components/dashboard/UpdateStatsCard.vuesrc/components/dashboard/BundleUploadsCard.vuesrc/pages/dashboard.vuesrc/pages/app/index.vuesrc/pages/app/[package].vuesrc/pages/settings/organization/Plans.vuesrc/components/dashboard/Usage.vuesrc/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 fromsrc/styles/style.css(e.g.,--color-primary-500: #515271) when introducing new UI.
Files:
src/components/dashboard/DevicesStats.vuesrc/components/dashboard/DeploymentStatsCard.vuesrc/components/dashboard/UpdateStatsCard.vuesrc/components/dashboard/BundleUploadsCard.vuesrc/pages/dashboard.vuesrc/pages/app/index.vuesrc/pages/app/[package].vuesrc/pages/settings/organization/Plans.vuesrc/components/dashboard/Usage.vuesrc/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 withunplugin-vue-routerand types are available insrc/typed-router.d.ts
Files:
src/pages/dashboard.vuesrc/pages/app/index.vuesrc/pages/app/[package].vuesrc/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.vuesrc/components/dashboard/DeploymentStatsCard.vuesrc/components/dashboard/UpdateStatsCard.vuesrc/components/dashboard/BundleUploadsCard.vuesrc/pages/dashboard.vuesrc/pages/app/index.vuesrc/pages/app/[package].vuesrc/pages/settings/organization/Plans.vuesrc/components/dashboard/Usage.vuesrc/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].vuesrc/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
PaymentRequiredModalfollow 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
forceDemoprop implementation is clean and follows Vue 3 conventions. The short-circuit evaluation inisDemoModecorrectly prioritizesforceDemobefore 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:
paymentFailedonly triggers when there's a subscription issue without a security compliance problemshouldBlurContentproperly aggregates both empty state and payment failure scenarios
90-96: LGTM!The conditional rendering correctly shows
FailedCardonly for security access issues while applying blur and showing thePaymentRequiredModalfor payment failures. The:force-demoprop is correctly passed to theUsagecomponent.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:
DeploymentBanneris 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:
StepsAppis hidden during payment failure to prevent onboarding actions- The blur and disabled state correctly prevent interactions with demo data
PaymentRequiredModalis properly positioned within the relative container
41-74: The code has already been addressed with theas 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 thedisplayAppscomputed 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
forceDemoprop 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 dataonMounted()correctly triggers immediate load whenforceDemois true, bypassing thedashboardFetchedcheckAlso 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.
| forceDemo: { | ||
| type: Boolean, | ||
| default: false, | ||
| }, |
There was a problem hiding this comment.
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"> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for z-index usage across Vue components to identify potential conflicts
rg -nP --type=vue 'z-\d+' -C2Repository: 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' -C2Repository: 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+' -C2Repository: 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.
|
* 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>


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
Changes
PaymentRequiredModal.vuecomponent with amber/orange designUsage.vuewithforceDemoprop to show demo chartsforceDemo🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Style
✏️ Tip: You can customize this high-level summary in your review settings.