Skip to content

feat(sdk): guard the delete organization flow - #1881

Open
whoAbhishekSah wants to merge 1 commit into
mainfrom
sdk-org-delete-guards
Open

feat(sdk): guard the delete organization flow#1881
whoAbhishekSah wants to merge 1 commit into
mainfrom
sdk-org-delete-guards

Conversation

@whoAbhishekSah

Copy link
Copy Markdown
Member

Part of #1837 — the client half of the delete pre-flight (server half: #1857, #1880). Independent of the Go stack; safe to review and merge separately.

Three guards in the client SDK's General settings view:

  • Unpaid invoices grey out the delete button. The view queries open invoices with a non-zero amount (SearchOrganizationInvoices, same query the billing page uses). While any exist, the delete button is disabled and its hover tooltip says to pay them from the billing page first. The server refuses the delete in that state anyway; this stops the user before the failed call.
  • The delete dialog warns about remaining tokens. When the billing account has a positive token balance, the dialog says how many tokens remain, that deleting forfeits them, and that support can transfer the amount to the user's bank account. Confirming the dialog is the user's consent — the server does not block on tokens.
  • Idempotent confirm. The dialog's delete button disables while the request is in flight, so repeated clicks cannot fire the delete twice. A failed_precondition response (paid subscription to downgrade, unpaid invoice, token debt) surfaces the server's reasons in the error toast instead of a generic message.

🤖 Generated with Claude Code

The delete button greys out while the org has open invoices with a
non-zero amount, and its hover tooltip says to pay them from the
billing page first — the server refuses the delete in that state
anyway, this stops the user before the failed call.

The delete dialog warns when tokens remain on the billing account:
deleting forfeits them, and support can transfer the amount to the
user's bank account. Confirming the dialog is the user's consent.

The confirm button also disables while the request is in flight, so
repeated clicks cannot fire the delete twice, and a failed_precondition
response shows the server's reasons instead of a generic error.
@vercel

vercel Bot commented Aug 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
frontier Ready Ready Preview Aug 13, 2026 11:34am

@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Added a warning when deleting an organization with remaining tokens, including guidance about forfeiture and transfer.
    • Improved deletion error messages for blocked requests.
    • Disabled deletion while the request is processing.
    • Prevented deletion when unpaid invoices are present and added billing guidance.

Walkthrough

Organization deletion now checks for unpaid invoices and remaining tokens. The UI blocks deletion when unpaid invoices exist, warns about forfeited tokens, reports failed-precondition errors, and disables submission while deletion is in progress.

Changes

Organization deletion validation

Layer / File(s) Summary
Unpaid invoice deletion gate
web/sdk/client/views/general/general-view.tsx
GeneralView queries open invoices with positive amounts and disables organization deletion when unpaid invoices exist. A billing tooltip explains the restriction.
Deletion dialog safeguards
web/sdk/client/views/general/components/delete-organization-dialog.tsx
The dialog displays remaining token warnings, handles failed-precondition errors, and disables the delete button during submission.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Mergeability Score: 🟡 Moderate · up to 347b3

The PR can allow the delete flow to proceed without reliably verifying unpaid invoices or showing a warning for positive token balances, so users may see avoidable failed deletes or forfeit tokens without the intended notice. Merge should wait for the checks to fail closed; the background balance request is a minor follow-up concern.

Possibly related issues

Possibly related PRs

Suggested reviewers: rohanchkrabrty

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
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.

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

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
web/sdk/client/views/general/components/delete-organization-dialog.tsx (1)

48-48: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Defer the balance query until the dialog opens.

GeneralView mounts DeleteOrganizationDialog even when open is false. Calling useTokens() starts the billing query for every permitted General view. A failed request can also show the “Unable to fetch balance” toast before the user opens the dialog.

Add an enabled: open option to useTokens, or mount the dialog only while it is open.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6e51689d-663d-4f44-83ac-03667d03f265

📥 Commits

Reviewing files that changed from the base of the PR and between 229ea89 and 347b39f.

📒 Files selected for processing (2)
  • web/sdk/client/views/general/components/delete-organization-dialog.tsx
  • web/sdk/client/views/general/general-view.tsx

Comment on lines +108 to +114
{tokenBalance > 0 ? (
<Text size="small" variant="danger">
You have {tokenBalance.toString()} tokens remaining. Deleting
the {orgLabelLower} forfeits them. Contact support to get the
amount transferred to your bank account.
</Text>
) : null}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Do not treat an unavailable token balance as zero.

useTokens initializes tokenBalance to 0n when the balance response is absent. This branch hides the warning while the confirm button remains enabled. A slow or failed balance request can therefore let a user delete an organization with a positive balance without seeing the forfeiture warning.

Expose the balance query’s loading and error state. Keep the destructive action unavailable, or require an explicit unresolved-balance confirmation, until a successful balance is known.

Comment on lines +126 to +132
const { invoices } = useOrganizationInvoices({
query: OPEN_INVOICES_QUERY,
enabled: canDeleteWorkspace && !!organization?.id
});
const hasUnpaidInvoices = invoices.some(
inv => inv.state === INVOICE_STATES.OPEN
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Fail closed when the invoice query is not verified.

useOrganizationInvoices exposes isLoading and isError, but this code reads only invoices. During the initial request or after a failed request, invoices can be empty and hasUnpaidInvoices becomes false. GeneralView can then enable deletion without proving that no blocking invoice exists.

Use the query status in the delete gate. Keep the button disabled while the check is loading or failed. Show an explicit loading or verification-error tooltip.

Based on learnings: count-dependent actions should remain unavailable when the query fails because the UI cannot show a reliable count.

Also applies to: 317-325, 332-337

Source: Learnings

@coveralls

Copy link
Copy Markdown

Coverage Report for CI Build 31696052991

Coverage remained the same at 48.266%

Details

  • Coverage remained the same as the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 39761
Covered Lines: 19191
Line Coverage: 48.27%
Coverage Strength: 15.43 hits per line

💛 - Coveralls

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