feat(sdk): guard the delete organization flow - #1881
Conversation
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.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughSummary by CodeRabbit
WalkthroughOrganization 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. ChangesOrganization deletion validation
Estimated code review effort: 3 (Moderate) | ~20 minutes Mergeability Score: 🟡 Moderate · up to 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: 🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
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: 2
🧹 Nitpick comments (1)
web/sdk/client/views/general/components/delete-organization-dialog.tsx (1)
48-48: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winDefer the balance query until the dialog opens.
GeneralView mounts
DeleteOrganizationDialogeven whenopenis false. CallinguseTokens()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: openoption touseTokens, 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
📒 Files selected for processing (2)
web/sdk/client/views/general/components/delete-organization-dialog.tsxweb/sdk/client/views/general/general-view.tsx
| {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} |
There was a problem hiding this comment.
🎯 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.
| const { invoices } = useOrganizationInvoices({ | ||
| query: OPEN_INVOICES_QUERY, | ||
| enabled: canDeleteWorkspace && !!organization?.id | ||
| }); | ||
| const hasUnpaidInvoices = invoices.some( | ||
| inv => inv.state === INVOICE_STATES.OPEN | ||
| ); |
There was a problem hiding this comment.
🎯 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
Coverage Report for CI Build 31696052991Coverage remained the same at 48.266%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
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:
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.failed_preconditionresponse (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