-
Notifications
You must be signed in to change notification settings - Fork 45
feat(sdk): guard the delete organization flow #1881
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,7 +13,10 @@ import { | |
| import { useQueryClient } from '@tanstack/react-query'; | ||
| import { | ||
| FrontierServiceQueries, | ||
| UpdateOrganizationRequestSchema | ||
| UpdateOrganizationRequestSchema, | ||
| RQLRequestSchema, | ||
| RQLFilterSchema, | ||
| RQLSortSchema | ||
| } from '@raystack/proton/frontier'; | ||
| import { | ||
| Button, | ||
|
|
@@ -28,6 +31,9 @@ import { | |
| import { useFrontier } from '../../contexts/FrontierContext'; | ||
| import { usePermissions } from '../../hooks/usePermissions'; | ||
| import { useTerminology } from '../../hooks/useTerminology'; | ||
| import { useOrganizationInvoices } from '../../hooks/useOrganizationInvoices'; | ||
| import { INVOICE_STATES } from '../../utils/constants'; | ||
| import { DEFAULT_PAGE_SIZE } from '../../utils/connect-pagination'; | ||
| import { PERMISSIONS, shouldShowComponent } from '../../../utils'; | ||
| import { AuthTooltipMessage } from '../../utils'; | ||
| import { ViewContainer } from '../../components/view-container'; | ||
|
|
@@ -47,6 +53,26 @@ const generalSchema = yup | |
|
|
||
| type FormData = yup.InferType<typeof generalSchema>; | ||
|
|
||
| // Open invoices with a non-zero amount. The server refuses the delete while | ||
| // any exist, so the delete button greys out and explains why. | ||
| const OPEN_INVOICES_QUERY = create(RQLRequestSchema, { | ||
| filters: [ | ||
| create(RQLFilterSchema, { | ||
| name: 'state', | ||
| operator: 'eq', | ||
| value: { case: 'stringValue', value: INVOICE_STATES.OPEN } | ||
| }), | ||
| create(RQLFilterSchema, { | ||
| name: 'amount', | ||
| operator: 'gt', | ||
| value: { case: 'numberValue', value: 0 } | ||
| }) | ||
| ], | ||
| sort: [create(RQLSortSchema, { name: 'created_at', order: 'desc' })], | ||
| offset: 0, | ||
| limit: DEFAULT_PAGE_SIZE | ||
| }); | ||
|
|
||
| export interface GeneralViewProps { | ||
| onDeleteSuccess?: () => void; | ||
| urlPrefix?: string; | ||
|
|
@@ -97,6 +123,14 @@ export function GeneralView({ onDeleteSuccess, urlPrefix }: GeneralViewProps = { | |
|
|
||
| const isLoading = !organization?.id || isActiveOrganizationLoading || isPermissionsFetching; | ||
|
|
||
| const { invoices } = useOrganizationInvoices({ | ||
| query: OPEN_INVOICES_QUERY, | ||
| enabled: canDeleteWorkspace && !!organization?.id | ||
| }); | ||
| const hasUnpaidInvoices = invoices.some( | ||
| inv => inv.state === INVOICE_STATES.OPEN | ||
| ); | ||
|
Comment on lines
+126
to
+132
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
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 |
||
|
|
||
| // Update organization form | ||
| const { mutateAsync: updateOrganization } = useMutation( | ||
| FrontierServiceQueries.updateOrganization, | ||
|
|
@@ -280,22 +314,27 @@ export function GeneralView({ onDeleteSuccess, urlPrefix }: GeneralViewProps = { | |
| </Text> | ||
| <Tooltip> | ||
| <Tooltip.Trigger | ||
| disabled={canDeleteWorkspace} | ||
| disabled={canDeleteWorkspace && !hasUnpaidInvoices} | ||
| render={<span className={styles.fitContent} />} | ||
| > | ||
| <Button | ||
| variant="solid" | ||
| color="danger" | ||
| onClick={() => setShowDeleteDialog(true)} | ||
| disabled={!canDeleteWorkspace} | ||
| disabled={!canDeleteWorkspace || hasUnpaidInvoices} | ||
| data-test-id="frontier-sdk-delete-organization-btn" | ||
| > | ||
| Delete {orgLabelLower} | ||
| </Button> | ||
| </Tooltip.Trigger> | ||
| {!canDeleteWorkspace && ( | ||
| {!canDeleteWorkspace ? ( | ||
| <Tooltip.Content>{AuthTooltipMessage}</Tooltip.Content> | ||
| )} | ||
| ) : hasUnpaidInvoices ? ( | ||
| <Tooltip.Content> | ||
| There are unpaid invoices. Pay them from the billing page | ||
| before deleting the {orgLabelLower}. | ||
| </Tooltip.Content> | ||
| ) : null} | ||
| </Tooltip> | ||
| </> | ||
| )} | ||
|
|
||
There was a problem hiding this comment.
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.
useTokensinitializestokenBalanceto0nwhen 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.