fix(workspace): let ended subscriptions resubscribe to the same plan - #14607
Conversation
An ended subscription keeps reporting its plan slug and tier, and the pricing table only treated a cancel-scheduled plan as re-buyable, so the plan the user just lost rendered as a disabled "Current Plan" and the other tiers read "Change to" instead of "Subscribe to". Treat subscription_status 'ended' as holding no current plan, for both the personal tier cards and the team credit stop.
📄 Knowledge reviewDosu skipped reviewing this PR because your organization has used its |
🎨 Storybook: ✅ Built — View Storybook🎭 Playwright: ✅ 1773 passed, 0 failed · 5 flaky📊 Browser Reports
📦 Bundle: 8.21 MB gzip 🔴 +37 BDetailsSummary
Category Glance App Entry Points — 3.64 kB (baseline 3.64 kB) • ⚪ 0 BMain entry bundles and manifests Status: 1 unchanged Graph Workspace — 1.29 MB (baseline 1.29 MB) • ⚪ 0 BGraph editor runtime, canvas, workflow orchestration Status: 2 unchanged Views & Navigation — 111 kB (baseline 111 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces Status: 16 unchanged Panels & Settings — 551 kB (baseline 551 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens Status: 26 unchanged User & Accounts — 28.7 kB (baseline 28.7 kB) • ⚪ 0 BAuthentication, profile, and account management bundles Status: 10 unchanged Editors & Dialogs — 124 kB (baseline 124 kB) • ⚪ 0 BModals, dialogs, drawers, and in-app editors Status: 7 unchanged UI Components — 70 kB (baseline 70 kB) • ⚪ 0 BReusable component library chunks Status: 15 unchanged Data & Services — 3.45 MB (baseline 3.45 MB) • ⚪ 0 BStores, services, APIs, and repositories Status: 17 unchanged Utilities & Hooks — 386 kB (baseline 386 kB) • ⚪ 0 BHelpers, composables, and utility bundles Status: 36 unchanged Vendor & Third-Party — 15.7 MB (baseline 15.7 MB) • ⚪ 0 BExternal libraries and shared vendor chunks Status: 16 unchanged Other — 12.8 MB (baseline 12.8 MB) • 🔴 +190 BBundles that do not match a named category
Status: 1 added / 1 removed / 280 unchanged ⚡ Performance Report
Show regressions
All metrics
Historical variance (last 15 runs)
Trend (last 15 commits on main)
Raw data{
"timestamp": "2026-08-03T17:16:34.690Z",
"gitSha": "4a3715e6d7738d5a4e7102a3f73dc6c8432cfa7f",
"branch": "fix/ended-subscription-pricing-cta",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2019.8509999999885,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.762999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 495.51599999999996,
"heapDeltaBytes": 5225988,
"heapUsedBytes": 73103088,
"domNodes": 18,
"jsHeapTotalBytes": 20574208,
"scriptDurationMs": 15.982,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "canvas-idle",
"durationMs": 2035.1860000000102,
"styleRecalcs": 8,
"styleRecalcDurationMs": 8.373999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 519.312,
"heapDeltaBytes": -21287228,
"heapUsedBytes": 46467944,
"domNodes": -281,
"jsHeapTotalBytes": 20176896,
"scriptDurationMs": 17.162,
"eventListeners": -148,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-mouse-sweep",
"durationMs": 2049.7529999999815,
"styleRecalcs": 80,
"styleRecalcDurationMs": 50.187999999999995,
"layouts": 12,
"layoutDurationMs": 3.9939999999999993,
"taskDurationMs": 1061.49,
"heapDeltaBytes": -14705928,
"heapUsedBytes": 53264400,
"domNodes": -280,
"jsHeapTotalBytes": 21225472,
"scriptDurationMs": 144.898,
"eventListeners": -148,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1852.685000000065,
"styleRecalcs": 74,
"styleRecalcDurationMs": 39.868,
"layouts": 12,
"layoutDurationMs": 3.941,
"taskDurationMs": 907.5419999999999,
"heapDeltaBytes": -20142528,
"heapUsedBytes": 47764520,
"domNodes": -280,
"jsHeapTotalBytes": 21225472,
"scriptDurationMs": 127.67899999999999,
"eventListeners": -148,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1754.3119999999703,
"styleRecalcs": 32,
"styleRecalcDurationMs": 18.038999999999994,
"layouts": 6,
"layoutDurationMs": 0.606,
"taskDurationMs": 393.25200000000007,
"heapDeltaBytes": 7851284,
"heapUsedBytes": 75872264,
"domNodes": 77,
"jsHeapTotalBytes": 20574208,
"scriptDurationMs": 20.451999999999998,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1694.7790000000396,
"styleRecalcs": 30,
"styleRecalcDurationMs": 17.971,
"layouts": 6,
"layoutDurationMs": 0.629,
"taskDurationMs": 411.99399999999997,
"heapDeltaBytes": 7936588,
"heapUsedBytes": 75834380,
"domNodes": 77,
"jsHeapTotalBytes": 20312064,
"scriptDurationMs": 21.637999999999998,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "dom-widget-clipping",
"durationMs": 576.5089999999873,
"styleRecalcs": 11,
"styleRecalcDurationMs": 7.443,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 379.596,
"heapDeltaBytes": -11869776,
"heapUsedBytes": 55830612,
"domNodes": 18,
"jsHeapTotalBytes": 21098496,
"scriptDurationMs": 59.615,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 608.6950000000115,
"styleRecalcs": 11,
"styleRecalcDurationMs": 8.174000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 396.44500000000005,
"heapDeltaBytes": -12004732,
"heapUsedBytes": 55910324,
"domNodes": 18,
"jsHeapTotalBytes": 21360640,
"scriptDurationMs": 62.712999999999994,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2018.82599999999,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.154000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 687.734,
"heapDeltaBytes": 7779852,
"heapUsedBytes": 67024240,
"domNodes": -280,
"jsHeapTotalBytes": 4780032,
"scriptDurationMs": 109.513,
"eventListeners": -142,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2025.6160000000136,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.782000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 676.0060000000001,
"heapDeltaBytes": 7664708,
"heapUsedBytes": 66362924,
"domNodes": -283,
"jsHeapTotalBytes": 5042176,
"scriptDurationMs": 106.59299999999999,
"eventListeners": -142,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "large-graph-pan",
"durationMs": 2180.316000000005,
"styleRecalcs": 68,
"styleRecalcDurationMs": 13.061000000000003,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1307.817,
"heapDeltaBytes": 5975820,
"heapUsedBytes": 66021800,
"domNodes": -285,
"jsHeapTotalBytes": 5005312,
"scriptDurationMs": 445.091,
"eventListeners": -142,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "large-graph-pan",
"durationMs": 2219.3959999999606,
"styleRecalcs": 69,
"styleRecalcDurationMs": 14.899,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1307.531,
"heapDeltaBytes": 8098732,
"heapUsedBytes": 68655904,
"domNodes": -284,
"jsHeapTotalBytes": 5005312,
"scriptDurationMs": 446.133,
"eventListeners": -144,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3280.916999999988,
"styleRecalcs": 65,
"styleRecalcDurationMs": 14.982,
"layouts": 60,
"layoutDurationMs": 7.729,
"taskDurationMs": 1522.459,
"heapDeltaBytes": 3092064,
"heapUsedBytes": 64489364,
"domNodes": -286,
"jsHeapTotalBytes": 7401472,
"scriptDurationMs": 532.9730000000001,
"eventListeners": -148,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3249.3149999999105,
"styleRecalcs": 65,
"styleRecalcDurationMs": 14.477,
"layouts": 60,
"layoutDurationMs": 7.097000000000001,
"taskDurationMs": 1513.595,
"heapDeltaBytes": 1982040,
"heapUsedBytes": 63454052,
"domNodes": -287,
"jsHeapTotalBytes": 7663616,
"scriptDurationMs": 530.671,
"eventListeners": -148,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "minimap-idle",
"durationMs": 2057.9890000000205,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.404000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 731.7529999999999,
"heapDeltaBytes": 7811440,
"heapUsedBytes": 68094260,
"domNodes": -281,
"jsHeapTotalBytes": 5042176,
"scriptDurationMs": 114.925,
"eventListeners": -146,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2026.8770000000131,
"styleRecalcs": 7,
"styleRecalcDurationMs": 6.527999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 681.403,
"heapDeltaBytes": 8515220,
"heapUsedBytes": 69255672,
"domNodes": -284,
"jsHeapTotalBytes": 3731456,
"scriptDurationMs": 109.59899999999999,
"eventListeners": -144,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 570.2180000000112,
"styleRecalcs": 46,
"styleRecalcDurationMs": 9.961999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 394.223,
"heapDeltaBytes": -11636428,
"heapUsedBytes": 56358948,
"domNodes": 18,
"jsHeapTotalBytes": 21884928,
"scriptDurationMs": 116.324,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 592.9099999999607,
"styleRecalcs": 47,
"styleRecalcDurationMs": 11.039999999999997,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 394.67800000000005,
"heapDeltaBytes": -10817240,
"heapUsedBytes": 57220024,
"domNodes": 20,
"jsHeapTotalBytes": 21360640,
"scriptDurationMs": 119.092,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 2017.0840000000112,
"styleRecalcs": 10,
"styleRecalcDurationMs": 10.651,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 513.1170000000001,
"heapDeltaBytes": -22174860,
"heapUsedBytes": 45743944,
"domNodes": 20,
"jsHeapTotalBytes": 20439040,
"scriptDurationMs": 15.373999999999999,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 2029.7540000000254,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.9430000000000005,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 486.565,
"heapDeltaBytes": 4676620,
"heapUsedBytes": 72644172,
"domNodes": 18,
"jsHeapTotalBytes": 20836352,
"scriptDurationMs": 15.382,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1740.1929999999766,
"styleRecalcs": 76,
"styleRecalcDurationMs": 35.905,
"layouts": 16,
"layoutDurationMs": 3.925,
"taskDurationMs": 839.859,
"heapDeltaBytes": -20015172,
"heapUsedBytes": 48056808,
"domNodes": -279,
"jsHeapTotalBytes": 20439040,
"scriptDurationMs": 92.221,
"eventListeners": -148,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1675.897999999961,
"styleRecalcs": 75,
"styleRecalcDurationMs": 35.742999999999995,
"layouts": 16,
"layoutDurationMs": 4.045,
"taskDurationMs": 761.927,
"heapDeltaBytes": -4787948,
"heapUsedBytes": 63023608,
"domNodes": 63,
"jsHeapTotalBytes": 21360640,
"scriptDurationMs": 90.412,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-transition-enter",
"durationMs": 1384.0579999999818,
"styleRecalcs": 17,
"styleRecalcDurationMs": 30.62799999999999,
"layouts": 13,
"layoutDurationMs": 13.388000000000003,
"taskDurationMs": 941.5279999999999,
"heapDeltaBytes": 8989448,
"heapUsedBytes": 100964840,
"domNodes": 13673,
"jsHeapTotalBytes": 9699328,
"scriptDurationMs": 34.57199999999999,
"eventListeners": 2371,
"totalBlockingTimeMs": 139,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "viewport-pan-sweep",
"durationMs": 8437.14,
"styleRecalcs": 249,
"styleRecalcDurationMs": 36.27,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4595.023999999999,
"heapDeltaBytes": 11748832,
"heapUsedBytes": 71508120,
"domNodes": -283,
"jsHeapTotalBytes": 6545408,
"scriptDurationMs": 1440.315,
"eventListeners": -128,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8303.77999999996,
"styleRecalcs": 249,
"styleRecalcDurationMs": 36.929,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4545.911,
"heapDeltaBytes": 10220124,
"heapUsedBytes": 69151768,
"domNodes": -283,
"jsHeapTotalBytes": 7593984,
"scriptDurationMs": 1422.185,
"eventListeners": -128,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 17216.478999999992,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17181.002,
"heapDeltaBytes": -64302840,
"heapUsedBytes": 167098404,
"domNodes": -8312,
"jsHeapTotalBytes": -15671296,
"scriptDurationMs": 595.529,
"eventListeners": -16385,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 17266.163000000008,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17246.349000000002,
"heapDeltaBytes": -54376196,
"heapUsedBytes": 171246072,
"domNodes": -8312,
"jsHeapTotalBytes": -5709824,
"scriptDurationMs": 564.476,
"eventListeners": -16389,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 21018.571000000007,
"styleRecalcs": 144,
"styleRecalcDurationMs": 17.35399999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 20972.475000000002,
"heapDeltaBytes": -47654376,
"heapUsedBytes": 178535276,
"domNodes": -8312,
"jsHeapTotalBytes": -12070912,
"scriptDurationMs": 888.9699999999999,
"eventListeners": -16383,
"totalBlockingTimeMs": 71,
"frameDurationMs": 18.333333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-pan",
"durationMs": 20832.178,
"styleRecalcs": 141,
"styleRecalcDurationMs": 17.087000000000018,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 20787.658,
"heapDeltaBytes": -54455140,
"heapUsedBytes": 154398248,
"domNodes": -8312,
"jsHeapTotalBytes": -12525568,
"scriptDurationMs": 873.5769999999999,
"eventListeners": -16383,
"totalBlockingTimeMs": 318,
"frameDurationMs": 17.780000000000047,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "workflow-execution",
"durationMs": 462.0360000000119,
"styleRecalcs": 14,
"styleRecalcDurationMs": 18.147,
"layouts": 2,
"layoutDurationMs": 0.4079999999999999,
"taskDurationMs": 108.37799999999999,
"heapDeltaBytes": -15705164,
"heapUsedBytes": 51291800,
"domNodes": 130,
"jsHeapTotalBytes": 6942720,
"scriptDurationMs": 9.554,
"eventListeners": 67,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "workflow-execution",
"durationMs": 479.67000000005555,
"styleRecalcs": 13,
"styleRecalcDurationMs": 19.988000000000003,
"layouts": 3,
"layoutDurationMs": 0.7390000000000001,
"taskDurationMs": 115.60900000000002,
"heapDeltaBytes": -16144448,
"heapUsedBytes": 50852044,
"domNodes": 119,
"jsHeapTotalBytes": 7467008,
"scriptDurationMs": 9.158,
"eventListeners": 65,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
}
]
} |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe pricing table now treats ended personal and team subscriptions as inactive. It presents fresh subscribe CTAs and emits new-subscription team actions. Tests mock subscription status and cover both CTA paths. ChangesEnded subscription CTA handling
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested labels: Suggested reviewers: Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 inconclusive)
✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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/platform/workspace/components/UnifiedPricingTable.test.ts`:
- Line 24: Update the mock refs in UnifiedPricingTable.test.ts, including both
occurrences of mockSubscriptionStatus, to use BillingSubscriptionStatus | null
instead of string | null; import the production BillingSubscriptionStatus type
from its existing module so the test matches the billing contract.
- Around line 127-144: Update the ended-subscription test around the subscribe
CTA to click the enabled “Subscribe to Creator Yearly” button, then assert it
emits subscribe with tierKey “creator” and billingCycle “yearly”; also assert
that no resubscribe event is emitted while preserving the existing label and
state assertions.
In `@src/platform/workspace/components/UnifiedPricingTable.vue`:
- Around line 684-686: Update the resubscribe emission around the selected stop
at the call site near line 895 to require isTeamSubscribed.value, preventing
ended team subscriptions from emitting resubscribe even when cancelled. Keep the
existing selected-stop behavior unchanged when isTeamSubscribed.value is true.
🪄 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 Plus
Run ID: 0b20ba88-96ab-4eca-b333-84d7ed0698cf
📒 Files selected for processing (2)
src/platform/workspace/components/UnifiedPricingTable.test.tssrc/platform/workspace/components/UnifiedPricingTable.vue
Address review: type the mocked subscription status with the production lifecycle union, assert the emitted subscribe payload rather than only the CTA label, and require isTeamSubscribed before emitting resubscribe now that the credit stop can outlive the subscription.
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## main #14607 +/- ##
=======================================
Coverage 79.10% 79.10%
=======================================
Files 1769 1769
Lines 118915 118923 +8
Branches 38244 38251 +7
=======================================
+ Hits 94062 94078 +16
+ Misses 24223 24215 -8
Partials 630 630
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
dante01yoon
left a comment
There was a problem hiding this comment.
Reviewed the ended-subscription lifecycle path for personal and team plans. The stale plan identifiers are now excluded consistently from current/change/resubscribe decisions, and the regression tests verify the fresh checkout payloads. All required CI checks are passing.
|
…ibe to the same plan (#14609) Manual backport of #14607 — the auto-backport workflow failed on this target with a conflict. ## Conflict resolution `src/platform/workspace/components/UnifiedPricingTable.test.ts` — the only conflict was a test rename that exists on `main` but not on `core/1.47`: `'keeps personal tier cards actionable for a team subscriber'` was later renamed to `'…for the original owner of a team plan'`. Kept this branch's existing name and inserted the new test above it. The unrelated rename was **not** backported. The component file applied without conflict. ## Original fix After a Stripe-side cancellation, access is revoked but Plans & Pricing still showed the lost plan as a disabled "Current Plan", so the user could not resubscribe to it. An ended subscription still reports its `plan_slug` and `subscription_tier`, and the pricing table only treated a cancel-scheduled plan as re-buyable. `subscription_status: 'ended'` now counts as holding no current plan. ## Verification - `UnifiedPricingTable` suite on this branch: 11 passed, including both new ended-state tests. - `vue-tsc --noEmit` and oxfmt clean.
…ibe to the same plan (#14610) Manual backport of #14607 — the auto-backport workflow failed. Cherry-pick of `10c56a1` applied cleanly; no changes beyond the original PR. ## Why this is manual The backport run ([30839173649](https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/30839173649)) hit a conflict on `core/1.47` only. That failed the whole "Backport commits" step, which skipped "Create PR for each successful backport" for the targets that *did* apply cleanly, and the cleanup step then deleted their branches. Filed as a follow-up on the workflow. ## Original fix After a Stripe-side cancellation, access is revoked but Plans & Pricing still showed the lost plan as a disabled "Current Plan", so the user could not resubscribe to it. An ended subscription still reports its `plan_slug` and `subscription_tier`, and the pricing table only treated a cancel-scheduled plan as re-buyable. `subscription_status: 'ended'` now counts as holding no current plan. ## Verification Unit tests pass on this branch; CI covers the rest.
…ribe to the same plan (#14611) Manual backport of #14607 — the auto-backport workflow failed. Cherry-pick of `10c56a1` applied cleanly; no changes beyond the original PR. ## Why this is manual The backport run ([30839173649](https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/30839173649)) hit a conflict on `core/1.47` only. That failed the whole "Backport commits" step, which skipped "Create PR for each successful backport" for the targets that *did* apply cleanly, and the cleanup step then deleted their branches. Filed as a follow-up on the workflow. ## Original fix After a Stripe-side cancellation, access is revoked but Plans & Pricing still showed the lost plan as a disabled "Current Plan", so the user could not resubscribe to it. An ended subscription still reports its `plan_slug` and `subscription_tier`, and the pricing table only treated a cancel-scheduled plan as re-buyable. `subscription_status: 'ended'` now counts as holding no current plan. ## Verification Unit tests pass on this branch; CI covers the rest.
After cancelling a subscription on Stripe's side, access is correctly revoked, but Plans & Pricing still showed the lost plan as "Current Plan" with a disabled button, so the user could not resubscribe to it. The other tiers also mislabelled as "Change to X" instead of "Subscribe to X".
Root cause
An ended subscription still reports its
plan_slugandsubscription_tieronGET /api/billing/status(onlyis_activeflips to false). The pricing table modelled just two states for the plan you hold — active (disabled "Current Plan") and cancel-scheduled (enabled "Resubscribe") — andisCancelledis derived fromsubscription_status === 'canceled'only, so anendedsubscription fell into the active bucket.This is the residual half of #14504, which fixed the same
endedblind spot in the subscription panel but not here.Change
UnifiedPricingTable.vuetreatssubscription_status: 'ended'as holding no current plan, so the CTA becomes an enabled "Subscribe to ". Applies to both the personal tier cards (isCurrentPlan,currentTierKey) and the team credit stop (isTeamSubscribed).Backend needs no change — the subscribe path already treats an ended subscription as "no subscription".
Verification
mainand pass with the fix.UnifiedPricingTablesuite: 12 passed, incl. under--coverage(matching CI).vue-tsc --noEmit, eslint, oxfmt clean.