chore(studio): use Admonition and deprecate AlertError (#41095)

* use admonition and deprecate

* spot fix

* remove mb on admonition itself

* smart layout handling based on actions count

* fixes

* remove class

* fixes

* remove mb-0 instances

* remove redundant m-0

* remove single-use component

* use props

* reset leading

* remove redundant clause
This commit is contained in:
Danny White authored and GitHub committed 2025-12-08 12:15:18 +11:00
1 parent cf1f7284fe
commit 0399beba0e
34 files changed
+115 -115

No files matched your search

@@ -139,7 +139,7 @@ export const NewAccessTokenDialog = ({
{tokenScope === 'V0' ? (
<Admonition
type="warning"
className="rounded-none border-t-0 border-x-0 mb-0"
className="rounded-none border-t-0 border-x-0"
title="The experimental API provides additional endpoints which allows you to manage your organizations and projects."
description={
<>
@@ -160,7 +160,7 @@ export const NewAccessTokenDialog = ({
) : (
<Admonition
type="warning"
className="rounded-none border-t-0 border-x-0 mb-0"
className="rounded-none border-t-0 border-x-0"
title="Access tokens can be used to control your whole account"
description="Be careful when sharing your tokens"
/>
@@ -1,26 +0,0 @@
import Link from 'next/link'
import { useParams } from 'common'
import { InlineLink } from 'components/ui/InlineLink'
import { DOCS_URL } from 'lib/constants'
import { Button } from 'ui'
import { Admonition } from 'ui-patterns/admonition'
export const EmailRateLimitsAlert = () => {
const { ref } = useParams()
return (
<Admonition type="warning" title="Set up custom SMTP">
<p>
You’re using the built-in email service. This service has rate limits and is not meant to be
used for production apps.{' '}
<InlineLink href={`${DOCS_URL}/guides/platform/going-into-prod#auth-rate-limits`}>
Learn more
</InlineLink>{' '}
</p>
<Button asChild type="default" className="mt-2">
<Link href={`/project/${ref}/auth/smtp`}>Set up SMTP</Link>
</Button>
</Admonition>
)
}
@@ -30,7 +30,7 @@ import {
TooltipContent,
TooltipTrigger,
} from 'ui'
import { Admonition } from 'ui-patterns'
import { Admonition } from 'ui-patterns/admonition'
import {
PageSection,
PageSectionContent,
@@ -39,7 +39,6 @@ import {
PageSectionTitle,
} from 'ui-patterns/PageSection'
import { TEMPLATES_SCHEMAS } from '../AuthTemplatesValidation'
import { EmailRateLimitsAlert } from '../EmailRateLimitsAlert'
import { slugifyTitle } from './EmailTemplates.utils'
const notificationEnabledKeys = TEMPLATES_SCHEMAS.filter(
@@ -142,9 +141,26 @@ export const EmailTemplates = () => {
{isSuccess && (
<>
{builtInSMTP && (
<div className="mt-12">
<EmailRateLimitsAlert />
</div>
<Admonition
type="warning"
title="Set up custom SMTP"
description={
<p>
You’re using the built-in email service. This service has rate limits and is not
meant to be used for production apps.{' '}
<InlineLink href={`${DOCS_URL}/guides/platform/going-into-prod#auth-rate-limits`}>
Learn more
</InlineLink>{' '}
</p>
}
layout="horizontal"
className="mt-12 mb-10"
actions={
<Button asChild type="default" className="mt-2">
<Link href={`/project/${projectRef}/auth/smtp`}>Set up SMTP</Link>
</Button>
}
/>
)}
<PageSection className="!pt-0">
<PageSectionMeta>
@@ -109,7 +109,7 @@ const PolicyTableRowComponent = ({
<Admonition
showIcon={false}
type="warning"
className="mb-0 border-0 border-y rounded-none [&>div]:text-foreground-light h-[50px] py-0 flex items-center"
className="border-0 border-y rounded-none [&>div]:text-foreground-light h-[50px] py-0 flex items-center"
>
No data will be selectable via Supabase APIs as this schema is not exposed. You may
configure this in your project's{' '}
@@ -182,7 +182,7 @@ export const UserOverview = ({ user, onDeleteSuccess }: UserOverviewProps) => {
<Admonition
type="warning"
label={`User banned until ${dayjs(user.banned_until).format(DATE_FORMAT)}`}
className="border-r-0 border-l-0 rounded-none -mt-px [&_svg]:ml-0.5 mb-0"
className="border-r-0 border-l-0 rounded-none -mt-px [&_svg]:ml-0.5"
/>
) : (
<Separator />
@@ -97,7 +97,6 @@ const PaymentMethods = () => {
<Admonition
type="note"
layout="horizontal"
className="mb-0"
title="Payment is currently by invoice"
description="You get a monthly invoice and payment link via email. To change your payment
method, please contact us via our support form."
@@ -529,7 +529,7 @@ export const DatabaseConnectionString = () => {
<Admonition
type="warning"
title="Highly recommended to not use Session Pooler"
className="[&>div]:gap-0 px-8 [&>svg]:left-7 border-0 border-b rounded-none border-border-muted !py-4 mb-0"
className="[&>div]:gap-0 px-8 [&>svg]:left-7 border-0 border-b rounded-none border-border-muted !py-4"
>
<p className="text-sm text-foreground-lighter !mb-0">
If you are using Session Pooler, we recommend switching to Direct
@@ -203,9 +203,7 @@ export const ExtensionRow = ({ extension }: ExtensionRowProps) => {
Are you sure you want to turn OFF the "{extension.name}" extension?
</p>
{EXTENSION_DISABLE_WARNINGS[extension.name] && (
<Admonition type="warning" className="m-0">
{EXTENSION_DISABLE_WARNINGS[extension.name]}
</Admonition>
<Admonition type="warning">{EXTENSION_DISABLE_WARNINGS[extension.name]}</Admonition>
)}
</div>
</ConfirmationModal>
@@ -77,7 +77,7 @@ export const PublicationsTables = () => {
{!isLoadingPermissions && !canUpdatePublications && (
<Admonition
type="note"
className="w-[500px] m-0"
className="w-[500px]"
title="You need additional permissions to update database replications"
/>
)}
@@ -71,14 +71,14 @@ export const PublicationSelection = ({
/>
</FormControl_Shadcn_>
{isSelectedPublicationMissing ? (
<Admonition type="warning" className="mt-2 mb-0">
<Admonition type="warning" className="mt-2">
<p className="!leading-normal">
The publication <strong className="text-foreground">{publicationName}</strong> was
not found, it may have been renamed or deleted, please select another one.
</p>
</Admonition>
) : hasTablesWithNoPrimaryKeys ? (
<Admonition type="warning" className="mt-2 mb-0">
<Admonition type="warning" className="mt-2">
<p className="!leading-normal">
Replication requires every table in the publication to have a primary key to work,
which these tables are missing:
@@ -51,7 +51,7 @@ export const EnableReplicationModal = () => {
<DialogSection className="flex flex-col gap-y-2 !p-0">
<Admonition
type="warning"
className="rounded-none border-0 mb-0"
className="rounded-none border-0"
title="Replication is currently in Alpha"
>
<p className="text-sm !leading-normal">
@@ -397,7 +397,6 @@ export function DiskManagementForm() {
{!isReadOnlyMode && usedPercentage >= 90 && isWithinCooldownWindow && (
<Admonition
type="destructive"
className="mb-0"
title="Database size is currently over 90% of disk size"
description="Your project will enter read-only mode once you reach 95% of the disk space to prevent your database from exceeding the disk limitations"
>
@@ -411,7 +410,6 @@ export function DiskManagementForm() {
{isReadOnlyMode && (
<Admonition
type="destructive"
className="mb-0"
title="Project is currently in read-only mode"
description="You will need to manually override read-only mode and reduce the database size to below 95% of the disk size"
>
@@ -27,7 +27,7 @@ export function SpendCapDisabledSection() {
exit={{ opacity: 0, height: 0 }}
transition={{ duration: 0.2 }}
>
<Admonition type="default" className="mb-0">
<Admonition type="default">
<AlertTitle>Disable spend cap to manage Disk configuration</AlertTitle>
<AlertDescription>
To access disk management features, you need to disable your spend cap. These features
@@ -140,7 +140,7 @@ const MembersView = ({ searchString }: MembersViewProps) => {
type="note"
title="You are currently assigned with project scoped roles in this organization"
description="All other members within the organization will not be visible to you"
className="m-0 bg-alternative border-0 rounded-none"
className="bg-alternative border-0 rounded-none"
/>
</TableCell>
</TableRow>,
@@ -56,7 +56,6 @@ export const TeamSettings = () => {
{hasProjectScopedRoles && totalCount > threshold && (
<Admonition
type="warning"
className="mb-0"
title="This page may not render properly due to the number of projects your account has access to"
description="We're actively looking into optimizing this page and will make things available as soon as we can!"
/>
@@ -73,7 +73,7 @@ export const OrganizationInvite = () => {
showIcon={false}
type="default"
title={`Sign in${isSignUpEnabled ? ' or create an account' : ''} first to view this invitation`}
className="mb-0 border-0 rounded-none text-left"
className="border-0 rounded-none text-left"
/>
<div className="p-4 border-muted border-t flex gap-x-3 justify-center">
<Button asChild type="default">
@@ -96,7 +96,7 @@ export const OrganizationInvite = () => {
type="default"
title="Invalid invitation"
description="This organization invite is no longer valid as it has either been accepted or declined"
className="mb-0 border-0 rounded-none text-left"
className="border-0 rounded-none text-left"
/>
<div className="p-4 border-muted border-t">
<Button type="default" asChild>
@@ -221,7 +221,7 @@ const MonacoEditor = ({
{disableEdit && (
<Admonition
type="default"
className="m-0 py-2 rounded-none border-0 border-b [&>h5]:mb-0.5"
className="py-2 rounded-none border-0 border-b [&>h5]:mb-0.5"
title="This snippet has been shared to the project and is only editable by the owner who created this snippet"
description='You may duplicate this snippet into a personal copy by right clicking on the snippet and selecting "Duplicate query"'
/>
@@ -448,7 +448,7 @@ const EnableReplicationDialogContent = ({ onClose }: { onClose: () => void }) =>
<DialogSection className="flex flex-col gap-y-2 !p-0">
<Admonition
type="warning"
className="rounded-none border-0 mb-0"
className="rounded-none border-0"
title={
noAccessToReplication
? 'Replication is currently unavailable for your project'
@@ -366,7 +366,7 @@ const ExtensionNotInstalled = ({
return (
<>
<ScaffoldSection isFullWidth>
<Admonition type="warning" title="Missing required extension" className="mb-0">
<Admonition type="warning" title="Missing required extension">
<p>
The Wrappers extension is required in order to query analytics tables.{' '}
{databaseNeedsUpgrading &&
@@ -417,7 +417,7 @@ const ExtensionNeedsUpgrade = ({
return (
<>
<ScaffoldSection isFullWidth>
<Admonition type="warning" title="Outdated extension version" className="mb-0">
<Admonition type="warning" title="Outdated extension version">
<p>
The {wrapperMeta.label} wrapper requires a minimum extension version of{' '}
{wrapperMeta.minimumExtensionVersion}. You have version{' '}
@@ -459,7 +459,7 @@ const WrapperMissing = ({ bucketName }: { bucketName?: string }) => {
return (
<>
<ScaffoldSection isFullWidth>
<Admonition type="warning" title="Missing integration" className="mb-0">
<Admonition type="warning" title="Missing integration">
<p>The Iceberg Wrapper integration is required in order to query analytics tables.</p>
<Button type="default" loading={isCreatingIcebergWrapper} onClick={onSetupWrapper}>
Install wrapper
@@ -60,7 +60,7 @@ export const EmptyBucketModal = ({ visible, bucket, onClose }: EmptyBucketModalP
<DialogSectionSeparator />
<Admonition
type="destructive"
className="rounded-none border-x-0 border-t-0 mb-0"
className="rounded-none border-x-0 border-t-0"
title="This action cannot be undone"
description="The contents of your bucket cannot be recovered once deleted."
/>
@@ -120,7 +120,7 @@ const StorageListV2MigrationDialog = () => {
<Admonition
type="warning"
className="mb-0 rounded-none border-x-0 border-t-0"
className="rounded-none border-x-0 border-t-0"
title="Migration required to optimise the database schema for upgrade"
description="We recommend running the update during periods of lower activity, although minimal to no disruption is expected."
/>
@@ -219,7 +219,7 @@ export const CreateVectorTableSheet = ({ bucketName }: CreateVectorTableSheetPro
{showIndexCreationNotice && (
<Admonition
type="warning"
className="mb-0 border-x-0 border-t-0 rounded-none"
className="border-x-0 border-t-0 rounded-none"
title="Vector table creation is currently only supported for projects in us-east-1"
description={`This is only applicable to projects on local/staging (Project is currently in ${project.region})`}
/>
@@ -27,7 +27,7 @@ export const ExtensionNotInstalled = ({
return (
<ScaffoldSection isFullWidth>
<Admonition type="warning" title="Missing required extension" className="mb-0">
<Admonition type="warning" title="Missing required extension">
<p>
The Wrappers extension is required in order to query vector tables.{' '}
{databaseNeedsUpgrading &&
@@ -71,7 +71,7 @@ export const ExtensionNeedsUpgrade = ({
return (
<ScaffoldSection isFullWidth>
<Admonition type="warning" title="Outdated extension version" className="mb-0">
<Admonition type="warning" title="Outdated extension version">
<p>
The {wrapperMeta.label} wrapper requires a minimum extension version of{' '}
{wrapperMeta.minimumExtensionVersion}. You have version{' '}
@@ -125,7 +125,7 @@ export const WrapperMissing = ({ bucketName }: { bucketName?: string }) => {
return (
<ScaffoldSection isFullWidth>
<Admonition type="warning" title="Missing integration" className="mb-0">
<Admonition type="warning" title="Missing integration">
<p>The S3 Vectors Wrapper integration is required in order to query vector tables.</p>
<Button type="default" loading={isLoading} onClick={onSetupWrapper}>
Install wrapper
@@ -54,7 +54,7 @@ export function CategoryAndSeverityInfo({
{(severity === 'Urgent' || severity === 'High') && (
<Admonition
type="default"
className="mb-0 sm:col-span-2"
className="sm:col-span-2"
title="We do our best to respond to everyone as quickly as possible"
description="Prioritization will be based on production status. We ask that you reserve High and Urgent severity for production-impacting issues only."
/>
@@ -52,11 +52,7 @@ export function ProjectAndPlanInfo({
<ProjectRefHighlighted projectRef={projectRef} />
{!hasProjectSelected && (
<Admonition
type="default"
className="mb-0"
title="Please note that no project has been selected"
/>
<Admonition type="default" title="Please note that no project has been selected" />
)}
{showPlanExpectationInfo &&
@@ -188,7 +188,7 @@ export const FeedbackWidget = ({ onClose }: FeedbackWidgetProps) => {
<Admonition
type="caution"
title="This looks like an issue that’s better handled by support"
className="rounded-none border-x-0 border-b-0 mb-0"
className="rounded-none border-x-0 border-b-0"
>
<p className="text-xs text-foreground-light !leading-normal !mb-0">
Please{' '}
@@ -121,7 +121,7 @@ export const PauseDisabledState = () => {
<Admonition
showIcon={false}
type="warning"
className="mb-0 rounded-none border-0 px-6"
className="rounded-none border-0 px-6"
title="Project can no longer be restored through the dashboard"
>
<p className="!leading-normal !mb-3">
@@ -103,7 +103,7 @@ export const AIAssistantHeader = ({
? 'Sharing query data in addition to schema can further improve responses. Update AI settings to enable this.'
: ''
}
className="border-0 border-b rounded-none bg-background mb-0"
className="border-0 border-b rounded-none bg-background"
>
{!isHipaaProjectDisallowed && (
<Button type="default" className="w-fit mt-4" onClick={() => setIsOptInModalOpen(true)}>
+59 -35
View File
@@ -2,23 +2,48 @@ import { SupportCategories } from '@supabase/shared-types/out/constants'
import { SupportLink } from 'components/interfaces/Support/SupportLink'
import { PropsWithChildren } from 'react'
import {
AlertDescription_Shadcn_,
AlertTitle_Shadcn_,
Alert_Shadcn_,
Button,
WarningIcon,
} from 'ui'
import { Admonition } from 'ui-patterns/admonition'
import { Button } from 'ui'
export interface AlertErrorProps {
projectRef?: string
subject?: string
error?: { message: string } | null
layout?: 'vertical' | 'horizontal'
className?: string
showIcon?: boolean
additionalActions?: React.ReactNode
}
const ContactSupportButton = ({
projectRef,
subject,
error,
}: {
projectRef?: string
subject?: string
error?: { message: string } | null
}) => {
return (
<Button asChild type="default" className="w-min">
<SupportLink
queryParams={{
category: SupportCategories.DASHBOARD_BUG,
projectRef,
subject,
error: error?.message,
}}
>
Contact support
</SupportLink>
</Button>
)
}
/**
* @deprecated Use `import { Admonition } from "ui-patterns/admonition"` instead
*/
// [Joshen] To standardize the language for all error UIs
export const AlertError = ({
@@ -27,6 +52,7 @@ export const AlertError = ({
error,
className,
showIcon = true,
layout = 'horizontal',
children,
additionalActions,
}: PropsWithChildren<AlertErrorProps>) => {
@@ -35,35 +61,33 @@ export const AlertError = ({
: error?.message
return (
<Alert_Shadcn_ className={className} variant="warning" title={subject}>
{showIcon && <WarningIcon className="h-4 w-4" strokeWidth={2} />}
<AlertTitle_Shadcn_ className="text-foreground">{subject}</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_ className="flex flex-col gap-3 break-words">
<div>
{error?.message && <p className="text-left">Error: {formattedErrorMessage}</p>}
<p className="text-left">
Try refreshing your browser, but if the issue persists for more than a few minutes,
please reach out to us via support.
<Admonition
type="warning"
layout={additionalActions ? 'vertical' : layout}
showIcon={showIcon}
title={subject}
description={
<>
{error?.message && <p>Error: {formattedErrorMessage}</p>}
<p>
Try refreshing your browser. If the issue persists for more than a few minutes, please
reach out to us via support.
</p>
</div>
{children}
<div className="flex gap-2">
{additionalActions}
<Button asChild type="warning" className="w-min">
<SupportLink
queryParams={{
category: SupportCategories.DASHBOARD_BUG,
projectRef,
subject,
error: error?.message,
}}
>
Contact support
</SupportLink>
</Button>
</div>
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
{children}
</>
}
actions={
additionalActions ? (
<div className="flex gap-2 mt-3">
{additionalActions}
<ContactSupportButton projectRef={projectRef} subject={subject} error={error} />
</div>
) : (
<ContactSupportButton projectRef={projectRef} subject={subject} error={error} />
)
}
className={className}
/>
)
}
@@ -106,7 +106,7 @@ export const EdgeFunctionBlock = ({
{showReplaceWarning && (
<Admonition
type="warning"
className="mb-0 rounded-none border-0 border-b shrink-0 bg-background-100"
className="rounded-none border-0 border-b shrink-0 bg-background-100"
>
<p>An edge function with the name "{functionName}" already exists.</p>
<p className="text-foreground-light">
@@ -306,7 +306,7 @@ export const EditorPanel = () => {
<div className="shrink-0">
<Admonition
type="warning"
className="m-0 rounded-none border-x-0 border-b-0 [&>div>div>pre]:text-sm [&>div]:flex [&>div]:flex-col [&>div]:gap-y-2"
className="rounded-none border-x-0 border-b-0 [&>div>div>pre]:text-sm [&>div]:flex [&>div]:flex-col [&>div]:gap-y-2"
title={errorHeader || 'Error running SQL query'}
description={
<div>
+1 -5
View File
@@ -76,11 +76,7 @@ const ClaimProjectPage: NextPageWithLayout = () => {
if ((selectedOrgSlug && claimToken && isErrorProjectClaim) || isErrorRequester) {
return (
<ProjectClaimLayout title="Claim a project" className="py-6">
<Admonition
type="warning"
className="mb-0"
title="Failed to retrieve project claim request details"
>
<Admonition type="warning" title="Failed to retrieve project claim request details">
<p>Please retry your claim request from the requesting app</p>
{!!errorProjectClaim && <p className="mt-2">Error: {errorProjectClaim?.message}</p>}
{!!errorRequester && <p className="mt-2">Error: {errorRequester?.message}</p>}
+1 -1
View File
@@ -107,7 +107,7 @@ export const Admonition = forwardRef<
ref={ref}
variant={typeMapped}
{...props}
className={cn('mb-2', admonitionSVG({ type: typeMapped }), props.className)}
className={cn(admonitionSVG({ type: typeMapped }), props.className)}
>
{!!icon ? (
icon
@@ -45,7 +45,7 @@ const AlertDescription = React.forwardRef<
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn('text-sm [&_p]:leading-relaxed text-foreground-light font-normal', className)}
className={cn('text-sm text-foreground-light font-normal', className)}
{...props}
/>
))