From 3135ff97862e3abd56a9c9e41a77e0f3943b5ad9 Mon Sep 17 00:00:00 2001 From: Martin Donadieu Date: Thu, 8 Jan 2026 17:16:59 +0000 Subject: [PATCH 01/11] fix: only show demo data when payment failed (forceDemo=true) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- src/components.d.ts | 2 + src/components/AppNotFoundModal.vue | 39 ++++++++++++++ .../dashboard/BundleUploadsCard.vue | 8 ++- .../dashboard/DeploymentStatsCard.vue | 8 ++- src/components/dashboard/DevicesStats.vue | 7 ++- src/components/dashboard/UpdateStatsCard.vue | 12 ++--- src/components/dashboard/Usage.vue | 3 ++ src/components/dashboard/UsageCard.vue | 22 +++----- src/pages/app/[package].vue | 54 ++++++++++--------- 9 files changed, 96 insertions(+), 59 deletions(-) create mode 100644 src/components/AppNotFoundModal.vue diff --git a/src/components.d.ts b/src/components.d.ts index 84bf403d8a..6304aecb5b 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -16,6 +16,7 @@ declare module 'vue' { AdminMultiLineChart: typeof import('./components/admin/AdminMultiLineChart.vue')['default'] AdminStatsCard: typeof import('./components/admin/AdminStatsCard.vue')['default'] AdminTrendChart: typeof import('./components/admin/AdminTrendChart.vue')['default'] + AppNotFoundModal: typeof import('./components/AppNotFoundModal.vue')['default'] AppSetting: typeof import('./components/dashboard/AppSetting.vue')['default'] AppTable: typeof import('./components/tables/AppTable.vue')['default'] AuditLogTable: typeof import('./components/tables/AuditLogTable.vue')['default'] @@ -80,6 +81,7 @@ declare global { const AdminMultiLineChart: typeof import('./components/admin/AdminMultiLineChart.vue')['default'] const AdminStatsCard: typeof import('./components/admin/AdminStatsCard.vue')['default'] const AdminTrendChart: typeof import('./components/admin/AdminTrendChart.vue')['default'] + const AppNotFoundModal: typeof import('./components/AppNotFoundModal.vue')['default'] const AppSetting: typeof import('./components/dashboard/AppSetting.vue')['default'] const AppTable: typeof import('./components/tables/AppTable.vue')['default'] const AuditLogTable: typeof import('./components/tables/AuditLogTable.vue')['default'] diff --git a/src/components/AppNotFoundModal.vue b/src/components/AppNotFoundModal.vue new file mode 100644 index 0000000000..1e2213f280 --- /dev/null +++ b/src/components/AppNotFoundModal.vue @@ -0,0 +1,39 @@ + + + diff --git a/src/components/dashboard/BundleUploadsCard.vue b/src/components/dashboard/BundleUploadsCard.vue index 3ef08207f6..40b3550967 100644 --- a/src/components/dashboard/BundleUploadsCard.vue +++ b/src/components/dashboard/BundleUploadsCard.vue @@ -94,9 +94,6 @@ const currentCacheOrgId = ref(null) // Cache for single app name to avoid refetching const singleAppNameCache = new Map() -// Check if we have real data -const hasRealData = computed(() => total.value > 0) - // Generate consistent demo data where total is derived from per-app breakdown const consistentDemoData = computed(() => { const days = getDemoDayCount(props.useBillingPeriod, bundleData.value.length) @@ -106,8 +103,9 @@ const consistentDemoData = computed(() => { const demoBundleData = computed(() => consistentDemoData.value.total) const demoDataByApp = computed(() => consistentDemoData.value.byApp) -// Demo mode detection - also force demo when forceDemo is true -const isDemoMode = computed(() => props.forceDemo || (!hasRealData.value && !isLoading.value)) +// Demo mode is ONLY enabled when forceDemo is true (payment failed) +// Never auto-show demo data based on empty data - users with apps should see real (even if empty) data +const isDemoMode = computed(() => props.forceDemo === true) // Effective values for display const effectiveBundleData = computed(() => isDemoMode.value ? demoBundleData.value : bundleData.value) diff --git a/src/components/dashboard/DeploymentStatsCard.vue b/src/components/dashboard/DeploymentStatsCard.vue index 1c6cee6d9c..392b9f6550 100644 --- a/src/components/dashboard/DeploymentStatsCard.vue +++ b/src/components/dashboard/DeploymentStatsCard.vue @@ -95,9 +95,6 @@ const deploymentDataByApp = ref<{ [appId: string]: number[] }>({}) const appNames = ref<{ [appId: string]: string }>({}) const isLoading = ref(true) -// Check if we have real data -const hasRealData = computed(() => totalDeployments.value > 0) - // Generate consistent demo data where total is derived from per-app breakdown const consistentDemoData = computed(() => { const days = getDemoDayCount(props.useBillingPeriod, deploymentData.value.length) @@ -107,8 +104,9 @@ const consistentDemoData = computed(() => { const demoDeploymentData = computed(() => consistentDemoData.value.total) const demoDataByApp = computed(() => consistentDemoData.value.byApp) -// Demo mode detection - also force demo when forceDemo is true -const isDemoMode = computed(() => props.forceDemo || (!hasRealData.value && !isLoading.value)) +// Demo mode is ONLY enabled when forceDemo is true (payment failed) +// Never auto-show demo data based on empty data - users with apps should see real (even if empty) data +const isDemoMode = computed(() => props.forceDemo === true) // Effective values for display const effectiveDeploymentData = computed(() => isDemoMode.value ? demoDeploymentData.value : deploymentData.value) diff --git a/src/components/dashboard/DevicesStats.vue b/src/components/dashboard/DevicesStats.vue index 80be5e4f6d..e2f7e6bf9b 100644 --- a/src/components/dashboard/DevicesStats.vue +++ b/src/components/dashboard/DevicesStats.vue @@ -423,6 +423,10 @@ const processedChartData = computed | null>(() => { const hasData = computed(() => !!(processedChartData.value && processedChartData.value.datasets.length > 0)) +// Demo mode is ONLY enabled when forceDemo is true (payment failed) +// Never auto-show demo data based on empty data - users with apps should see real (even if empty) data +const isDemoMode = computed(() => props.forceDemo === true) + const todayLineOptions = computed(() => { if (!props.useBillingPeriod || !currentRange.value) return { enabled: false } @@ -513,7 +517,7 @@ async function loadData(forceRefetch = false) { return } - // If forceDemo is true, use demo data instead of fetching + // If forceDemo is true (payment failed), use demo data instead of fetching if (props.forceDemo) { const { startDate, endDate } = getDateRange() const days = Math.floor((endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24)) + 1 @@ -647,6 +651,7 @@ watch( :title="t('active_users_by_version')" :is-loading="isLoading" :has-data="hasData" + :is-demo-data="isDemoMode" >