feat(billing): role-aware run-lock for cancelled/inactive team plans (FE-978)#12786
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthroughThe PR adds member-aware subscription UI by introducing a new inactive subscription dialog for members, updating the SubscribeToRun button with permission-based labels and tooltips, routing subscription dialogs based on user permissions, and adding translation strings and comprehensive tests throughout. ChangesMember-Aware Subscription UI
Sequence Diagram(s)sequenceDiagram
participant User as User (Member)
participant Button as SubscribeToRun Button
participant Dialog as Subscription Dialog
participant InactiveDialog as SubscriptionInactiveMemberDialog
participant WorkspaceUI as Workspace Permissions
User->>Button: view button
Button->>WorkspaceUI: check canManageSubscription
WorkspaceUI-->>Button: false (member)
Button-->>User: display Run label
User->>Button: click button
Button->>Dialog: showSubscriptionDialog()
Dialog->>WorkspaceUI: check canManageSubscription
WorkspaceUI-->>Dialog: false
Dialog->>InactiveDialog: async load and show
InactiveDialog-->>User: display inactive message with CTA
User->>InactiveDialog: click close or CTA
InactiveDialog->>Dialog: onClose()
Dialog->>Dialog: hide()
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Suggested labels
Suggested reviewers
Poem
Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 warning, 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 |
🎨 Storybook: ✅ Built — View Storybook |
🎭 Playwright: ✅ 1668 passed, 0 failed · 2 flaky📊 Browser Reports
|
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/platform/cloud/subscription/components/SubscribeToRun.test.ts`:
- Around line 68-69: The test stubs the tooltip directive as a no-op so the new
role-aware tooltip branch (buttonTooltip) in SubscribeToRun is untested; update
the test in SubscribeToRun.test.ts to replace directives: { tooltip: () => {} }
with a spy/mocked implementation that records calls and then add assertions that
the component triggers the tooltip with the expected text for a member versus an
owner (i.e., exercise the buttonTooltip branch), referencing the SubscribeToRun
component and its buttonTooltip behavior to verify the correct tooltip payload
is passed when rendering under different roles.
🪄 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: CHILL
Plan: Pro
Run ID: c5dce754-3779-47dd-9f92-b68379809600
📒 Files selected for processing (6)
src/components/actionbar/ComfyRunButton/CloudRunButtonWrapper.test.tssrc/locales/en/main.jsonsrc/platform/cloud/subscription/components/SubscribeToRun.test.tssrc/platform/cloud/subscription/components/SubscribeToRun.vuesrc/platform/workspace/components/SubscriptionRequiredDialogContentWorkspace.test.tssrc/platform/workspace/components/SubscriptionRequiredDialogContentWorkspace.vue
Codecov Report❌ Patch coverage is
@@ Coverage Diff @@
## main #12786 +/- ##
===========================================
- Coverage 76.25% 61.67% -14.59%
===========================================
Files 1565 1458 -107
Lines 103375 75274 -28101
Branches 31150 21256 -9894
===========================================
- Hits 78831 46423 -32408
- Misses 23700 28497 +4797
+ Partials 844 354 -490
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 1180 files with indirect coverage changes 🚀 New features to boost your workflow:
|
…978)
Members of a cancelled/inactive team workspace now see a small Modal/Small
card matching Figma 3253-19473 (title + close, muted body, right-aligned
"Ok, got it"), routed before the pricing-table fork in useSubscriptionDialog
so it works regardless of the pricing dialog variant. Copy aligned to Figma
("subscription is inactive" / "reactivate" / "Ok, got it"). Removes the prior
big-dialog member block from SubscriptionRequiredDialogContentWorkspace.vue,
which is now owner-only.
jtydhr88
left a comment
There was a problem hiding this comment.
Code looks good to me, but I don't test it on real cloud env, so please verify it on preview-cpu cloud test env
|
@coderabbitai approve |
✅ Action performedComments resolved and changes approved. |
…12830) Stacked on the permission infra #12829. Gates owner **Cancel** (plan menu) + **Resubscribe** (panel button + workspace popover re-activate path) on `canManageSubscriptionLifecycle` (original owner only), per the billing-permission matrix (FE SSOT). Promoted owners keep manage-payment / upgrade / top-up; members get none. **Original-owner signal (repointed).** Rebased onto the updated #12829 (`a51183ae`), so the gate now resolves from the member-list `is_original_owner` field that the cloud cutover (Comfy-Org/cloud #4359) ships: the store getter `isCurrentUserOriginalOwner` matches the current user's member self-row by email, and `useWorkspaceUI` eagerly `fetchMembers()` so billing surfaces have the roster loaded. This replaces the earlier `is_creator`-on-`/api/workspaces` assumption — BE standardized on a per-member `is_original_owner` instead. No `is_creator` remains. **Merge-gated** on cutover cloud#4359 reaching cloud main: until `GET /api/workspace/members` returns `is_original_owner` in prod, the gate fails closed (lifecycle actions hidden for every owner). Fail-closed = no regression pre-deploy, but do not merge until the field is live. **Follow-up (non-blocking).** Exposing `is_original_owner` (current-user-relative, sibling of `role`) on `/api/workspaces` — Hunter pre-approved 2026-06-17 — would let us read it directly and drop the eager member fetch. Tracked on #12829 / FE-770. Part of FE-978 (member run-lock modal shipped separately in #12786).
…l, out of error panel (FE-878) (Comfy-Org#12785) ## Summary Account preconditions (sign-in / subscription / credits) on running a workflow now open their modal directly and stay out of the error panel + error count — previously `subscription_required` fell through to a red "1 ERROR — Subscription required to queue workflows" banner. This covers **both** paths: the `execution_error` websocket event and the `POST /prompt` 402 queue paywall (`{ type: "PAYMENT_REQUIRED", message: "Subscription required to queue workflows" }`), which is the exact payload reported in Comfy-Org#12840. ## Changes - **What**: `execution_error` is classified by a pure `accountPreconditionRouting` resolver (precedence sign-in > subscription > credits) and routed to the existing modal via `useAccountPreconditionDialog`; `executionStore` returns early for preconditions so they never populate `lastExecutionError` / `lastPromptError` / `lastNodeErrors` → fully excluded from the panel and `totalErrorCount`. Runtime credit error at a node → credits modal (out of panel; can name the node). - **Queue paywall**: the `queuePrompt` catch resolves the same precondition from the `POST /prompt` 402 response and opens the modal, short-circuiting before `lastPromptError`, so the queue paywall stays out of the panel too. The runtime matcher learns the `"Subscription required to queue workflows"` message. - **Breaking**: none. ## Before / After Free-tier queue paywall (`POST /prompt` → 402) on a cloud build: **Before** — raw `Subscription required to queue workflows` surfaced in the error panel (no actionable upgrade): <img width="1600" height="873" alt="before-error-panel" src="https://github.com/user-attachments/assets/1b76b742-16bf-47e3-9245-17e35f8f1e70" /> **After** — clean subscription modal opens; nothing in the error panel or error count: <img width="1600" height="873" alt="after-subscription-modal" src="https://github.com/user-attachments/assets/13d238cb-20bf-4795-a530-5abcf9968dc7" /> ## Review Focus - **Routing-only — the run button is intentionally untouched.** The original AC#3 ("no Subscribe-to-Run button") is superseded by the FE-978 run-lock decision (pre-emptive role-aware lock, Figma 3253-18671). Complements Comfy-Org#12786 (FE-978 run-lock); disjoint file sets. - Tests: `accountPreconditionRouting` / `useAccountPreconditionDialog` / `executionStore` — each precondition routes to its modal and is excluded from the panel/count; precedence resolves on co-occurrence. Plus Playwright `browser_tests/tests/subscriptionPaywallError.spec.ts` — the queue paywall (402) stays out of the error panel, with a control asserting ordinary queue errors still surface. typecheck / oxlint / eslint / stylelint / oxfmt / knip clean. Fixes FE-878 Fixes Comfy-Org#12840
|
@dante01yoon Successfully backported to #13006 |
…on (FE-770) (Comfy-Org#12829) ## What Adds a creator-only `canManageSubscriptionLifecycle` permission (cancel / reactivate / downgrade) to `useWorkspaceUI`: - Any workspace **owner** keeps `canManageSubscription` (manage payment / top-up / change-commit). - Only the **original owner (creator)** also gets `canManageSubscriptionLifecycle`. - **Personal** workspaces (single-member) always get it; **members** never do. Driven by an `is_creator` flag on the workspace from `/api/workspaces`, plumbed through the shared type and the auth/session Zod schema. ## Safe to merge ahead of BE / no regression - **Pure infra — nothing reads the permission yet.** Consumers land separately: Comfy-Org#12786 (cancel/reactivate), Comfy-Org#12789 (downgrade). No code on `main` references `canManageSubscriptionLifecycle` or `is_creator`. - **Additive & fails closed.** `is_creator` is optional; when absent the permission is `false`. Existing permission values are unchanged, so existing consumers (`SubscribeToRun`, `SubscriptionPanelContentWorkspace`, `CurrentUserPopoverWorkspace`, …) behave identically. ## BE contract (confirmed with Hunter, 2026-06-17) - `is_creator` = current-user-relative boolean on `/api/workspaces`; the creator is tracked explicitly (not by creation date). - Intentionally **temporary** — removed once member-removal auto-provisions a personal workspace. - The FE-770 members-panel creator-**row** lock additionally needs the creator **id** (`created_by_user_id`) — separate, still-open ask. ## Follow-up (separate PR, once BE ships) `is_creator` + `WorkspaceWithRoleSchema` are hand-rolled only because the cloud ingest OpenAPI doesn't expose the field yet. `@comfyorg/ingest-types` already generates `WorkspaceWithRole` + `zWorkspaceWithRole`. Once BE adds `is_creator` to `services/ingest/openapi.yaml`, regenerate and swap the hand-rolled interface + schema for the generated ones (TODOs marked in `workspaceApi.ts` / `workspaceAuthStore.ts`).
Summary
Cancelled / inactive team plans keep members but lock runs; the run button and the subscription-required dialog are now role-aware — owners are routed to the pricing/subscribe flow, members (who cannot subscribe) see "contact your workspace owner to resubscribe".
Changes
SubscribeToRun.vuebecomes a role-aware locked run button (owner → "Subscribe to Run"; member → neutral locked "Run" + contact-owner tooltip; both open the subscription dialog).SubscriptionRequiredDialogContentWorkspace.vuebranches on role (member → read-only contact-owner panel, no pricing/subscribe affordance; owner → existing pricing/preview; member view suppressed forout_of_creditsso the active-but-low-credits path is unchanged).subscription.inactive.*i18n keys.Review Focus
useWorkspaceUI().permissions.value.canManageSubscription(owner / personal = true, member = false) — the same accessorSubscriptionPanelContentWorkspace.vueuses.InactiveSubscriptionError;is_activechecked before funds). The lock is gated onis_active, the same field the orchestrator uses, so FE/BE stay consistent; leftover-credits-while-inactive remains blocked by design.SubscribeToRun(4) /CloudRunButtonWrapper(3) /SubscriptionRequiredDialogContentWorkspacerole cases — member sees contact-owner (no subscribe), owner sees pricing, run locks on!is_activeand unlocks when active (22 total); fulltest:unitgreen.Fixes FE-978