Skip to content

fix(billing): mega vue hardening v3 — UX + design + a11y + V4 - #4063

Merged
PierreBrisorgueil merged 2 commits into
masterfrom
fix/billing-vue-hardening-v3
May 3, 2026
Merged

PierreBrisorgueil merged 2 commits into
masterfrom
fix/billing-vue-hardening-v3

Conversation

@PierreBrisorgueil

@PierreBrisorgueil PierreBrisorgueil commented May 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

Comprehensive Vue billing module hardening bundling UX post-payment fixes + design/a11y/V4 typography migration. Goal: module simple, polished, accessible.

Source: 4-agent audit 2026-05-02 found 21 frontend findings (1 🔴 + 7 🟠 + 8 🟡 + 4 🔵 + 1 ⚪).

Sections

A — UX hardening (🔴 + 9 issues)

Critical: successUrl strip caused silent post-payment landing — fixed via param-aware route + v-alert + URL clean. Plus dual CTA gating, inline modal for units, portal error UI, status badge mapping, meter-mode upgrade prompt, pricing skeleton, annual hint, signup error surface, meterProgress affordance honesty.

B — Design + a11y + V4 (12 issues)

text-body-2 → text-body-medium V4 migration (6 sites), gradient theme tokens, aria-live announcements, mobile fullscreen dialog, inline → scoped CSS, v-card-title cleanup, casing fix, prefers-reduced-motion guard.

Test plan

  • CI green (unit + e2e + lint)
  • Component tests for status badge × 5 statuses
  • Component test for success URL flow
  • Component test for aria-live attributes
  • Manual QA: mobile viewport on subscriptions + extrasCheckoutModal
  • Manual QA: VoiceOver on overage state

Summary by CodeRabbit

Release Notes

  • New Features

    • Signup error messages displayed in closable alert boxes
    • Loading skeleton for pricing information
    • Inline purchase modal for extra units
    • Improved accessibility with live ARIA regions
  • Bug Fixes

    • Enhanced error messaging for subscription portal actions
    • Query parameters preserved during billing redirects
  • UX Improvements

    • Visual feedback during pricing load states
    • Conditional button states for upgrade flows
    • Updated typography for better consistency

Bundles UX post-payment fixes + design/a11y/V4 migration into one PR.

UX HARDENING (🔴 + 3× 🟠 + 4× 🟡 + 2× 🔵 + ⚪)
- successUrl includes ?success=true preserved through router redirect, success
  v-alert fires post-payment + URL cleaned (close trust-breaking silent landing)
- Dual CTA "Manage Subscription" + "Upgrade" gated on highest plan
- "Buy units" opens BillingExtrasCheckoutModal inline (no /pricing nav hop)
- openPortal errors surfaced via portalError + v-alert
- subscription.status mapped to colored v-chip + actionable button on past_due/canceled
- BillingUpgradePrompt meter-mode aware (emits buy-pack vs link to /pricing)
- pricingCard skeleton + tooltip while Stripe data loading
- Annual savings hint always visible
- Signup error surfaced via v-alert (not just console.error)
- meterProgress role/cursor conditional on @click listener

DESIGN + a11y + V4 (4× 🟠 + 4× 🟡 + 2× 🔵 + ⚪)
- text-body-2 V3 alias → text-body-medium V4 (6 occurrences)
- Hex hardcoded admin gradient → Vuetify theme tokens via CSS custom properties
- aria-live="polite" on summary, "assertive" on overage chip
- v-dialog extrasCheckout :fullscreen smAndDown
- Inline styles → scoped CSS classes (cursor, max-width, legend dot, monospace)
- v-card-title typography override cleaned
- billingPlanBadgeComponent → BillingPlanBadgeComponent (PascalCase consistency)
- pricingCard hover transform respects prefers-reduced-motion
Copilot AI review requested due to automatic review settings May 3, 2026 07:18
@coderabbitai

coderabbitai Bot commented May 3, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@PierreBrisorgueil has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 48 minutes and 22 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 804efb51-c534-4241-b5fa-359cc6f88587

📥 Commits

Reviewing files that changed from the base of the PR and between a35d03e and d396da6.

📒 Files selected for processing (7)
  • src/modules/auth/tests/auth.signup.view.unit.tests.js
  • src/modules/auth/views/signup.view.vue
  • src/modules/billing/components/billing.subscriptions.component.vue
  • src/modules/billing/components/billing.upgradePrompt.component.vue
  • src/modules/billing/router/billing.router.js
  • src/modules/billing/tests/billing.subscriptions.component.unit.tests.js
  • src/modules/billing/views/billing.pricing.view.vue

Walkthrough

This PR updates billing checkout flows to redirect to the subscriptions tab with success/error messages, adds signup error alert UI, makes numerous billing component improvements for UX and accessibility, and updates typography in the notfound view.

Changes

Signup Error Handling

Layer / File(s) Summary
Data & Methods
src/modules/auth/views/signup.view.vue
Added signupError data property and signupErrorMessage(error) method to extract user-friendly messages from API error payloads (string, message/error fields, or error arrays).
Error Alert UI
src/modules/auth/views/signup.view.vue
Signup form now displays a closable error alert bound to signupError above credentials. On submission failure, the catch block sets signupError via the helper instead of logging to console.
Tests
src/modules/auth/tests/auth.signup.view.unit.tests.js
Updated existing rejection test to assert signupError state and rendered message. Added new test verifying API error payloads are surfaced in both state and UI.

Billing Checkout Success & Query Parameter Handling

Layer / File(s) Summary
Router Redirect Logic
src/modules/billing/router/billing.router.js
/billing redirect changed from static object to function that preserves incoming query parameters while enforcing tab: 'subscriptions'.
Stripe Redirect URLs
src/modules/billing/stores/billing.store.js
createCheckout success URL now routes to /users?tab=subscriptions&success=true. createExtrasCheckout success URL routes to /users?tab=subscriptions&success=true&type=extras and cancel URL routes to /pricing#units.
Subscriptions Component Success/Error Flow
src/modules/billing/components/billing.subscriptions.component.vue
Added state for paymentSuccessMessage, portalError, and extrasCheckoutDialog. New handleCheckoutSuccessQuery() method interprets redirect query params and schedules cleanup. New computed helpers (availablePlanIds, canUpgrade, subscriptionStatus*) manage plan/portal UI. Updated meter component inputs with computed overage values.
Pricing View Deep Linking
src/modules/billing/views/billing.pricing.view.vue
Added prices-loading binding to pricing card. Route hash #units now sets activeTab = 1 to deep-link into Units tab on pricing load.
Router Query Preservation Test
src/modules/app/tests/app.router.unit.tests.js
New test verifies /billing?success=true&type=extras redirects to /users with success, type, and tab query parameters intact.
Store & Component Tests
src/modules/billing/tests/billing.store.unit.tests.js, src/modules/billing/tests/billing.subscriptions.component.unit.tests.js
Updated checkout redirect URL assertions. Added tests for subscription status rendering, portal error handling, and checkout success query cleanup with timer validation.

Billing Component UI & Accessibility Improvements

Layer / File(s) Summary
Pricing Card Loading State
src/modules/billing/components/billing.pricingCard.component.vue
Added pricesLoading prop and skeleton loader display during Stripe pricing resolution. CTA now disables with tooltip ("Pricing temporarily unavailable") when pricing unavailable. New selectPlan() method guards against disabled state. Added prefers-reduced-motion style override.
Pricing Toggle Savings Caption
src/modules/billing/components/billing.pricingToggle.component.vue
Centered toggle container with "Save 20% annually" caption displayed below toggle.
Meter Progress Accessibility & Interactivity
src/modules/billing/components/billing.meterProgress.component.vue
Computed hasClickListener guards button semantics and click emission. Overage badge and summary text now have aria-live/aria-atomic attributes for screen reader updates.
Usage Bar Styling & Accessibility
src/modules/billing/components/billing.usageBar.component.vue
Meter mode summary rows use new billing-usage-bar__summary class with aria-live="polite". Admin display replaced inline gradient with admin-rainbow and billing-usage-bar__admin-track CSS classes.
Extras Ledger CSS Classes
src/modules/billing/components/billing.extrasLedger.component.vue
Amount and ID cells now use billing-extras-ledger__amount and billing-extras-ledger__truncate scoped CSS classes instead of inline styles.
Meter Breakdown Legend
src/modules/billing/components/billing.meterBreakdownChart.component.vue
Legend dot replaced inline size/radius styles with breakdown-legend-dot CSS class.
Extras Checkout Modal Styling
src/modules/billing/components/billing.extrasCheckoutModal.component.vue
Title changed from v-card-title to styled div. Pack label separator changed from em dash to hyphen. Empty-state text class updated to text-body-medium.
Upgrade Prompt Mode Support
src/modules/billing/components/billing.upgradePrompt.component.vue
Added mode prop ('subscription' or 'meter'). Meter mode shows "Buy units" button emitting buy-pack; otherwise shows "Upgrade" link to /pricing.
Component Tests
src/modules/billing/tests/billing.*.component.unit.tests.js
Enhanced mounts to stub Vuetify components (v-tooltip, v-skeleton-loader). Added tests for loading states, fullscreen responsiveness, accessibility attributes, and snapshot coverage.

Home Notfound View Typography

Layer / File(s) Summary
Typography Classes
src/modules/home/views/home.notfound.view.vue
Updated heading from text-h2 to text-display-medium and message from text-body-1 to text-body-large.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes


Possibly related PRs


Suggested labels

billing, checkout, accessibility, Tests, components, auth

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The description covers summary, sections, and test plan, but lacks several required template sections: Scope (modules impacted, cross-module impact, risk level), Validation (checkboxes for build steps), Guardrails check, and Notes for reviewers. Add missing sections: Scope (modules, cross-module impact, risk assessment), Validation checklist, Guardrails, and Notes for reviewers to align with the repository template.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title succinctly captures the main change: comprehensive Vue billing module hardening addressing UX, design, accessibility, and Vuetify V4 migration. It clearly relates to the core objective of the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/billing-vue-hardening-v3

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 48 minutes and 22 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@codacy-production

codacy-production Bot commented May 3, 2026

Copy link
Copy Markdown

Not up to standards ⛔

🔴 Issues 5 high

Alerts:
⚠ 5 issues (≤ 0 issues of at least minor severity)

Results:
5 new issues

Category Results
ErrorProne 5 high

View in Codacy

🟢 Metrics 30 complexity · 43 duplication

Metric Results
Complexity 30
Duplication 43

View in Codacy

AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.

Run reviewer

TIP This summary will be updated as you push new changes.

@codecov

codecov Bot commented May 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.51%. Comparing base (61d6897) to head (d396da6).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #4063   +/-   ##
=======================================
  Coverage   99.51%   99.51%           
=======================================
  Files          31       31           
  Lines        1034     1034           
  Branches      278      278           
=======================================
  Hits         1029     1029           
  Misses          5        5           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@codacy-production codacy-production Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR is currently not up to standards according to Codacy. While the functional additions for billing hardening are mostly present, there are critical issues regarding destructive state management in the router and brittle implementation patterns that should be addressed before merging.

Specifically, the URL cleanup logic in the subscriptions component inadvertently discards all other application state, and the method used to detect click listeners relies on Vue internals that may break in future updates. Additionally, a potential 'object Object' display bug exists in the signup error handling, which lacks sufficient unit test coverage for its branching logic.

About this PR

  • Detection of parent click listeners in BillingMeterProgressComponent relies on instance?.vnode?.props?.onClick. This is an internal Vue property that is susceptible to breaking in minor version updates. Use standard attrs or a dedicated boolean prop to signal interactivity instead.
  • The implementation of handleCheckoutSuccessQuery uses a 100ms setTimeout to clean the URL. Consider using Vue's nextTick or managing this through a router navigation guard to ensure the state is cleared predictably after the UI has updated.

Test suggestions

  • Verify query parameter preservation on /billing to /users?tab=subscriptions redirect.
  • Verify success alert rendering and subsequent URL cleanup in BillingSubscriptionsComponent.
  • Verify subscription status mapping for all 5 statuses including chip colors and recovery button text.
  • Verify 'Change Plan' CTA gating when user is on the highest available plan.
  • Verify BillingMeterProgress affordance logic (role=button and pointer) toggles based on listener presence.
  • Verify presence of aria-live='polite' and aria-live='assertive' on usage and overage regions.
  • Verify PricingCard displays v-skeleton-loader when pricesLoading prop is true.
  • Verify that API signup errors are surfaced and rendered in a closable v-alert.
  • Verify signupErrorMessage branch coverage for strings, error objects, and arrays of error messages.
Prompt proposal for missing tests
Consider implementing these tests if applicable:
1. Verify signupErrorMessage branch coverage for strings, error objects, and arrays of error messages.

TIP Improve review quality by adding custom instructions
TIP How was this review? Give us feedback

Comment thread src/modules/auth/views/signup.view.vue Outdated
Comment thread src/modules/billing/components/billing.subscriptions.component.vue Outdated
coderabbitai[bot]
coderabbitai Bot previously requested changes May 3, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 (4)
src/modules/auth/views/signup.view.vue (1)

177-199: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add JSDoc to the modified data() function.

data() was modified (new signupError state) but still has no JSDoc header; this violates the function-doc requirement for changed .vue code.

Proposed fix
 export default {
   components: {
     AuthOrganizationSetupComponent,
   },
+  /**
+   * `@desc` Initialize signup view reactive state.
+   * `@returns` {object} Component data state.
+   */
   data() {
     const theme = useTheme();
     return {

As per coding guidelines: "**/*.{js,ts,vue}: Every new or modified function must have a JSDoc header with one-line description, @param for each argument, and @returns for any non-void return value."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/auth/views/signup.view.vue` around lines 177 - 199, Add a JSDoc
header above the data() function describing its purpose (returns the component
reactive state), include an `@returns` tag specifying the returned object (e.g.,
`@returns` {{theme: *, valid: boolean, signupError: (null|*), ...}} or simply
{`@link` Object}) and, since data() has no parameters, no `@param` tags are
required; update the JSDoc to reference the new signupError state so the header
reflects the modified return shape and place it immediately above the data()
declaration in signup.view.vue.
src/modules/billing/router/billing.router.js (1)

24-32: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Document the new redirect function with JSDoc (extract to named helper).

Line 26 introduces a modified function without a JSDoc header. Extracting it to a named constant keeps route config clean and satisfies the function-doc requirement.

♻️ Suggested fix
+/**
+ * `@desc` Redirect legacy /billing route to subscriptions while preserving incoming query params.
+ * `@param` {import('vue-router').RouteLocationNormalized} to - Incoming route location.
+ * `@returns` {{path: string, query: Object}} Redirect target route.
+ */
+const redirectBillingToSubscriptions = (to) => ({ path: '/users', query: { ...to.query, tab: 'subscriptions' } });
+
 export default [
@@
   {
     path: '/billing',
     name: 'Billing',
-    redirect: (to) => ({ path: '/users', query: { ...to.query, tab: 'subscriptions' } }),
+    redirect: redirectBillingToSubscriptions,

As per coding guidelines **/*.{js,ts,vue}: Every new or modified function must have a JSDoc header with one-line description, @param for each argument, and @returns for any non-void return value (always include @returns for async functions).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/billing/router/billing.router.js` around lines 24 - 32, Extract
the inline redirect function used in the Billing route (the anonymous function
at path '/billing' that redirects to { path: '/users', query: { ...to.query,
tab: 'subscriptions' } }) into a named constant (e.g., billingRedirect) and
replace the inline function with that constant; add a JSDoc header above the new
helper with a one-line description, a `@param` for the 'to' route/location
argument, and a `@returns` describing the returned route object; ensure the
router's redirect property references billingRedirect.
src/modules/billing/views/billing.pricing.view.vue (1)

205-229: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add missing @returns in async created() JSDoc.

Line 208 is an async lifecycle method and its JSDoc should include an explicit @returns annotation for consistency/compliance.

♻️ Suggested fix
   /**
    * `@desc` Fetch billing plans and subscription data on component creation.
+   * `@returns` {Promise<void>}
    */
   async created() {

As per coding guidelines **/*.{js,ts,vue}: Every new or modified function must have a JSDoc header with one-line description, @param for each argument, and @returns for any non-void return value (always include @returns for async functions).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/billing/views/billing.pricing.view.vue` around lines 205 - 229,
The JSDoc for the async created() lifecycle method is missing an `@returns`
annotation; update the comment block above the async created() method to include
an explicit `@returns` tag (e.g. `@returns` {Promise<void>} or similar descriptive
text) since created() is declared async (ensure the tag matches the async
signature and mention Promise<void>), leaving existing description and any
`@param` tags intact; locate the async created() method that calls
this.billingStore.fetchPlans(), this.billingStore.fetchSubscription(), and
inspects this.$route.query/hash to apply the change.
src/modules/billing/components/billing.subscriptions.component.vue (1)

407-432: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add missing JSDoc contract on modified lifecycle hooks.

mounted is async and was modified but has no @returns, and the new beforeUnmount hook has no JSDoc header.

Proposed patch
   /**
    * `@desc` Fetch subscription data on mount and handle Stripe redirect query params.
    * The legacy /billing page is retired; this component is now the landing point for
    * Stripe redirects (success, cancel, packPurchased).
+   * `@returns` {Promise<void>}
    */
   async mounted() {
@@
-  beforeUnmount() {
+  /**
+   * `@desc` Clear pending checkout-success URL cleanup timer.
+   * `@returns` {void}
+   */
+  beforeUnmount() {
     if (this.successCleanupTimer) {
       clearTimeout(this.successCleanupTimer);
     }
   },

As per coding guidelines **/*.{js,ts,vue}: “Every new or modified function must have a JSDoc header with one-line description, @param for each argument, and @returns for any non-void return value (always include @returns for async functions)”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/billing/components/billing.subscriptions.component.vue` around
lines 407 - 432, Add JSDoc headers for the modified lifecycle hooks: add a
one-line description and an `@returns` tag for the async mounted() (since it now
returns a Promise), and add a one-line description plus `@returns` (void) for
beforeUnmount(); also include an explicit `@param` section (none) if your linter
requires it for functions with no args. Update the comments immediately above
the mounted() and beforeUnmount() methods in the component (referencing mounted,
beforeUnmount, successCleanupTimer, and the billingStore.fetchSubscription call)
to satisfy the rule that async functions must document `@returns` and all
new/modified functions have JSDoc.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/modules/billing/components/billing.subscriptions.component.vue`:
- Around line 440-445: The code treats query.packPurchased as truthy which
causes 'false' strings to be considered success; update the logic in the
isSuccess calculation and the ternary that sets paymentSuccessMessage to
explicitly parse packPurchased (e.g., compare query.packPurchased === 'true' or
coerce with a safe parser) instead of raw truthiness so only an explicit true
value signals success; adjust references to isSuccess, paymentSuccessMessage and
the packPurchased checks accordingly.

In `@src/modules/billing/components/billing.upgradePrompt.component.vue`:
- Around line 76-80: Add a JSDoc header above the validator function for the
`mode` prop in billing.upgradePrompt.component.vue: include a one-line
description, a `@param` tag for the `value` parameter (string) and a `@returns` tag
indicating it returns a boolean; apply this JSDoc immediately above the
validator arrow function that validates ['subscription','meter'] so the file
meets the new JSDoc requirement for modified/added functions.

---

Outside diff comments:
In `@src/modules/auth/views/signup.view.vue`:
- Around line 177-199: Add a JSDoc header above the data() function describing
its purpose (returns the component reactive state), include an `@returns` tag
specifying the returned object (e.g., `@returns` {{theme: *, valid: boolean,
signupError: (null|*), ...}} or simply {`@link` Object}) and, since data() has no
parameters, no `@param` tags are required; update the JSDoc to reference the new
signupError state so the header reflects the modified return shape and place it
immediately above the data() declaration in signup.view.vue.

In `@src/modules/billing/components/billing.subscriptions.component.vue`:
- Around line 407-432: Add JSDoc headers for the modified lifecycle hooks: add a
one-line description and an `@returns` tag for the async mounted() (since it now
returns a Promise), and add a one-line description plus `@returns` (void) for
beforeUnmount(); also include an explicit `@param` section (none) if your linter
requires it for functions with no args. Update the comments immediately above
the mounted() and beforeUnmount() methods in the component (referencing mounted,
beforeUnmount, successCleanupTimer, and the billingStore.fetchSubscription call)
to satisfy the rule that async functions must document `@returns` and all
new/modified functions have JSDoc.

In `@src/modules/billing/router/billing.router.js`:
- Around line 24-32: Extract the inline redirect function used in the Billing
route (the anonymous function at path '/billing' that redirects to { path:
'/users', query: { ...to.query, tab: 'subscriptions' } }) into a named constant
(e.g., billingRedirect) and replace the inline function with that constant; add
a JSDoc header above the new helper with a one-line description, a `@param` for
the 'to' route/location argument, and a `@returns` describing the returned route
object; ensure the router's redirect property references billingRedirect.

In `@src/modules/billing/views/billing.pricing.view.vue`:
- Around line 205-229: The JSDoc for the async created() lifecycle method is
missing an `@returns` annotation; update the comment block above the async
created() method to include an explicit `@returns` tag (e.g. `@returns`
{Promise<void>} or similar descriptive text) since created() is declared async
(ensure the tag matches the async signature and mention Promise<void>), leaving
existing description and any `@param` tags intact; locate the async created()
method that calls this.billingStore.fetchPlans(),
this.billingStore.fetchSubscription(), and inspects this.$route.query/hash to
apply the change.
🪄 Autofix (Beta)

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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: cd1b7fdc-62d9-413e-969c-97871ba4d486

📥 Commits

Reviewing files that changed from the base of the PR and between 61d6897 and a35d03e.

⛔ Files ignored due to path filters (2)
  • src/modules/billing/tests/__snapshots__/billing.meterProgress.component.unit.tests.js.snap is excluded by !**/*.snap
  • src/modules/billing/tests/__snapshots__/billing.usageBar.component.unit.tests.js.snap is excluded by !**/*.snap
📒 Files selected for processing (24)
  • src/modules/app/tests/app.router.unit.tests.js
  • src/modules/auth/tests/auth.signup.view.unit.tests.js
  • src/modules/auth/views/signup.view.vue
  • src/modules/billing/components/billing.extrasCheckoutModal.component.vue
  • src/modules/billing/components/billing.extrasLedger.component.vue
  • src/modules/billing/components/billing.meterBreakdownChart.component.vue
  • src/modules/billing/components/billing.meterProgress.component.vue
  • src/modules/billing/components/billing.pricingCard.component.vue
  • src/modules/billing/components/billing.pricingToggle.component.vue
  • src/modules/billing/components/billing.subscriptions.component.vue
  • src/modules/billing/components/billing.upgradePrompt.component.vue
  • src/modules/billing/components/billing.usageBar.component.vue
  • src/modules/billing/router/billing.router.js
  • src/modules/billing/stores/billing.store.js
  • src/modules/billing/tests/billing.extrasCheckoutModal.component.unit.tests.js
  • src/modules/billing/tests/billing.meterProgress.component.unit.tests.js
  • src/modules/billing/tests/billing.pricingCard.component.unit.tests.js
  • src/modules/billing/tests/billing.pricingToggle.component.unit.tests.js
  • src/modules/billing/tests/billing.store.unit.tests.js
  • src/modules/billing/tests/billing.subscriptions.component.unit.tests.js
  • src/modules/billing/tests/billing.upgradePrompt.component.unit.tests.js
  • src/modules/billing/tests/billing.usageBar.component.unit.tests.js
  • src/modules/billing/views/billing.pricing.view.vue
  • src/modules/home/views/home.notfound.view.vue

Comment thread src/modules/billing/components/billing.subscriptions.component.vue Outdated
Comment thread src/modules/billing/components/billing.upgradePrompt.component.vue

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the billing UX around Stripe checkout returns and improves UI consistency/accessibility across billing (and a couple adjacent) views as part of the Vuetify v4 typography migration.

Changes:

  • Updates Stripe checkout success/cancel routing to land users on the /users?tab=subscriptions billing area, preserving relevant redirect query parameters.
  • Improves billing UI/UX: inline “Buy units” modal flow for meter mode, status chip/action mapping, error surfacing, and clearer pricing loading/unavailable states.
  • Adds/updates unit tests and snapshots covering new a11y attributes, redirect/query cleanup behavior, and new UI states.

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/modules/home/views/home.notfound.view.vue Updates typography classes to v4 utilities.
src/modules/billing/views/billing.pricing.view.vue Passes pricing-loading state to cards; supports #units deep-link tab selection.
src/modules/billing/components/billing.pricingCard.component.vue Adds price skeleton + “pricing unavailable” tooltip/CTA gating.
src/modules/billing/components/billing.pricingToggle.component.vue Adds always-visible annual savings hint and refines chip logic.
src/modules/billing/components/billing.subscriptions.component.vue Handles Stripe success query, portal error UI, status chip/action mapping, inline extras modal, and “Change Plan” CTA gating.
src/modules/billing/components/billing.upgradePrompt.component.vue Adds meter-mode behavior to emit buy-pack instead of routing to pricing.
src/modules/billing/components/billing.usageBar.component.vue Migrates typography, removes inline gradients, and adds aria-live regions.
src/modules/billing/components/billing.meterProgress.component.vue Conditional interactivity semantics, aria-live regions, and click handling only when listeners exist.
src/modules/billing/components/billing.meterBreakdownChart.component.vue Replaces inline styles with scoped CSS for bar/legend dot sizing.
src/modules/billing/components/billing.extrasLedger.component.vue Moves inline styles to scoped CSS (monospace + truncation).
src/modules/billing/components/billing.extrasCheckoutModal.component.vue Adds mobile fullscreen dialog behavior and v4 typography updates.
src/modules/billing/stores/billing.store.js Updates Stripe checkout success/cancel URLs for subscription + extras flows.
src/modules/billing/router/billing.router.js Redirects /billing to /users?tab=subscriptions while preserving incoming query params.
src/modules/billing/tests/billing.store.unit.tests.js Updates expectations for new success/cancel URLs.
src/modules/billing/tests/billing.subscriptions.component.unit.tests.js Adds coverage for inline extras modal, portal error UI, status chip mapping, and success-query cleanup.
src/modules/billing/tests/billing.upgradePrompt.component.unit.tests.js Tests meter-mode buy-pack emit behavior.
src/modules/billing/tests/billing.pricingCard.component.unit.tests.js Tests skeleton and tooltip behavior for pricing load/unavailable states.
src/modules/billing/tests/billing.pricingToggle.component.unit.tests.js Updates tests for new annual hint + chip conditions.
src/modules/billing/tests/billing.meterProgress.component.unit.tests.js Adds tests for conditional button semantics + aria-live; adds snapshot.
src/modules/billing/tests/billing.usageBar.component.unit.tests.js Adds aria-live assertions + snapshot for meter overage rendering.
src/modules/billing/tests/billing.extrasCheckoutModal.component.unit.tests.js Tests fullscreen prop wiring and viewport/display behavior.
src/modules/billing/tests/snapshots/billing.meterProgress.component.unit.tests.js.snap Snapshot output for meter overage state.
src/modules/billing/tests/snapshots/billing.usageBar.component.unit.tests.js.snap Snapshot output for usage bar meter overage state.
src/modules/auth/views/signup.view.vue Surfaces signup errors via a dismissible alert; adds API error-to-message mapping helper.
src/modules/auth/tests/auth.signup.view.unit.tests.js Adds assertions for new signup error UI and message mapping.
src/modules/app/tests/app.router.unit.tests.js Verifies /billing redirect preserves Stripe success query params.

Comment thread src/modules/billing/components/billing.subscriptions.component.vue
Comment thread src/modules/billing/tests/billing.usageBar.component.unit.tests.js
…servation, JSDoc, branch tests

- Parse packPurchased strictly (=== 'true') to prevent 'false' string triggering success banner
- Preserve existing query params on router.replace; only unset success/type/packPurchased keys
- Add JSDoc to async mounted(), beforeUnmount(), data(), mode validator, async created()
- Extract billing router redirect to named helper with JSDoc
- Add signupErrorMessage entry.error fallback in errors array map
- Add branch tests: packPurchased strict/false, string data, error key, plain-string arrays
@PierreBrisorgueil
PierreBrisorgueil dismissed coderabbitai[bot]’s stale review May 3, 2026 07:32

Stale review — all issues addressed in d396da6. CodeRabbit has already re-approved the new push.

@PierreBrisorgueil
PierreBrisorgueil merged commit 802cbc4 into master May 3, 2026
6 of 7 checks passed
@PierreBrisorgueil
PierreBrisorgueil deleted the fix/billing-vue-hardening-v3 branch May 3, 2026 07:40
PierreBrisorgueil pushed a commit that referenced this pull request Jun 1, 2026
# [2.1.0](v2.0.0...v2.1.0) (2026-06-01)

### Bug Fixes

* **billing:** 4 pricing page bugs (signed-in features, hero bg, truncation, guest CTA) ([#4123](#4123)) ([bd399bd](bd399bd))
* **billing:** add aria-expanded + touch support to BillingComputeGauge components ([#4181](#4181)) ([7e7fa97](7e7fa97))
* **billing:** billing UX hardening — 5 reliability fixes ([#4079](#4079)) ([dd62b3d](dd62b3d)), closes [#4078](#4078)
* **billing:** canonical FA6 icons + meterError lifecycle + reject URL credentials ([#4082](#4082)) ([be16045](be16045)), closes [#4081](#4081)
* **billing:** clear intentId on Stripe cancel-redirect ([#4085](#4085)) ([11be9ab](11be9ab))
* **billing:** drop empty overage aria-live div + add fr overDetail key ([#4142](#4142)) ([10e5d81](10e5d81))
* **billing:** i18n migration + Intl.NumberFormat USD (audit Codex P1) ([#4069](#4069)) ([3d3a4f6](3d3a4f6))
* **billing:** mega vue hardening v3 — UX + design + a11y + V4 ([#4063](#4063)) ([802cbc4](802cbc4))
* **billing:** send {} body on portal POST to satisfy Zod PortalRequest schema ([#4137](#4137)) ([bf6b9c8](bf6b9c8))
* **billing:** send intentId UUID to close extras double-charge window ([#4084](#4084)) ([3e27c4c](3e27c4c))
* **billing:** subscription state safety + webhook lag polling (audit Codex P1) ([#4068](#4068)) ([328700b](328700b))
* **billing:** surface meterError + remove dead 409 dialog from subscriptions ([#4081](#4081)) ([61f5d53](61f5d53))
* **billing:** V5 polish — F5 polling recovery + visibility refetch + i18n residual + locale null-guard ([#4070](#4070)) ([4908adb](4908adb))
* **billing:** V6 polish — sessionStorage guard + locale BCP47 + i18n plural + NaN guard ([#4076](#4076)) ([2ad018c](2ad018c))
* **build:** drop ARG defaults for analytics_* + filter empty env vars ([#4112](#4112)) ([afd336e](afd336e)), closes [Vue#4110](https://github.com/Vue/issues/4110) [#4110](#4110)
* **build:** restore Layer 5 empty-string env override (revert [#4112](#4112) part 2) ([c9aa9e6](c9aa9e6)), closes [Vue#4110](https://github.com/Vue/issues/4110) [comes-io/trawl_vue#880](https://github.com/comes-io/trawl_vue/issues/880) [Vue#4110](https://github.com/Vue/issues/4110)
* **core:** pin CorePageHeader to content size in flex-column contexts ([#4210](#4210)) ([e182512](e182512)), closes [#4202](#4202)
* **layout:** route /pricing outside app shell — no drawer offset on signed-in ([#4124](#4124)) ([d862fbb](d862fbb))
* **legal:** cookie banner UA-detection + appName fallback to app.title ([#4113](#4113)) ([04cc70a](04cc70a)), closes [trawl_vue#876](https://github.com/trawl_vue/issues/876) [#4109](#4109)
* **legal:** cookie consent + footer polish (5 QA bugs) ([#4098](#4098)) ([a9bf0a3](a9bf0a3))
* **legal:** gate cookie banner on isMounted to prevent prerender hydration double-render ([#4109](#4109)) ([3198e48](3198e48)), closes [#app](https://github.com/pierreb-devkit/Vue/issues/app)
* **router:** redirect authenticated users to config.sign.route instead of hardcoded '/' ([#4088](#4088)) ([ca9094e](ca9094e)), closes [#4083](#4083)
* **tasks:** propagate store errors so views gate navigation on success ([#4221](#4221)) ([b029d39](b029d39)), closes [#4218](#4218)
* **ui:** chrome convergence — restore section title + surface backgrounds + homogeneous gutter ([#4188](#4188)) ([1e5fdf1](1e5fdf1))
* **users:** re-apply route tab when serverConfig.billing arrives async ([#4138](#4138)) ([87cc7b2](87cc7b2))
* **users:** refetch billing subscription on auth state change ([#4125](#4125)) ([5a3425a](5a3425a))

### Features

* **admin:** invitations management tab ([#4217](#4217)) ([61ad86c](61ad86c)), closes [invitedBy/#actions](https://github.com/pierreb-devkit/Vue/issues/actions)
* **analytics:** add identify and reset helpers, wire auth store ([#4104](#4104)) ([c4d8b87](c4d8b87))
* **auth:** invite-gated signup UI ([#4212](#4212)) ([d17a841](d17a841))
* **billing:** add 'Manage subscription' footer link in meterDrawer ([#4056](#4056)) ([#4057](#4057)) ([7d494af](7d494af))
* **billing:** align packs.component on BillingCardComponent (V4 unified schema) ([#4147](#4147)) ([7f5dd41](7f5dd41))
* **billing:** combined pool gauge + linear breakdown bars + alerts cleanup ([f5034b1](f5034b1))
* **billing:** config-driven static-content resolver (no file replace) ([e95d944](e95d944))
* **billing:** expose netRemainingRaw + overage in useMeter for negative quota display ([#4061](#4061)) ([111e64b](111e64b)), closes [#4060](#4060)
* **billing:** meter gauges display % primary, compute units in overflow tooltip ([#4139](#4139)) ([50498ba](50498ba))
* **billing:** meter UX refonte — drop drawer + subscriptions tab ([#4059](#4059)) ([891b5ae](891b5ae)), closes [#subscriptions](https://github.com/pierreb-devkit/Vue/issues/subscriptions) [#1](#1) [#2](#2) [#3](#3) [#4](#4)
* **billing:** post-grant upgrade prompt variant for depleted signupGrant ([#4128](#4128)) ([0ae8558](0ae8558))
* **billing:** pricing page redesign — multi-mode + auto-savings + sectioned features + FAQ ([#4102](#4102)) ([2c83ab6](2c83ab6))
* **billing:** pricingCard Free CTA — 'Sign up' for guests, route to /signup ([#4106](#4106)) ([cc92ac8](cc92ac8))
* **billing:** redesign Subscriptions view to match user-tabs aesthetic ([#4127](#4127)) ([15fd466](15fd466))
* **billing:** relocate billing under Organization settings ([#4175](#4175)) ([1f36137](1f36137))
* **billing:** sidenav compute gauge above sign-out row (meter mode) ([#4126](#4126)) ([fe1497f](fe1497f))
* **billing:** sidenav compute gauge redesign — button-shape above sign-out row ([#4140](#4140)) ([9d3a090](9d3a090))
* **billing:** sidenav compute gauge revamp — v-progress-circular + v-tooltip ([#4144](#4144)) ([5d60f1b](5d60f1b)), closes [#prepend](https://github.com/pierreb-devkit/Vue/issues/prepend)
* **billing:** subscriptions view 2-col layout — drop dup bar, CTA to /pricing#units ([#4141](#4141)) ([b7ef516](b7ef516)), closes [pricing#units](https://github.com/pricing/issues/units)
* **billing:** unified BillingCardComponent + annual toggle disabled state ([#4146](#4146)) ([474bb11](474bb11))
* **billing:** unified BillingCardComponent + annual toggle disabled state ([#4149](#4149)) ([d1403ff](d1403ff))
* **billing:** v4 hardening + Phase 3 polish — equivalences chips + UX gaps + a11y ([#4066](#4066)) ([dd40b2a](dd40b2a))
* **billing:** wire netRemainingRaw + overage into devkit components ([#4062](#4062)) ([61d6897](61d6897)), closes [#4061](#4061)
* **core:** reusable PageTabs component + Account view refactor ([#4183](#4183)) ([dc2504a](dc2504a))
* **core:** unified logo+title lockup in header and sidenav ([#4086](#4086)) ([ec457db](ec457db)), closes [#4083](#4083)
* **feature:** read ERRORS.md in Phase 0 before coding ([#4089](#4089)) ([ddf8f60](ddf8f60))
* **legal:** Add legal module + cookie consent (RGPD) ([#4097](#4097)) ([595e6c4](595e6c4)), closes [#3204116570](https://github.com/pierreb-devkit/Vue/issues/3204116570) [#3204116650](https://github.com/pierreb-devkit/Vue/issues/3204116650) [#1](#1) [#19](#19) [#18](#18) [#13](#13) [#11](#11) [#18](#18) [#3](#3) [#10](#10) [#12](#12) [#14](#14) [#15](#15) [#17](#17) [#20](#20) [#4](#4) [#5](#5) [#6](#6) [#7](#7) [#8](#8) [#9](#9) [#16](#16) [#3](#3) [#16](#16) [#22](#22)
* **legal:** liquid glass cookie banner — Vuetify-only with friendlier copy ([#4115](#4115)) ([3d74789](3d74789)), closes [#4114](#4114)
* **monitoring:** single-source PostHog Error Tracking (drop Sentry) ([#4118](#4118)) ([82dfca6](82dfca6))
* **organizations:** add Organization tab + rename General route to tab-addressable ([#4184](#4184)) ([fdfbf0b](fdfbf0b))
* **organizations:** soft suggestedJoin onboarding banner + recovery-screen copy ([#4176](#4176)) ([25a3ceb](25a3ceb))
* **seo:** enrich seoInjectPlugin — multi-schemas + rich SoftwareApplication + themeColor ([#4092](#4092)) ([#4111](#4111)) ([7f8bd09](7f8bd09))
* **skill/feature:** add Phase 0.0 issue claim-on-start ([#4117](#4117)) ([a582430](a582430)), closes [pierreb-projects/infra#28](https://github.com/pierreb-projects/infra/issues/28)
* **skills/update-stack:** block on undeclared drift vs upstream ([#4228](#4228)) ([ab8ee68](ab8ee68)), closes [#4227](#4227)
* **users:** subs full-width + delete account danger zone + halo full-bleed fix ([#4143](#4143)) ([981f073](981f073))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants