diff --git a/web/sdk/client/views/general/components/delete-organization-dialog.tsx b/web/sdk/client/views/general/components/delete-organization-dialog.tsx index 52f9bffddc..d01131dc61 100644 --- a/web/sdk/client/views/general/components/delete-organization-dialog.tsx +++ b/web/sdk/client/views/general/components/delete-organization-dialog.tsx @@ -20,6 +20,7 @@ import { } from '@raystack/apsara'; import { useFrontier } from '../../../contexts/FrontierContext'; import { useTerminology } from '../../../hooks/useTerminology'; +import { useTokens } from '../../../hooks/useTokens'; import { handleConnectError } from '~/utils/error'; const deleteOrgSchema = yup @@ -44,6 +45,7 @@ export const DeleteOrganizationDialog = ({ const orgLabel = t.organization({ case: 'capital' }); const orgLabelLower = t.organization({ case: 'lower' }); const [isAcknowledged, setIsAcknowledged] = useState(false); + const { tokenBalance } = useTokens(); const { mutateAsync: deleteOrganization } = useMutation( FrontierServiceQueries.deleteOrganization @@ -83,6 +85,7 @@ export const DeleteOrganizationDialog = ({ } catch (error) { handleConnectError(error, { PermissionDenied: () => toastManager.add({ title: "You don't have permission to perform this action", type: 'error' }), + FailedPrecondition: (err) => toastManager.add({ title: `Cannot delete this ${orgLabelLower} yet`, description: err.rawMessage, type: 'error' }), NotFound: (err) => toastManager.add({ title: 'Not found', description: err.message, type: 'error' }), Default: (err) => toastManager.add({ title: 'Something went wrong', description: err.message, type: 'error' }), }); @@ -102,6 +105,13 @@ export const DeleteOrganizationDialog = ({ This action can not be undone. This will permanently delete all the projects and resources in {organization?.title}. + {tokenBalance > 0 ? ( + + You have {tokenBalance.toString()} tokens remaining. Deleting + the {orgLabelLower} forfeits them. Contact support to get the + amount transferred to your bank account. + + ) : null} ; +// 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 + ); + // Update organization form const { mutateAsync: updateOrganization } = useMutation( FrontierServiceQueries.updateOrganization, @@ -280,22 +314,27 @@ export function GeneralView({ onDeleteSuccess, urlPrefix }: GeneralViewProps = { } > - {!canDeleteWorkspace && ( + {!canDeleteWorkspace ? ( {AuthTooltipMessage} - )} + ) : hasUnpaidInvoices ? ( + + There are unpaid invoices. Pay them from the billing page + before deleting the {orgLabelLower}. + + ) : null} )}