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}
>
)}