Files
Gildas GarciaandDanny White 1fbed21528 chore: migrate old <Alert> usages to <Admonition> (#45797)
## Problem

Some pages still use the old `<Alert>` component

## Solution

- [x] Use the new `<Admonition>`
- [x] Remove `<Alert>` 

## Screenshots

### Table Editor - Disable RLS modal
Before:
<img width="582" height="527" alt="image"
src="https://github.com/user-attachments/assets/bbc5b874-2569-4cd6-98c0-5edd5ac78e0f"
/>

After:
<img width="543" height="505" alt="image"
src="https://github.com/user-attachments/assets/bb1da899-2163-4b26-ba0b-74726e0cb5df"
/>

### Organization Billing
Before:
<img width="1111" height="512" alt="image"
src="https://github.com/user-attachments/assets/6fb410fb-9ac0-4fb7-8dde-e304ddee7ece"
/>

After:
<img width="1103" height="531" alt="image"
src="https://github.com/user-attachments/assets/6eda6ede-edc6-482f-ab9c-d085402cbd64"
/>




<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Refactor**
* Replaced Alert UIs with Admonition across billing, settings, storage,
integrations, and editor interfaces.
  * Removed the deprecated AlphaPreview component.

* **Chores**
* Removed the Alert component from the UI library, including its styles,
tests, and theme configuration.

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45797)
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Danny White <3104761+dnywh@users.noreply.github.com>
2026-05-12 16:48:06 +02:00

209 lines
8.7 KiB
TypeScript

import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useFlag, useParams } from 'common'
import { ExternalLink } from 'lucide-react'
import { useTheme } from 'next-themes'
import Image from 'next/image'
import Link from 'next/link'
import { Alert_Shadcn_, AlertTitle_Shadcn_, Button } from 'ui'
import { Admonition } from 'ui-patterns/admonition'
import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'
import { ProjectUpdateDisabledTooltip } from '../ProjectUpdateDisabledTooltip'
import SpendCapSidePanel from './SpendCapSidePanel'
import {
ScaffoldSection,
ScaffoldSectionContent,
ScaffoldSectionDetail,
} from '@/components/layouts/Scaffold'
import AlertError from '@/components/ui/AlertError'
import { InlineLink } from '@/components/ui/InlineLink'
import NoPermission from '@/components/ui/NoPermission'
import PartnerIcon from '@/components/ui/PartnerIcon'
import { PARTNER_TO_NAME } from '@/components/ui/PartnerManagedResource'
import { useOrgSubscriptionQuery } from '@/data/subscriptions/org-subscription-query'
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
import { BASE_PATH, DOCS_URL } from '@/lib/constants'
import { MANAGED_BY } from '@/lib/constants/infrastructure'
import { useOrgSettingsPageStateSnapshot } from '@/state/organization-settings'
export interface CostControlProps {}
const CostControl = ({}: CostControlProps) => {
const { slug } = useParams()
const { resolvedTheme } = useTheme()
const { data: selectedOrganization } = useSelectedOrganizationQuery()
const { isSuccess: isPermissionsLoaded, can: canReadSubscriptions } = useAsyncCheckPermissions(
PermissionAction.BILLING_READ,
'stripe.subscriptions'
)
const snap = useOrgSettingsPageStateSnapshot()
const projectUpdateDisabled = useFlag('disableProjectCreationAndUpdate')
const {
data: subscription,
error,
isPending: isLoading,
isSuccess,
isError,
} = useOrgSubscriptionQuery({ orgSlug: slug }, { enabled: canReadSubscriptions })
const currentPlan = subscription?.plan
const isUsageBillingEnabled = subscription?.usage_billing_enabled ?? false
const canChangeTier =
!projectUpdateDisabled && !['team', 'enterprise', 'platform'].includes(currentPlan?.id || '')
const costControlDisabled = selectedOrganization?.managed_by === MANAGED_BY.AWS_MARKETPLACE
return (
<>
<ScaffoldSection>
<ScaffoldSectionDetail>
<div className="sticky space-y-6 top-12">
<div className="space-y-2">
<p className="text-foreground text-base m-0">Cost Control</p>
<p className="text-sm text-foreground-light m-0">
Allow scaling beyond your plan's{' '}
<InlineLink href={`/org/${slug}/usage`}>included quota</InlineLink>.
</p>
</div>
<div className="space-y-2">
<p className="text-sm text-foreground-light m-0">More information</p>
<div>
<Link
href={`${DOCS_URL}/guides/platform/cost-control#spend-cap`}
target="_blank"
rel="noreferrer"
>
<div className="flex items-center space-x-2 opacity-50 hover:opacity-100 transition">
<p className="text-sm m-0">Spend cap</p>
<ExternalLink size={16} strokeWidth={1.5} />
</div>
</Link>
</div>
</div>
</div>
</ScaffoldSectionDetail>
<ScaffoldSectionContent>
{isPermissionsLoaded && !canReadSubscriptions ? (
<NoPermission resourceText="update this organization's cost control" />
) : (
<>
{isLoading && (
<div className="space-y-2">
<ShimmeringLoader />
<ShimmeringLoader className="w-3/4" />
<ShimmeringLoader className="w-1/2" />
</div>
)}
{isError && <AlertError subject="Failed to retrieve subscription" error={error} />}
{isSuccess && costControlDisabled && (
<Alert_Shadcn_ className="flex flex-col items-center gap-y-2 border-0 rounded-none">
<PartnerIcon
organization={{ managed_by: selectedOrganization?.managed_by }}
showTooltip={false}
size="large"
/>
<AlertTitle_Shadcn_ className="text-sm">
The Spend Cap is not available for organizations managed by{' '}
{PARTNER_TO_NAME[selectedOrganization?.managed_by]}.
</AlertTitle_Shadcn_>
</Alert_Shadcn_>
)}
{isSuccess && !costControlDisabled && (
<div className="space-y-6">
{['team', 'enterprise', 'platform'].includes(currentPlan?.id || '') ? (
<>
<Admonition
type="default"
layout="horizontal"
title={`You will be charged for any additional usage on the ${
currentPlan?.name || ''
} plan`}
description={
<>
{currentPlan?.name || ''} plan requires you to have spend cap off at all
times. Your projects will never become unresponsive. Only when your{' '}
<Link
href={`/org/${slug}/usage`}
className="text-green-900 transition hover:text-green-1000"
>
included usage
</Link>{' '}
is exceeded will you be charged for any additional usage.
</>
}
/>
</>
) : (
<p className="text-sm text-foreground-light">
If you need to go beyond the included quota, simply switch off your spend cap
to pay for additional usage.
</p>
)}
<div className="flex flex-col md:flex-row gap-6">
<div>
<div className="rounded-md bg-surface-200 w-[160px] h-[96px] shadow-sm">
<Image
alt="Spend Cap"
width={160}
height={96}
src={
isUsageBillingEnabled
? `${BASE_PATH}/img/spend-cap-off${
resolvedTheme?.includes('dark') ? '' : '--light'
}.png`
: `${BASE_PATH}/img/spend-cap-on${
resolvedTheme?.includes('dark') ? '' : '--light'
}.png`
}
/>
</div>
</div>
<div>
<p className="mb-1">
Spend cap is {isUsageBillingEnabled ? 'disabled' : 'enabled'}
</p>
<p className="text-sm text-foreground-light">
{isUsageBillingEnabled ? (
<span>You will be charged for usage beyond the included quota.</span>
) : (
<span>
You won't be charged any extra for usage. However, your projects could
become unresponsive or enter read only mode if you exceed the included
quota.
</span>
)}
</p>
<ProjectUpdateDisabledTooltip projectUpdateDisabled={projectUpdateDisabled}>
<Button
type="default"
className="mt-4 pointer-events-auto"
disabled={!canChangeTier}
onClick={() => snap.setPanelKey('costControl')}
>
Change spend cap
</Button>
</ProjectUpdateDisabledTooltip>
</div>
</div>
</div>
)}
</>
)}
</ScaffoldSectionContent>
</ScaffoldSection>
<SpendCapSidePanel />
</>
)
}
export default CostControl