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..63179859eb 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,18 @@ 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: 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 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 +}) // Effective values for display const effectiveBundleData = computed(() => isDemoMode.value ? demoBundleData.value : bundleData.value) @@ -116,7 +123,7 @@ const effectiveAppNames = computed(() => isDemoMode.value ? DEMO_APP_NAMES : app const effectiveTotal = computed(() => isDemoMode.value ? calculateDemoTotal(demoBundleData.value) : total.value) const effectiveLastDayEvolution = computed(() => isDemoMode.value ? calculateDemoEvolution(demoBundleData.value) : lastDayEvolution.value) -const hasData = computed(() => effectiveBundleData.value.length > 0) +const hasData = computed(() => effectiveTotal.value > 0 || isDemoMode.value) async function calculateStats(forceRefetch = false) { const startTime = Date.now() diff --git a/src/components/dashboard/DeploymentStatsCard.vue b/src/components/dashboard/DeploymentStatsCard.vue index 1c6cee6d9c..e1a4ae7a1e 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,17 @@ 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: 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 isDemoMode = computed(() => { + if (props.forceDemo) + return true + // If user has apps, never show demo data + if (dashboardAppsStore.apps.length > 0) + return false + // No apps and store is loaded = show demo + return dashboardAppsStore.isLoaded +}) // 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..e10ab1ce1a 100644 --- a/src/components/dashboard/DevicesStats.vue +++ b/src/components/dashboard/DevicesStats.vue @@ -13,6 +13,7 @@ import { useChartData } from '~/services/chartDataService' import { createTooltipConfig, todayLinePlugin, verticalLinePlugin } from '~/services/chartTooltip' import { generateChartDayLabels, getChartDateRange, normalizeToStartOfDay } from '~/services/date' import { useSupabase } from '~/services/supabase' +import { useDashboardAppsStore } from '~/stores/dashboardApps' import { useOrganizationStore } from '~/stores/organization' import ChartCard from './ChartCard.vue' @@ -421,7 +422,20 @@ const processedChartData = computed | null>(() => { } }) -const hasData = computed(() => !!(processedChartData.value && processedChartData.value.datasets.length > 0)) +// 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 + // If user has apps, never show demo data + if (dashboardAppsStore.apps.length > 0) + return false + // No apps and store is loaded = show demo + return dashboardAppsStore.isLoaded +}) + +const hasData = computed(() => !!(processedChartData.value && processedChartData.value.datasets.length > 0) || isDemoMode.value) const todayLineOptions = computed(() => { if (!props.useBillingPeriod || !currentRange.value) @@ -513,7 +527,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 +661,7 @@ watch( :title="t('active_users_by_version')" :is-loading="isLoading" :has-data="hasData" + :is-demo-data="isDemoMode" >