feat(admin): intention-based admin hubs with Tabs sub-tabs - #2975
feat(admin): intention-based admin hubs with Tabs sub-tabs#2975riderx wants to merge 15 commits into
Conversation
Reorganize Capgo admin around admin intentions (Pulse, Onboarding, Product, Retention, Customers, Revenue, Platform) using the existing Tabs primary + secondaryTabs pattern. Includes proposal write-up and visual mocks for PR review. Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThe admin dashboard is reorganized into intention-based hubs with nested routes, legacy redirects, shared loading utilities, and new onboarding, retention, platform, product, customer, and revenue views. Supporting localization, typings, tests, and screenshot capture were added. ChangesAdmin dashboard reorganization
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
Comment |
Merging this PR will not alter performance
Comparing Footnotes
|
Extract onboarding funnel/sources/cohorts and retention trials/churn/inactive pages from the monolithic users dashboard; keep users.vue as a redirect. Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
Replace fake proposal mocks with the implemented Tabs primary/secondary admin IA and capture live worktree screenshots of Pulse, Onboarding, Product, Retention, and Customers. Skip local SSO enforcement when VITE_BRANCH=local so seed admin login works without edge functions. Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
Move legacy admin path redirects into main.ts to clear Sonar duplication. Point registration-source unit tests at onboarding/sources.vue. Widen email preferences mock return types for vue-tsc. Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 3fd1769. Configure here.
There was a problem hiding this comment.
Stale comment
Risk: medium. Left a non-blocking comment — Cursor Bugbot reported 2 unresolved findings and did not complete successfully, so this is above the low-risk auto-approve threshold. Human review is needed; reviewers were assigned.
Sent by Cursor Approval Agent: Pull Request Approver External
There was a problem hiding this comment.
Actionable comments posted: 27
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/capture-admin-screens.mjs`:
- Around line 7-11: Update waitForAdmin to accept a route-specific ready
locator, verify new URL(page.url()).pathname matches the requested path after
navigation, and wait for that locator instead of the generic admin navigation
button. Update each caller to provide the expected content locator before
capturing its screenshot.
In `@src/layouts/admin.vue`:
- Around line 98-117: Update the fallback in activeSecondaryTab to return the
first key from secondaryTabs.value when no exact or prefix match exists, instead
of hubConfig[hub].base; preserve undefined when no secondary tabs are available.
- Around line 66-96: Remove the hardcoded hub conditions from activeTab and
centralize each hub’s primary route in the existing admin tab configuration.
Derive the active tab from that single mapping, ensuring every primary route is
represented in adminTabs while preserving exact, prefix, and pulse fallback
behavior.
In `@src/modules/sso-enforcement.ts`:
- Around line 60-65: Update the SSO bypass condition in the enforcement guard to
require both import.meta.env.DEV and VITE_BRANCH === 'local' before returning
next(). Preserve the existing fail-closed behavior for all production builds and
non-local branches.
In `@src/pages/admin/dashboard/customers/credits.vue`:
- Around line 245-249: Update searchOrgs so queries shorter than two characters
invalidate the active search by clearing or changing currentSearchQuery and
resetting isSearching to false before returning; preserve the existing empty
searchResults behavior and prevent any in-flight longer-query response from
passing the currentSearchQuery check and repopulating results.
- Around line 141-165: Update the monthlyCreditSummary aggregation to make
monthEndMrr order-independent: track the date associated with the selected MRR
for each month and only replace it when the current row.date is later than the
stored date. Preserve the existing creditsBought and creditsConsumed
accumulation and return shape.
In `@src/pages/admin/dashboard/onboarding/cohorts.vue`:
- Around line 114-211: Extract the shared onboarding funnel logic from
onboardingFunnelRates, onboardingFunnelConversionSummaries, and
onboardingFunnelConversionGridClass into a composable such as
useOnboardingFunnel, preserving the existing calculations, labels, activation
telemetry handling, and grid classes. Update both cohorts.vue and index.vue to
consume the composable and remove their duplicated implementations.
In `@src/pages/admin/dashboard/onboarding/index.vue`:
- Around line 450-459: Add the mainStore.isAdmin guard to both reload watchers
in src/pages/admin/dashboard/onboarding/index.vue:450-459, wrapping the
loadGlobalStatsTrend and loadOnboardingFunnel calls. Apply the same guard to
both watchers in src/pages/admin/dashboard/onboarding/cohorts.vue:213-220 around
loadOnboardingFunnel, src/pages/admin/dashboard/onboarding/sources.vue:238-249
around all three loader calls,
src/pages/admin/dashboard/revenue/upgrades.vue:183-189 around
loadGlobalStatsTrend, and src/pages/admin/dashboard/product/cli.vue:103-109
around loadCliUsage.
- Around line 104-118: Remove the debug console.log calls that dump fetched
stats payloads while preserving the console.error calls: in
src/pages/admin/dashboard/onboarding/index.vue lines 104-118, remove the log in
loadGlobalStatsTrend and the matching log in loadOnboardingFunnel; in
src/pages/admin/dashboard/onboarding/cohorts.vue lines 48-62, remove the
loader’s payload log; and in src/pages/admin/dashboard/onboarding/sources.vue
lines 68-82, remove its payload log.
- Around line 140-149: Update the series label in the chart data returned by the
visible computed flow to use the existing `t('daily-registrations')` translation
key instead of the hardcoded “Daily Registrations” string, matching the
`ChartCard` title and keeping the legend localized.
In `@src/pages/admin/dashboard/platform/capacity.vue`:
- Around line 154-205: Replace all inline English user-facing labels in the
three admin pages with i18n keys, without passing fallback text to translation
calls, and add the corresponding entries to messages/en.json. In
src/pages/admin/dashboard/platform/capacity.vue lines 154-205, use the existing
admin-pulse-builders-* and admin-pulse-builder-unreachable keys for the five
cards and add keys for the chart title, description, and Workers/Used/Free
series labels. In src/pages/admin/dashboard/product/plugins.vue lines 239-259,
add keys for the three device cards, Version Ladder headers at lines 331-341,
chart titles/descriptions, Min share, and Device Share. In
src/pages/admin/dashboard/product/updates.vue lines 78-152, add keys for the
four chart series labels and the Total Updates Today and Success Rate card text
at lines 206-240.
In `@src/pages/admin/dashboard/product/cli.vue`:
- Around line 138-160: Replace all hardcoded user-facing strings in the CLI
dashboard, including the three AdminStatsCard titles/subtitles, chart titles,
section heading/description, table headers, and empty state, with translation
keys via the existing t function. Add corresponding English entries to
messages/en.json, and do not use inline fallback text in translation calls;
preserve the existing t('cli') navigation localization.
In `@src/pages/admin/dashboard/product/plugins.vue`:
- Around line 211-226: Remove the redundant displayStore.NavTitle assignment
from the onMounted callback in
src/pages/admin/dashboard/product/plugins.vue#L211-L226, retaining the
setup-scope assignment. Apply the same change in
src/pages/admin/dashboard/product/updates.vue#L169-L184 by removing the
onMounted assignment for the updates title and keeping its setup-scope
assignment.
- Around line 400-427: Add accessible names to both FormKit controls in the
threshold filter: update the select bound to thresholdSelection with a label or
aria-label describing the minimum share, and update the customThreshold number
input with a distinct label or aria-label describing the custom threshold
percentage.
In `@src/pages/admin/dashboard/product/updates.vue`:
- Line 61: Remove the debug console.log statement that logs the complete stats
payload from the dashboard updates data-loading flow, while leaving the
surrounding statistics and date-range behavior unchanged.
- Around line 57-71: Update loadGlobalStatsTrend to use a trendRequestSeq-style
sequence guard matching notifications.vue: increment the request sequence when
starting a fetch, capture that value locally, and only apply success or error
results when the response belongs to the latest request. Keep loading-state
cleanup from stale requests from overriding the current request’s state.
In `@src/pages/admin/dashboard/pulse.vue`:
- Around line 18-54: Define canonical admin stats response types in
src/services/adminStatsTypes.ts, including OnboardingFunnelData,
GlobalStatsTrendPoint, BuilderCapacityLive, and BuilderCapacity, then remove
duplicate declarations and import required types. In
src/pages/admin/dashboard/pulse.vue#L18-L54, import OnboardingFunnelData,
GlobalStatsTrendPoint, and BuilderCapacity. In
src/pages/admin/dashboard/onboarding/index.vue#L28-L100, import
OnboardingFunnelData and GlobalStatsTrendPoint; in
src/pages/admin/dashboard/onboarding/cohorts.vue#L26-L43 and
src/pages/admin/dashboard/onboarding/sources.vue#L32-L59, import
OnboardingFunnelData while retaining only page-specific breakdown types in
sources.vue.
- Around line 137-142: Update the payingOrgs computed value to use nullish
coalescing when selecting stats.paying_orgs_total, so a legitimate zero is
preserved and only null or undefined falls back to stats.paying or zero.
In `@src/pages/admin/dashboard/retention/churn.vue`:
- Around line 103-149: Add a module-scope monotonic sequence counter for
loadCancelledOrganizations in src/pages/admin/dashboard/retention/churn.vue
(lines 103-149), increment and capture it per request, and guard writes to
cancelledOrganizations, cancelledOrganizationsTotal, and
isLoadingCancelledOrganizations so stale responses cannot update state or clear
the loading flag. Apply the same counter and guards to loadTrialOrganizations in
src/pages/admin/dashboard/retention/index.vue (lines 166-213), covering
trialOrganizations, trialOrganizationsTotal, and isLoadingTrialOrganizations.
In `@src/pages/admin/dashboard/retention/inactive.vue`:
- Around line 95-102: Update the organization_insights loading flow around the
request body and current-page handling to paginate the complete paid
organization set before applying inactivity filtering, or apply the inactivity
filter server-side before LIMIT/OFFSET. Ensure inactiveOrganizationsTotal
reflects the full filtered result and current-page supports page changes instead
of remaining fixed at 1.
In `@src/pages/admin/dashboard/retention/index.vue`:
- Line 252: Replace the hard-coded labels at the chart series definitions and
KPI card title/subtitle in the retention dashboard with `t(...)` calls. Add
corresponding translation keys and English values to `messages/en.json`, and do
not pass inline fallback text to `t`; preserve the existing label and card text
through the new keys.
- Around line 109-164: Update src/pages/admin/dashboard/retention/index.vue
lines 109-164 by changing trialOrganizationsColumns from
ref<TableColumn[]>([...]) to computed<TableColumn[]>(() => [...]), preserving
its column definitions. Update src/pages/admin/dashboard/retention/churn.vue
lines 59-101 by wrapping cancelledOrganizationsColumns in
computed<TableColumn[]>(() => [...]) and adding computed to the Vue import, so
translated headers react to locale changes.
In `@src/pages/admin/dashboard/revenue/index.vue`:
- Line 105: Remove the full global_stats_trend payload debug logging from the
revenue dashboard loader in src/pages/admin/dashboard/revenue/index.vue at lines
105-105 and the retention dashboard loader in
src/pages/admin/dashboard/retention/index.vue at lines 219-219; leave the
surrounding data-loading behavior unchanged and follow the no-logging pattern
used by the risk dashboard.
- Line 761: Replace the truthiness fallbacks in both revenue cards with
nullish-coalescing fallbacks: update the expressions at the “Total Paid
Organizations” and “Paid via Subscription” values to use `??` so a legitimate
zero is preserved while null or undefined still falls back to the alternate
metric and then zero.
In `@src/pages/admin/dashboard/revenue/risk.vue`:
- Line 59: Replace the hard-coded series labels in the chart configuration near
“New Subscriptions” and the corresponding label at line 67 with translation keys
resolved through the page’s existing t(...) helper. Add the new keys and English
values to messages/en.json, and apply the same translated labels in the
corresponding chart configuration in revenue/index.vue; do not use inline
fallback text as a translation argument.
- Around line 59-67: Add a shared pair of subscription-flow label keys to
messages/en.json, then update subscriptionFlowSeries in
src/pages/admin/dashboard/revenue/risk.vue (59-67) and
src/pages/admin/dashboard/revenue/index.vue (148-156) to use t(...) with those
keys for both labels. Do not pass inline fallback text; use the same keys in
both files to keep the chart labels localized and synchronized.
In `@src/pages/admin/dashboard/revenue/upgrades.vue`:
- Around line 136-146: Update the `need-upgrade` card’s `title` to use the
existing `t(...)` translation pattern, add the corresponding title key with its
English text to `messages/en.json`, and do not pass inline fallback text to the
translation call.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: ad6fed9a-49d1-492c-87b0-60f3dcdb03af
📒 Files selected for processing (47)
docs/pr-assets/admin-intention-ia/real-customers.webpdocs/pr-assets/admin-intention-ia/real-onboarding-cohorts.webpdocs/pr-assets/admin-intention-ia/real-onboarding-funnel.webpdocs/pr-assets/admin-intention-ia/real-onboarding-sources.webpdocs/pr-assets/admin-intention-ia/real-product-updates.webpdocs/pr-assets/admin-intention-ia/real-pulse.webpdocs/pr-assets/admin-intention-ia/real-retention.webpdocs/proposals/admin-dashboard-intention-ia.mdmessages/en.jsonscripts/capture-admin-screens.mjssrc/auto-imports.d.tssrc/components.d.tssrc/constants/adminCustomersTabs.tssrc/constants/adminOnboardingTabs.tssrc/constants/adminPlatformTabs.tssrc/constants/adminProductTabs.tssrc/constants/adminRetentionTabs.tssrc/constants/adminRevenueTabs.tssrc/constants/adminTabs.tssrc/layouts/admin.vuesrc/main.tssrc/modules/sso-enforcement.tssrc/pages/admin/dashboard/customers/credits.vuesrc/pages/admin/dashboard/customers/organizations.vuesrc/pages/admin/dashboard/index.vuesrc/pages/admin/dashboard/onboarding/cohorts.vuesrc/pages/admin/dashboard/onboarding/index.vuesrc/pages/admin/dashboard/onboarding/sources.vuesrc/pages/admin/dashboard/platform/capacity.vuesrc/pages/admin/dashboard/platform/debug.vuesrc/pages/admin/dashboard/platform/replication.vuesrc/pages/admin/dashboard/product/builder.vuesrc/pages/admin/dashboard/product/cli.vuesrc/pages/admin/dashboard/product/notifications.vuesrc/pages/admin/dashboard/product/plugins.vuesrc/pages/admin/dashboard/product/updates.vuesrc/pages/admin/dashboard/pulse.vuesrc/pages/admin/dashboard/retention/churn.vuesrc/pages/admin/dashboard/retention/inactive.vuesrc/pages/admin/dashboard/retention/index.vuesrc/pages/admin/dashboard/revenue/index.vuesrc/pages/admin/dashboard/revenue/risk.vuesrc/pages/admin/dashboard/revenue/upgrades.vuesrc/pages/admin/dashboard/users.vuesrc/route-map.d.tstests/admin-registration-source-dashboard.unit.test.tstests/email-preferences-public.unit.test.ts
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
Cap-go/capacitor-updater(manual)
💤 Files with no reviewable changes (2)
- src/pages/admin/dashboard/index.vue
- src/pages/admin/dashboard/users.vue
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
Actionable comments posted: 27
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/capture-admin-screens.mjs`:
- Around line 7-11: Update waitForAdmin to accept a route-specific ready
locator, verify new URL(page.url()).pathname matches the requested path after
navigation, and wait for that locator instead of the generic admin navigation
button. Update each caller to provide the expected content locator before
capturing its screenshot.
In `@src/layouts/admin.vue`:
- Around line 98-117: Update the fallback in activeSecondaryTab to return the
first key from secondaryTabs.value when no exact or prefix match exists, instead
of hubConfig[hub].base; preserve undefined when no secondary tabs are available.
- Around line 66-96: Remove the hardcoded hub conditions from activeTab and
centralize each hub’s primary route in the existing admin tab configuration.
Derive the active tab from that single mapping, ensuring every primary route is
represented in adminTabs while preserving exact, prefix, and pulse fallback
behavior.
In `@src/modules/sso-enforcement.ts`:
- Around line 60-65: Update the SSO bypass condition in the enforcement guard to
require both import.meta.env.DEV and VITE_BRANCH === 'local' before returning
next(). Preserve the existing fail-closed behavior for all production builds and
non-local branches.
In `@src/pages/admin/dashboard/customers/credits.vue`:
- Around line 245-249: Update searchOrgs so queries shorter than two characters
invalidate the active search by clearing or changing currentSearchQuery and
resetting isSearching to false before returning; preserve the existing empty
searchResults behavior and prevent any in-flight longer-query response from
passing the currentSearchQuery check and repopulating results.
- Around line 141-165: Update the monthlyCreditSummary aggregation to make
monthEndMrr order-independent: track the date associated with the selected MRR
for each month and only replace it when the current row.date is later than the
stored date. Preserve the existing creditsBought and creditsConsumed
accumulation and return shape.
In `@src/pages/admin/dashboard/onboarding/cohorts.vue`:
- Around line 114-211: Extract the shared onboarding funnel logic from
onboardingFunnelRates, onboardingFunnelConversionSummaries, and
onboardingFunnelConversionGridClass into a composable such as
useOnboardingFunnel, preserving the existing calculations, labels, activation
telemetry handling, and grid classes. Update both cohorts.vue and index.vue to
consume the composable and remove their duplicated implementations.
In `@src/pages/admin/dashboard/onboarding/index.vue`:
- Around line 450-459: Add the mainStore.isAdmin guard to both reload watchers
in src/pages/admin/dashboard/onboarding/index.vue:450-459, wrapping the
loadGlobalStatsTrend and loadOnboardingFunnel calls. Apply the same guard to
both watchers in src/pages/admin/dashboard/onboarding/cohorts.vue:213-220 around
loadOnboardingFunnel, src/pages/admin/dashboard/onboarding/sources.vue:238-249
around all three loader calls,
src/pages/admin/dashboard/revenue/upgrades.vue:183-189 around
loadGlobalStatsTrend, and src/pages/admin/dashboard/product/cli.vue:103-109
around loadCliUsage.
- Around line 104-118: Remove the debug console.log calls that dump fetched
stats payloads while preserving the console.error calls: in
src/pages/admin/dashboard/onboarding/index.vue lines 104-118, remove the log in
loadGlobalStatsTrend and the matching log in loadOnboardingFunnel; in
src/pages/admin/dashboard/onboarding/cohorts.vue lines 48-62, remove the
loader’s payload log; and in src/pages/admin/dashboard/onboarding/sources.vue
lines 68-82, remove its payload log.
- Around line 140-149: Update the series label in the chart data returned by the
visible computed flow to use the existing `t('daily-registrations')` translation
key instead of the hardcoded “Daily Registrations” string, matching the
`ChartCard` title and keeping the legend localized.
In `@src/pages/admin/dashboard/platform/capacity.vue`:
- Around line 154-205: Replace all inline English user-facing labels in the
three admin pages with i18n keys, without passing fallback text to translation
calls, and add the corresponding entries to messages/en.json. In
src/pages/admin/dashboard/platform/capacity.vue lines 154-205, use the existing
admin-pulse-builders-* and admin-pulse-builder-unreachable keys for the five
cards and add keys for the chart title, description, and Workers/Used/Free
series labels. In src/pages/admin/dashboard/product/plugins.vue lines 239-259,
add keys for the three device cards, Version Ladder headers at lines 331-341,
chart titles/descriptions, Min share, and Device Share. In
src/pages/admin/dashboard/product/updates.vue lines 78-152, add keys for the
four chart series labels and the Total Updates Today and Success Rate card text
at lines 206-240.
In `@src/pages/admin/dashboard/product/cli.vue`:
- Around line 138-160: Replace all hardcoded user-facing strings in the CLI
dashboard, including the three AdminStatsCard titles/subtitles, chart titles,
section heading/description, table headers, and empty state, with translation
keys via the existing t function. Add corresponding English entries to
messages/en.json, and do not use inline fallback text in translation calls;
preserve the existing t('cli') navigation localization.
In `@src/pages/admin/dashboard/product/plugins.vue`:
- Around line 211-226: Remove the redundant displayStore.NavTitle assignment
from the onMounted callback in
src/pages/admin/dashboard/product/plugins.vue#L211-L226, retaining the
setup-scope assignment. Apply the same change in
src/pages/admin/dashboard/product/updates.vue#L169-L184 by removing the
onMounted assignment for the updates title and keeping its setup-scope
assignment.
- Around line 400-427: Add accessible names to both FormKit controls in the
threshold filter: update the select bound to thresholdSelection with a label or
aria-label describing the minimum share, and update the customThreshold number
input with a distinct label or aria-label describing the custom threshold
percentage.
In `@src/pages/admin/dashboard/product/updates.vue`:
- Line 61: Remove the debug console.log statement that logs the complete stats
payload from the dashboard updates data-loading flow, while leaving the
surrounding statistics and date-range behavior unchanged.
- Around line 57-71: Update loadGlobalStatsTrend to use a trendRequestSeq-style
sequence guard matching notifications.vue: increment the request sequence when
starting a fetch, capture that value locally, and only apply success or error
results when the response belongs to the latest request. Keep loading-state
cleanup from stale requests from overriding the current request’s state.
In `@src/pages/admin/dashboard/pulse.vue`:
- Around line 18-54: Define canonical admin stats response types in
src/services/adminStatsTypes.ts, including OnboardingFunnelData,
GlobalStatsTrendPoint, BuilderCapacityLive, and BuilderCapacity, then remove
duplicate declarations and import required types. In
src/pages/admin/dashboard/pulse.vue#L18-L54, import OnboardingFunnelData,
GlobalStatsTrendPoint, and BuilderCapacity. In
src/pages/admin/dashboard/onboarding/index.vue#L28-L100, import
OnboardingFunnelData and GlobalStatsTrendPoint; in
src/pages/admin/dashboard/onboarding/cohorts.vue#L26-L43 and
src/pages/admin/dashboard/onboarding/sources.vue#L32-L59, import
OnboardingFunnelData while retaining only page-specific breakdown types in
sources.vue.
- Around line 137-142: Update the payingOrgs computed value to use nullish
coalescing when selecting stats.paying_orgs_total, so a legitimate zero is
preserved and only null or undefined falls back to stats.paying or zero.
In `@src/pages/admin/dashboard/retention/churn.vue`:
- Around line 103-149: Add a module-scope monotonic sequence counter for
loadCancelledOrganizations in src/pages/admin/dashboard/retention/churn.vue
(lines 103-149), increment and capture it per request, and guard writes to
cancelledOrganizations, cancelledOrganizationsTotal, and
isLoadingCancelledOrganizations so stale responses cannot update state or clear
the loading flag. Apply the same counter and guards to loadTrialOrganizations in
src/pages/admin/dashboard/retention/index.vue (lines 166-213), covering
trialOrganizations, trialOrganizationsTotal, and isLoadingTrialOrganizations.
In `@src/pages/admin/dashboard/retention/inactive.vue`:
- Around line 95-102: Update the organization_insights loading flow around the
request body and current-page handling to paginate the complete paid
organization set before applying inactivity filtering, or apply the inactivity
filter server-side before LIMIT/OFFSET. Ensure inactiveOrganizationsTotal
reflects the full filtered result and current-page supports page changes instead
of remaining fixed at 1.
In `@src/pages/admin/dashboard/retention/index.vue`:
- Line 252: Replace the hard-coded labels at the chart series definitions and
KPI card title/subtitle in the retention dashboard with `t(...)` calls. Add
corresponding translation keys and English values to `messages/en.json`, and do
not pass inline fallback text to `t`; preserve the existing label and card text
through the new keys.
- Around line 109-164: Update src/pages/admin/dashboard/retention/index.vue
lines 109-164 by changing trialOrganizationsColumns from
ref<TableColumn[]>([...]) to computed<TableColumn[]>(() => [...]), preserving
its column definitions. Update src/pages/admin/dashboard/retention/churn.vue
lines 59-101 by wrapping cancelledOrganizationsColumns in
computed<TableColumn[]>(() => [...]) and adding computed to the Vue import, so
translated headers react to locale changes.
In `@src/pages/admin/dashboard/revenue/index.vue`:
- Line 105: Remove the full global_stats_trend payload debug logging from the
revenue dashboard loader in src/pages/admin/dashboard/revenue/index.vue at lines
105-105 and the retention dashboard loader in
src/pages/admin/dashboard/retention/index.vue at lines 219-219; leave the
surrounding data-loading behavior unchanged and follow the no-logging pattern
used by the risk dashboard.
- Line 761: Replace the truthiness fallbacks in both revenue cards with
nullish-coalescing fallbacks: update the expressions at the “Total Paid
Organizations” and “Paid via Subscription” values to use `??` so a legitimate
zero is preserved while null or undefined still falls back to the alternate
metric and then zero.
In `@src/pages/admin/dashboard/revenue/risk.vue`:
- Line 59: Replace the hard-coded series labels in the chart configuration near
“New Subscriptions” and the corresponding label at line 67 with translation keys
resolved through the page’s existing t(...) helper. Add the new keys and English
values to messages/en.json, and apply the same translated labels in the
corresponding chart configuration in revenue/index.vue; do not use inline
fallback text as a translation argument.
- Around line 59-67: Add a shared pair of subscription-flow label keys to
messages/en.json, then update subscriptionFlowSeries in
src/pages/admin/dashboard/revenue/risk.vue (59-67) and
src/pages/admin/dashboard/revenue/index.vue (148-156) to use t(...) with those
keys for both labels. Do not pass inline fallback text; use the same keys in
both files to keep the chart labels localized and synchronized.
In `@src/pages/admin/dashboard/revenue/upgrades.vue`:
- Around line 136-146: Update the `need-upgrade` card’s `title` to use the
existing `t(...)` translation pattern, add the corresponding title key with its
English text to `messages/en.json`, and do not pass inline fallback text to the
translation call.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: ad6fed9a-49d1-492c-87b0-60f3dcdb03af
📒 Files selected for processing (47)
docs/pr-assets/admin-intention-ia/real-customers.webpdocs/pr-assets/admin-intention-ia/real-onboarding-cohorts.webpdocs/pr-assets/admin-intention-ia/real-onboarding-funnel.webpdocs/pr-assets/admin-intention-ia/real-onboarding-sources.webpdocs/pr-assets/admin-intention-ia/real-product-updates.webpdocs/pr-assets/admin-intention-ia/real-pulse.webpdocs/pr-assets/admin-intention-ia/real-retention.webpdocs/proposals/admin-dashboard-intention-ia.mdmessages/en.jsonscripts/capture-admin-screens.mjssrc/auto-imports.d.tssrc/components.d.tssrc/constants/adminCustomersTabs.tssrc/constants/adminOnboardingTabs.tssrc/constants/adminPlatformTabs.tssrc/constants/adminProductTabs.tssrc/constants/adminRetentionTabs.tssrc/constants/adminRevenueTabs.tssrc/constants/adminTabs.tssrc/layouts/admin.vuesrc/main.tssrc/modules/sso-enforcement.tssrc/pages/admin/dashboard/customers/credits.vuesrc/pages/admin/dashboard/customers/organizations.vuesrc/pages/admin/dashboard/index.vuesrc/pages/admin/dashboard/onboarding/cohorts.vuesrc/pages/admin/dashboard/onboarding/index.vuesrc/pages/admin/dashboard/onboarding/sources.vuesrc/pages/admin/dashboard/platform/capacity.vuesrc/pages/admin/dashboard/platform/debug.vuesrc/pages/admin/dashboard/platform/replication.vuesrc/pages/admin/dashboard/product/builder.vuesrc/pages/admin/dashboard/product/cli.vuesrc/pages/admin/dashboard/product/notifications.vuesrc/pages/admin/dashboard/product/plugins.vuesrc/pages/admin/dashboard/product/updates.vuesrc/pages/admin/dashboard/pulse.vuesrc/pages/admin/dashboard/retention/churn.vuesrc/pages/admin/dashboard/retention/inactive.vuesrc/pages/admin/dashboard/retention/index.vuesrc/pages/admin/dashboard/revenue/index.vuesrc/pages/admin/dashboard/revenue/risk.vuesrc/pages/admin/dashboard/revenue/upgrades.vuesrc/pages/admin/dashboard/users.vuesrc/route-map.d.tstests/admin-registration-source-dashboard.unit.test.tstests/email-preferences-public.unit.test.ts
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
Cap-go/capacitor-updater(manual)
💤 Files with no reviewable changes (2)
- src/pages/admin/dashboard/index.vue
- src/pages/admin/dashboard/users.vue
🛑 Comments failed to post (9)
src/pages/admin/dashboard/customers/credits.vue (2)
141-165: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win
monthEndMrrdepends on the row order returned by the API.The loop assigns
current.monthEndMrr = Number(row.mrr || 0)for every row in a month, so the last row processed wins. This equals the month-end value only whenglobal_stats_trendreturns rows in ascending date order. Compare dates before you overwrite the value.♻️ Proposed change to make the month-end value order independent
const monthlyBuckets = new Map<string, { date: string + monthEndDate: string creditsBought: number creditsConsumed: number monthEndMrr: number }>() for (const row of globalStatsTrendData.value) { const monthKey = toMonthKey(row.date) const current = monthlyBuckets.get(monthKey) ?? { date: monthKey, + monthEndDate: '', creditsBought: 0, creditsConsumed: 0, monthEndMrr: 0, } current.creditsBought += Number(row.credits_bought || 0) current.creditsConsumed += Number(row.credits_consumed || 0) - current.monthEndMrr = Number(row.mrr || 0) + if (row.date >= current.monthEndDate) { + current.monthEndDate = row.date + current.monthEndMrr = Number(row.mrr || 0) + } monthlyBuckets.set(monthKey, current) }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.const monthlyCreditSummary = computed(() => { const monthlyBuckets = new Map<string, { date: string monthEndDate: string creditsBought: number creditsConsumed: number monthEndMrr: number }>() for (const row of globalStatsTrendData.value) { const monthKey = toMonthKey(row.date) const current = monthlyBuckets.get(monthKey) ?? { date: monthKey, monthEndDate: '', creditsBought: 0, creditsConsumed: 0, monthEndMrr: 0, } current.creditsBought += Number(row.credits_bought || 0) current.creditsConsumed += Number(row.credits_consumed || 0) if (row.date >= current.monthEndDate) { current.monthEndDate = row.date current.monthEndMrr = Number(row.mrr || 0) } monthlyBuckets.set(monthKey, current) } return Array.from(monthlyBuckets.values()) })🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/admin/dashboard/customers/credits.vue` around lines 141 - 165, Update the monthlyCreditSummary aggregation to make monthEndMrr order-independent: track the date associated with the selected MRR for each month and only replace it when the current row.date is later than the stored date. Preserve the existing creditsBought and creditsConsumed accumulation and return shape.
245-249: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Invalidate the in-flight search when the query drops below two characters.
The early return clears
searchResultsbut leavescurrentSearchQueryat the previous longer query. A request that is still in flight for that longer query then passes thecurrentSearchQuery === querycheck at Line 268 and repopulates the dropdown after the user erased the input.isSearchingalso staystruein that window.🐛 Proposed fix to invalidate stale searches
async function searchOrgs(query: string) { if (query.length < 2) { + currentSearchQuery = query searchResults.value = [] + isSearching.value = false return }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.async function searchOrgs(query: string) { if (query.length < 2) { currentSearchQuery = query searchResults.value = [] isSearching.value = false return }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/admin/dashboard/customers/credits.vue` around lines 245 - 249, Update searchOrgs so queries shorter than two characters invalidate the active search by clearing or changing currentSearchQuery and resetting isSearching to false before returning; preserve the existing empty searchResults behavior and prevent any in-flight longer-query response from passing the currentSearchQuery check and repopulating results.src/pages/admin/dashboard/product/cli.vue (1)
138-160: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Replace the hardcoded English strings with translation keys.
This page renders user-facing text directly: the three card titles and subtitles here, the chart titles on Lines 164, 179, 194, and 210, the section heading and description on Lines 223 and 226, the table headers on Lines 234 and 237, and the empty state on Line 244. The navigation title already uses
t('cli'), so the page mixes localized and unlocalized text. Add keys for each string and updatemessages/en.json.🌐 Example for the summary cards
<AdminStatsCard - title="Total invocations" + :title="t('admin-cli-total-invocations')" :value="totalInvocations" color-class="text-primary" :is-loading="isLoadingStats" - subtitle="Selected period" + :subtitle="t('selected-period')" />As per coding guidelines: "Never pass inline fallback text as the second argument to translation calls; use translation keys and update
messages/en.json."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/admin/dashboard/product/cli.vue` around lines 138 - 160, Replace all hardcoded user-facing strings in the CLI dashboard, including the three AdminStatsCard titles/subtitles, chart titles, section heading/description, table headers, and empty state, with translation keys via the existing t function. Add corresponding English entries to messages/en.json, and do not use inline fallback text in translation calls; preserve the existing t('cli') navigation localization.Source: Coding guidelines
src/pages/admin/dashboard/product/plugins.vue (2)
211-226: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
Both pages assign
displayStore.NavTitletwice. Each page sets the title at setup scope and again at the end ofonMounted. The second assignment has no effect and duplicates the same expression.
src/pages/admin/dashboard/product/plugins.vue#L211-L226: deletedisplayStore.NavTitle = t('plugins')fromonMountedand keep the setup-scope assignment.src/pages/admin/dashboard/product/updates.vue#L169-L184: deletedisplayStore.NavTitle = t('updates')fromonMountedand keep the setup-scope assignment.📍 Affects 2 files
src/pages/admin/dashboard/product/plugins.vue#L211-L226(this comment)src/pages/admin/dashboard/product/updates.vue#L169-L184🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/admin/dashboard/product/plugins.vue` around lines 211 - 226, Remove the redundant displayStore.NavTitle assignment from the onMounted callback in src/pages/admin/dashboard/product/plugins.vue#L211-L226, retaining the setup-scope assignment. Apply the same change in src/pages/admin/dashboard/product/updates.vue#L169-L184 by removing the onMounted assignment for the updates title and keeping its setup-scope assignment.
400-427: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Add accessible names to the threshold select and the custom threshold input.
The
<span>Min share</span>element is not associated with either control. Screen reader users receive no name for the select or the number input. Addlabeloraria-labelto both FormKit controls.♿ Proposed fix
<FormKit v-model="thresholdSelection" type="select" + :label="t('admin-plugins-min-share')" + label-class="sr-only" :options="[<FormKit v-model="customThreshold" type="number" + :label="t('admin-plugins-min-share-custom')" + label-class="sr-only" number="float"Based on the coding guideline "Every input, select, and textarea must have an associated accessible label or
aria-label."📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.<div class="flex flex-wrap items-center gap-2 text-xs text-slate-500 dark:text-slate-400"> <span>Min share</span> <FormKit v-model="thresholdSelection" type="select" :label="t('admin-plugins-min-share')" label-class="sr-only" :options="[ { label: '0%', value: '0' }, { label: '0.1%', value: '0.1' }, { label: '0.5%', value: '0.5' }, { label: '1%', value: '1' }, { label: '2%', value: '2' }, { label: '5%', value: '5' }, { label: 'Custom', value: 'custom' }, ]" :classes="{ outer: 'mb-0! w-[92px]', input: 'd-select d-select-sm' }" /> <div v-if="thresholdSelection === 'custom'" class="flex items-center gap-1"> <FormKit v-model="customThreshold" type="number" :label="t('admin-plugins-min-share-custom')" label-class="sr-only" number="float" :min="0" :max="100" :step="0.1" :classes="{ outer: 'mb-0! w-[80px]', input: 'd-input d-input-sm' }" /> <span>%</span> </div>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/admin/dashboard/product/plugins.vue` around lines 400 - 427, Add accessible names to both FormKit controls in the threshold filter: update the select bound to thresholdSelection with a label or aria-label describing the minimum share, and update the customThreshold number input with a distinct label or aria-label describing the custom threshold percentage.Source: Coding guidelines
src/pages/admin/dashboard/product/updates.vue (2)
57-71: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Discard stale responses in
loadGlobalStatsTrend.The watchers on lines 160-167 can start a second request before the first resolves. Without a sequence guard, an older response can overwrite a newer one, and the charts then show data for the previous date range.
src/pages/admin/dashboard/product/notifications.vuelines 43-68 already implements this guard withtrendRequestSeq. Apply the same pattern here.🐛 Proposed fix
const isLoadingGlobalStatsTrend = ref(false) +let trendRequestSeq = 0async function loadGlobalStatsTrend() { + const requestSeq = ++trendRequestSeq isLoadingGlobalStatsTrend.value = true try { const data = await adminStore.fetchStats('global_stats_trend') + if (requestSeq !== trendRequestSeq) + return globalStatsTrendData.value = data || [] } catch (error) { + if (requestSeq !== trendRequestSeq) + return console.error('[Admin Dashboard Updates] Error loading global stats trend:', error) globalStatsTrendData.value = [] } finally { - isLoadingGlobalStatsTrend.value = false + if (requestSeq === trendRequestSeq) + isLoadingGlobalStatsTrend.value = false } }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.let trendRequestSeq = 0 async function loadGlobalStatsTrend() { const requestSeq = ++trendRequestSeq isLoadingGlobalStatsTrend.value = true try { const data = await adminStore.fetchStats('global_stats_trend') if (requestSeq !== trendRequestSeq) return console.log('[Admin Dashboard Updates] Global stats trend data:', data) globalStatsTrendData.value = data || [] } catch (error) { if (requestSeq !== trendRequestSeq) return console.error('[Admin Dashboard Updates] Error loading global stats trend:', error) globalStatsTrendData.value = [] } finally { if (requestSeq === trendRequestSeq) isLoadingGlobalStatsTrend.value = false } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/admin/dashboard/product/updates.vue` around lines 57 - 71, Update loadGlobalStatsTrend to use a trendRequestSeq-style sequence guard matching notifications.vue: increment the request sequence when starting a fetch, capture that value locally, and only apply success or error results when the response belongs to the latest request. Keep loading-state cleanup from stale requests from overriding the current request’s state.
61-61: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
Remove the debug
console.log.This statement logs the complete platform statistics payload on every load and on every date-range change. Delete it.
♻️ Proposed fix
const data = await adminStore.fetchStats('global_stats_trend') - console.log('[Admin Dashboard Updates] Global stats trend data:', data) globalStatsTrendData.value = data || []📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.const data = await adminStore.fetchStats('global_stats_trend') globalStatsTrendData.value = data || []🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/admin/dashboard/product/updates.vue` at line 61, Remove the debug console.log statement that logs the complete stats payload from the dashboard updates data-loading flow, while leaving the surrounding statistics and date-range behavior unchanged.src/pages/admin/dashboard/revenue/index.vue (2)
105-105: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
Debug logging of the full
global_stats_trendpayload remains in two dashboards. Each loader logs the entire response on every mount, date-range change, and refresh, which writes aggregate revenue, MRR, LTV, and customer counts to the browser console.src/pages/admin/dashboard/revenue/risk.vueloads the same metric without logging, which is the pattern to follow.
src/pages/admin/dashboard/revenue/index.vue#L105-L105: delete theconsole.log('[Admin Dashboard Revenue] Global stats trend data:', data)statement.src/pages/admin/dashboard/retention/index.vue#L219-L219: delete theconsole.log('[Admin Dashboard Retention Trials] Global stats trend data:', data)statement.📍 Affects 2 files
src/pages/admin/dashboard/revenue/index.vue#L105-L105(this comment)src/pages/admin/dashboard/retention/index.vue#L219-L219🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/admin/dashboard/revenue/index.vue` at line 105, Remove the full global_stats_trend payload debug logging from the revenue dashboard loader in src/pages/admin/dashboard/revenue/index.vue at lines 105-105 and the retention dashboard loader in src/pages/admin/dashboard/retention/index.vue at lines 219-219; leave the surrounding data-loading behavior unchanged and follow the no-logging pattern used by the risk dashboard.
761-761: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Use
??so a real count of zero is not replaced by a different metric.
latestGlobalStats.paying_orgs_total || latestGlobalStats.paying || 0treats a genuine0as missing and falls back topaying. The card then reports the total paying count under the "Total Paid Organizations" heading. Line 777 has the same defect for "Paid via Subscription". Use??, which only falls back when the field isnullorundefined.🐛 Proposed fix for both cards
- {{ formatNumberValue(latestGlobalStats.paying_orgs_total || latestGlobalStats.paying || 0) }} + {{ formatNumberValue(latestGlobalStats.paying_orgs_total ?? latestGlobalStats.paying ?? 0) }} @@ - {{ formatNumberValue(latestGlobalStats.paying_orgs_subscription || latestGlobalStats.paying || 0) }} + {{ formatNumberValue(latestGlobalStats.paying_orgs_subscription ?? latestGlobalStats.paying ?? 0) }}Also applies to: 777-777
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/admin/dashboard/revenue/index.vue` at line 761, Replace the truthiness fallbacks in both revenue cards with nullish-coalescing fallbacks: update the expressions at the “Total Paid Organizations” and “Paid via Subscription” values to use `??` so a legitimate zero is preserved while null or undefined still falls back to the alternate metric and then zero.
There was a problem hiding this comment.
All reported issues were addressed across 47 files
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Re-trigger cubic
Gate local SSO bypass on DEV, centralize hub primary routes, and fall back secondary tabs to the first key. Extract shared onboarding funnel metrics and organization insight types. Guard admin reload watchers, reset churn pagination, page inactive orgs, drop debug logs, and move hardcoded labels into i18n keys. Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
Add request-sequence guards for capacity and revenue risk stats. Avoid starting capacity polling after unmount. Remove unreachable trailing-slash admin dashboard redirect. Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
There was a problem hiding this comment.
Stale comment
Risk: medium. Left a non-blocking comment — broad admin IA reorg (50 files) with SSO-gate and routing changes exceeds the low-risk auto-approve threshold; Cursor Bugbot was not present on this head after the first poll. Human review is needed; reviewers were already assigned, so none were added.
Sent by Cursor Approval Agent: Pull Request Approver External
There was a problem hiding this comment.
All reported issues were addressed across 24 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
Extract shared revenue panels/composables and admin stats types. Harden loaders, fix a11y/i18n/watchers, and migrate trend loaders onto useAdminGlobalStatsTrend so new-code duplication drops under the Sonar gate. Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
There was a problem hiding this comment.
Stale comment
Risk: medium. Left a non-blocking comment — broad admin IA reorg (56 files) with SSO-gate and routing changes exceeds the low-risk auto-approve threshold; Cursor Bugbot was not present on this head after the first poll. Human review is needed; reviewers were already assigned, so none were added.
Sent by Cursor Approval Agent: Pull Request Approver External
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/pages/admin/dashboard/onboarding/index.vue (1)
40-52: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winPrevent stale onboarding funnel responses from replacing current data.
A prior request can finish after a newer date-range request. It then replaces the funnel data and clears the loading state for the current selection. Add a per-page request sequence and ignore stale success, error, and
finallyupdates, asuseAdminGlobalStatsTrendalready does.
src/pages/admin/dashboard/onboarding/index.vue#L40-L52: increment and validate a funnel request sequence before each state update.src/pages/admin/dashboard/onboarding/cohorts.vue#L30-L43: apply the same sequence guard to funnel state and loading updates.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/admin/dashboard/onboarding/index.vue` around lines 40 - 52, In src/pages/admin/dashboard/onboarding/index.vue lines 40-52, add a per-page funnel request sequence in loadOnboardingFunnel, increment it for each request, and guard success, catch, and finally state updates so stale requests cannot replace data or clear loading for the current selection. Apply the same sequence guard to funnel data and loading updates in src/pages/admin/dashboard/onboarding/cohorts.vue lines 30-43, following the pattern used by useAdminGlobalStatsTrend.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/admin/AdminStatsCard.vue`:
- Around line 61-62: Update the interactive root element in AdminStatsCard,
including its conditional clickable class binding, to add the project-standard
DaisyUI d- button or card class while preserving the existing Tailwind layout
and interaction utilities.
- Around line 58-62: Update the root component in AdminStatsCard so the
clickable button variant contains only valid phrasing-content descendants:
replace nested div and paragraph wrappers with appropriate span elements while
preserving the existing layout, styling, and non-clickable div behavior.
In `@src/composables/useOnboardingFunnelMetrics.ts`:
- Around line 111-118: Update onboardingFunnelConversionGridClass so five
conversion summaries use five columns at the sm breakpoint, keeping the
subscribed summary on the same row; preserve the existing six- and seven-summary
layouts and the two-column base layout.
In `@src/pages/admin/dashboard/customers/organizations.vue`:
- Around line 270-280: Merge the separate watchers for
adminStore.activeDateRange and adminStore.refreshTrigger into one combined
watcher, following the existing pattern in the credits page. Preserve the admin
check, resetting to the first page for date-range changes and loading
immediately for refresh-only changes, while ensuring a single refresh triggers
only one request through loadOrganizationsImmediately.
In `@src/pages/admin/dashboard/onboarding/sources.vue`:
- Around line 233-243: Prevent stale dashboard requests from overwriting current
state by adding a request sequence or AbortController to the loader flows. In
src/pages/admin/dashboard/onboarding/sources.vue#L233-L243, guard all three
loader result updates; in src/pages/admin/dashboard/product/cli.vue#L103-L111,
guard cliUsage.value and isLoadingStats.value; and in
src/pages/admin/dashboard/product/plugins.vue#L198-L206, guard
pluginBreakdown.value and isLoadingBreakdown.value. Only commit results and
loading-state changes when the request remains current.
---
Outside diff comments:
In `@src/pages/admin/dashboard/onboarding/index.vue`:
- Around line 40-52: In src/pages/admin/dashboard/onboarding/index.vue lines
40-52, add a per-page funnel request sequence in loadOnboardingFunnel, increment
it for each request, and guard success, catch, and finally state updates so
stale requests cannot replace data or clear loading for the current selection.
Apply the same sequence guard to funnel data and loading updates in
src/pages/admin/dashboard/onboarding/cohorts.vue lines 30-43, following the
pattern used by useAdminGlobalStatsTrend.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: cab3fa21-749e-4d8f-9763-2035d06fa404
📒 Files selected for processing (32)
messages/en.jsonscripts/capture-admin-screens.mjssrc/auto-imports.d.tssrc/components.d.tssrc/components/admin/AdminRevenueRiskPanel.vuesrc/components/admin/AdminRevenueUpgradesPanel.vuesrc/components/admin/AdminStatsCard.vuesrc/composables/useAdminGlobalStatsTrend.tssrc/composables/useAdminRevenueDashboard.tssrc/composables/useOnboardingFunnelMetrics.tssrc/constants/adminHubs.tssrc/layouts/admin.vuesrc/main.tssrc/modules/sso-enforcement.tssrc/pages/admin/dashboard/customers/credits.vuesrc/pages/admin/dashboard/customers/organizations.vuesrc/pages/admin/dashboard/onboarding/cohorts.vuesrc/pages/admin/dashboard/onboarding/index.vuesrc/pages/admin/dashboard/onboarding/sources.vuesrc/pages/admin/dashboard/platform/capacity.vuesrc/pages/admin/dashboard/product/cli.vuesrc/pages/admin/dashboard/product/plugins.vuesrc/pages/admin/dashboard/product/updates.vuesrc/pages/admin/dashboard/pulse.vuesrc/pages/admin/dashboard/retention/churn.vuesrc/pages/admin/dashboard/retention/inactive.vuesrc/pages/admin/dashboard/retention/index.vuesrc/pages/admin/dashboard/revenue/index.vuesrc/pages/admin/dashboard/revenue/risk.vuesrc/pages/admin/dashboard/revenue/upgrades.vuesrc/services/adminOrganizationInsights.tssrc/services/adminStatsTypes.ts
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
Cap-go/capacitor-updater(manual)
💤 Files with no reviewable changes (1)
- src/main.ts
There was a problem hiding this comment.
All reported issues were addressed across 26 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
Re-export onboarding funnel types from backend pg utils, share org insight columns and admin reload helpers, and fix AdminStatsCard keyboard/DaisyUI semantics so duplication stays under the gate. Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
There was a problem hiding this comment.
Stale comment
Risk: medium. Left a non-blocking comment — broad admin IA reorg (56 files) with SSO-gate and routing changes exceeds the low-risk auto-approve threshold; Cursor Bugbot was not present on this head after the first poll. Human review is needed; reviewers were already assigned, so none were added.
Sent by Cursor Approval Agent: Pull Request Approver External
Share chart series types, clear stale inactive truncation, localize upgrade empty-rate display, and keep AdminStatsCard keyboard-safe. Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/composables/useAdminStatsReload.ts`:
- Around line 37-42: Update the loading flow in the useAdminStatsReload
composable so the isLoading ref is always reset to false in a finally block,
including when the optional load function rejects. Preserve the existing
behavior of setting isLoading to true before invoking load and only invoke load
when provided.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 7ec7e7a3-a1b8-4fbb-8a3f-974e0936bafc
📒 Files selected for processing (19)
src/auto-imports.d.tssrc/components/admin/AdminRevenueRiskPanel.vuesrc/components/admin/AdminRevenueUpgradesPanel.vuesrc/components/admin/AdminStatsCard.vuesrc/composables/useAdminGlobalStatsTrend.tssrc/composables/useAdminRevenueDashboard.tssrc/composables/useAdminStatsReload.tssrc/composables/useOnboardingFunnelMetrics.tssrc/pages/admin/dashboard/customers/organizations.vuesrc/pages/admin/dashboard/onboarding/sources.vuesrc/pages/admin/dashboard/product/cli.vuesrc/pages/admin/dashboard/product/plugins.vuesrc/pages/admin/dashboard/product/updates.vuesrc/pages/admin/dashboard/retention/inactive.vuesrc/pages/admin/dashboard/revenue/index.vuesrc/pages/admin/dashboard/revenue/risk.vuesrc/pages/admin/dashboard/revenue/upgrades.vuesrc/services/adminOrganizationInsightColumns.tssrc/services/adminStatsTypes.ts
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
Cap-go/capacitor-updater(manual)
There was a problem hiding this comment.
All reported issues were addressed across 19 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
messages/en.json (1)
2072-2072: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winRemove the internal TODO from the rendered translation.
This string is user-visible in the admin dashboard. Replace
TODO —with a user-facing status message.Proposed copy
- "plans-analytics-checkout-completion-description": "TODO — this graph will be implemented after reliable checkout-completion tracking is available.", + "plans-analytics-checkout-completion-description": "Checkout completion data is not available yet. This graph will be added after reliable tracking is available.",🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@messages/en.json` at line 2072, Update the plans-analytics-checkout-completion-description translation to remove the internal “TODO —” prefix and replace it with user-facing wording, while preserving the message’s explanation about checkout-completion tracking availability.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/pages/admin/dashboard/onboarding/frontend.vue`:
- Around line 67-68: Update the funnel data used by funnelStages and
funnelSummaries, plus the largest-drop-off rendering around the referenced
sections, to derive labels from each stage.key through the i18n translation
system instead of using the fixed stage.label. Add the corresponding English
stage-label entries to messages/en.json and propagate those keys and
translations through the locale catalogs, preserving existing chart and summary
behavior.
In `@src/pages/admin/dashboard/revenue/plans.vue`:
- Around line 307-314: Update the documentation anchor in the plans page by
adding the configured DaisyUI d-link class to its existing class list, while
preserving the existing hover:no-underline utility and other styling.
---
Outside diff comments:
In `@messages/en.json`:
- Line 2072: Update the plans-analytics-checkout-completion-description
translation to remove the internal “TODO —” prefix and replace it with
user-facing wording, while preserving the message’s explanation about
checkout-completion tracking availability.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 4f5d0cac-3117-4993-88c9-af42c348924f
📒 Files selected for processing (13)
messages/en.context.jsonmessages/en.jsonsrc/auto-imports.d.tssrc/composables/useAdminStatsReload.tssrc/constants/adminOnboardingTabs.tssrc/constants/adminRevenueTabs.tssrc/main.tssrc/modules/sso-enforcement.tssrc/pages/admin/dashboard/onboarding/frontend.vuesrc/pages/admin/dashboard/revenue/plans.vuesrc/route-map.d.tstests/admin-frontend-onboarding-dashboard.unit.test.tstests/admin-plans-analytics-dashboard.unit.test.ts
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
Cap-go/capacitor-updater(manual)
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
messages/en.json (1)
2072-2072: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winRemove the internal TODO from the rendered translation.
This string is user-visible in the admin dashboard. Replace
TODO —with a user-facing status message.Proposed copy
- "plans-analytics-checkout-completion-description": "TODO — this graph will be implemented after reliable checkout-completion tracking is available.", + "plans-analytics-checkout-completion-description": "Checkout completion data is not available yet. This graph will be added after reliable tracking is available.",🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@messages/en.json` at line 2072, Update the plans-analytics-checkout-completion-description translation to remove the internal “TODO —” prefix and replace it with user-facing wording, while preserving the message’s explanation about checkout-completion tracking availability.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/pages/admin/dashboard/onboarding/frontend.vue`:
- Around line 67-68: Update the funnel data used by funnelStages and
funnelSummaries, plus the largest-drop-off rendering around the referenced
sections, to derive labels from each stage.key through the i18n translation
system instead of using the fixed stage.label. Add the corresponding English
stage-label entries to messages/en.json and propagate those keys and
translations through the locale catalogs, preserving existing chart and summary
behavior.
In `@src/pages/admin/dashboard/revenue/plans.vue`:
- Around line 307-314: Update the documentation anchor in the plans page by
adding the configured DaisyUI d-link class to its existing class list, while
preserving the existing hover:no-underline utility and other styling.
---
Outside diff comments:
In `@messages/en.json`:
- Line 2072: Update the plans-analytics-checkout-completion-description
translation to remove the internal “TODO —” prefix and replace it with
user-facing wording, while preserving the message’s explanation about
checkout-completion tracking availability.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 4f5d0cac-3117-4993-88c9-af42c348924f
📒 Files selected for processing (13)
messages/en.context.jsonmessages/en.jsonsrc/auto-imports.d.tssrc/composables/useAdminStatsReload.tssrc/constants/adminOnboardingTabs.tssrc/constants/adminRevenueTabs.tssrc/main.tssrc/modules/sso-enforcement.tssrc/pages/admin/dashboard/onboarding/frontend.vuesrc/pages/admin/dashboard/revenue/plans.vuesrc/route-map.d.tstests/admin-frontend-onboarding-dashboard.unit.test.tstests/admin-plans-analytics-dashboard.unit.test.ts
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
Cap-go/capacitor-updater(manual)
🛑 Comments failed to post (2)
src/pages/admin/dashboard/onboarding/frontend.vue (1)
67-68: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Localize funnel stage labels before rendering.
FrontendOnboardingAnalytics.funnel[].labelcontains fixed English text. These lines send that text to the funnel chart, summary cards, and largest-drop-off message. The page therefore shows English stage labels in non-English locales.Map
stage.keyto translation keys before rendering these values. Add the English values tomessages/en.jsonand update the locale catalogs.Also applies to: 84-92, 186-195
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/admin/dashboard/onboarding/frontend.vue` around lines 67 - 68, Update the funnel data used by funnelStages and funnelSummaries, plus the largest-drop-off rendering around the referenced sections, to derive labels from each stage.key through the i18n translation system instead of using the fixed stage.label. Add the corresponding English stage-label entries to messages/en.json and propagate those keys and translations through the locale catalogs, preserving existing chart and summary behavior.src/pages/admin/dashboard/revenue/plans.vue (1)
307-314: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash set -eu printf '%s\n' '--- target file context ---' sed -n '285,325p' src/pages/admin/dashboard/revenue/plans.vue printf '%s\n' '--- DaisyUI/config references ---' rg -n --hidden -g '!node_modules' -g '!dist' -g '!build' 'd-link|DaisyUI|daisyui|link-primary|class="[^"]*\blink\b' . | head -200 printf '%s\n' '--- package/config files ---' find . -maxdepth 2 -type f \( -name 'package.json' -o -name 'bunfig.toml' -o -name 'vite.config.*' -o -name 'tailwind.config.*' -o -name 'postcss.config.*' \) -printRepository: Cap-go/capgo.app
Length of output: 9296
🏁 Script executed:
#!/bin/bash set -eu printf '%s\n' '--- DaisyUI prefix configuration ---' sed -n '25,95p' src/styles/style.css printf '%s\n' '--- project guidance ---' sed -n '745,775p' AGENTS.md sed -n '155,175p' .github/copilot-instructions.md sed -n '205,218p' DESIGN.md printf '%s\n' '--- existing d-link usage ---' sed -n '30,50p' src/components/PaymentRequiredModal.vue printf '%s\n' '--- anchor class patterns ---' rg -n --glob 'src/**/*.{vue,ts,tsx}' '<a\b|class="[^"]*(d-link|d-btn-link|link)' src | head -160Repository: Cap-go/capgo.app
Length of output: 10991
🌐 Web query:
DaisyUI 5 link component class prefix d-link documentation💡 Result:
In daisyUI 5, there is no specific link component called "d-link" by default. The standard link component uses the class name "link" [1][2]. If you are seeing "d-link" in your project, it is likely because you have configured a custom class prefix for daisyUI [3]. In daisyUI 5, you can set a global prefix for all daisyUI component classes (e.g., "d-") within your CSS file using the
@plugindirective [3][4]. For example, if you configure your daisyUI plugin as follows:@plugin"daisyui" { prefix: "d-"; } Then the standard "link" component class becomes "d-link", "link-primary" becomes "d-link-primary", and so on [3]. You can find the official documentation for the link component at the daisyUI website [1][5]. While the documentation typically displays the default class names (e.g., "link"), these will automatically reflect your configured prefix if you are using a version of the documentation or setup that supports it, or you can manually apply the prefix to the class names provided in the examples [3][4].Citations:
- 1: https://daisyui.com/components/link/
- 2: https://github.com/saadeghi/daisyui/blob/master/packages/docs/src/routes/(routes)/components/link/+page.md
- 3: https://daisyui.com/docs/config/
- 4: saadeghi/daisyui#4173
- 5: https://daisyui.com/components/link/?lang=en
Use the configured DaisyUI link class.
Add
d-linkto the anchor’s existing class list. Keep the existinghover:no-underlineutility.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/admin/dashboard/revenue/plans.vue` around lines 307 - 314, Update the documentation anchor in the plans page by adding the configured DaisyUI d-link class to its existing class list, while preserving the existing hover:no-underline utility and other styling.Source: Coding guidelines
|






Summary (AI generated)
main; folded frontend-onboarding + plans analytics into Onboarding/Revenue hubsfinally,paid-ati18n, typo, unit test paths, translation contexts, lint import orderMotivation (AI generated)
Admin IA was page-scattered. Intention hubs make ops workflows discoverable. Merge conflicts with
mainhad blockedpull_requestCI.Business Impact (AI generated)
Faster internal triage of onboarding, retention, revenue, and platform health without changing customer-facing product.
Test Plan (AI generated)
bd30e381e)Generated with AI
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Summary by CodeRabbit
New Features
Bug Fixes