ref(etl): Add better badges (#40828)

* ref(etl): Add better badges

* Fix

* Update apps/studio/components/layouts/DatabaseLayout/DatabaseMenu.utils.tsx

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>

* UI nudges

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
Riccardo Busetti
2025-11-27 04:42:27 +01:00
committed by GitHub
parent 3ee1ad6ce2
commit 09cda344a1
9 changed files with 62 additions and 36 deletions

View File

@@ -6,7 +6,7 @@ import ReactFlow, { Background, Handle, Position, ReactFlowProvider } from 'reac
import 'reactflow/dist/style.css'
import { BASE_PATH } from 'lib/constants'
import { Badge, Button, Card, CardContent } from 'ui'
import { Button, Card, CardContent } from 'ui'
import { NODE_WIDTH } from '../../Settings/Infrastructure/InfrastructureConfiguration/InstanceConfiguration.constants'
const STATIC_NODES = [
@@ -157,7 +157,7 @@ const ReplicaNode = ({
<div className="flex items-start justify-between p-3" style={{ width: NODE_WIDTH / 2 - 10 }}>
<div className="flex gap-x-3">
<div className="flex flex-col gap-y-0.5">
<p className="">{data.label}</p>
<p>{data.label}</p>
<p className="flex items-center gap-x-1">
<span className="text-sm text-foreground-light">{data.details}</span>
</p>
@@ -196,20 +196,22 @@ const BlankNode = () => {
const CTANode = ({ projectRef }: { projectRef: string }) => {
return (
<Card className="w-[500px] p-6">
<Card className="w-[570px] p-6">
<CardContent>
<div className="flex items-center gap-x-2 justify-between mb-2">
<h2 className="text-lg">Replicate data to destinations in real-time</h2>
<Badge variant="warning">Early Access</Badge>
</div>
<h2 className="text-lg mb-2">Stream database changes to external destinations</h2>
<p className="text-foreground-light mb-2">
Automatically replicate your data to external data warehouses and analytics platforms in
real-time. No manual exports, no lag.
</p>
<p className="text-foreground-light">
Replicate database changes to multiple destinations - no manual exports, no lag. Limited
rollout for external destinations has begun, read replicas available now.
We are currently in <span className="text-foreground">private alpha</span> and slowly
onboarding new customers to ensure stable data pipelines. Request access below to join the
waitlist. Read replicas are available now.
</p>
<div className="flex items-center gap-x-2 mt-6">
<Button asChild type="secondary" iconRight={<ArrowUpRight size={16} strokeWidth={1.5} />}>
<Link href="https://forms.supabase.com/pg_replicate" target="_blank" rel="noreferrer">
Request early access
Request alpha access
</Link>
</Button>
<Button asChild type="default" iconRight={<ArrowRight size={16} strokeWidth={1.5} />}>

View File

@@ -5,6 +5,7 @@ import { useState } from 'react'
import { useParams } from 'common'
import { ScaffoldHeader, ScaffoldSection, ScaffoldSectionTitle } from 'components/layouts/Scaffold'
import AlertError from 'components/ui/AlertError'
import { AlphaNotice } from 'components/ui/AlphaNotice'
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
import { useAnalyticsBucketsQuery } from 'data/storage/analytics-buckets-query'
import { AnalyticsBucket as AnalyticsBucketIcon } from 'icons'
@@ -23,7 +24,6 @@ import {
} from 'ui'
import { TimestampInfo } from 'ui-patterns'
import { Input } from 'ui-patterns/DataInputs/Input'
import { AlphaNotice } from '../AlphaNotice'
import { EmptyBucketState } from '../EmptyBucketState'
import { CreateAnalyticsBucketModal } from './CreateAnalyticsBucketModal'
@@ -64,7 +64,10 @@ export const AnalyticsBuckets = () => {
return (
<ScaffoldSection isFullWidth>
<AlphaNotice type="analytics" />
<AlphaNotice
entity="Analytics buckets"
feedbackUrl="https://github.com/orgs/supabase/discussions/40116"
/>
{isLoadingBuckets && <GenericSkeletonLoader />}

View File

@@ -1,13 +1,20 @@
import { ScaffoldSection } from 'components/layouts/Scaffold'
import { AlphaNotice } from 'components/ui/AlphaNotice'
import { UpgradePlanButton } from 'components/ui/UpgradePlanButton'
import { AnalyticsBucket as AnalyticsBucketIcon, VectorBucket as VectorBucketIcon } from 'icons'
import { AlphaNotice } from './AlphaNotice'
import { BUCKET_TYPES } from './Storage.constants'
export const BucketsUpgradePlan = ({ type }: { type: 'analytics' | 'vector' }) => {
return (
<ScaffoldSection isFullWidth>
<AlphaNotice type={type} />
<AlphaNotice
entity={type === 'analytics' ? 'Analytics buckets' : 'Vector buckets'}
feedbackUrl={
type === 'analytics'
? 'https://github.com/orgs/supabase/discussions/40116'
: 'https://github.com/orgs/supabase/discussions/40815'
}
/>
<aside className="border border-dashed w-full bg-surface-100 rounded-lg px-4 py-10 flex flex-col gap-y-4 items-center text-center gap-1 text-balance">
{type === 'analytics' ? (
<AnalyticsBucketIcon size={24} strokeWidth={1.5} className="text-foreground-muted" />

View File

@@ -5,6 +5,7 @@ import { useState, type KeyboardEvent, type MouseEvent } from 'react'
import { useParams } from 'common'
import { ScaffoldHeader, ScaffoldSection, ScaffoldSectionTitle } from 'components/layouts/Scaffold'
import AlertError from 'components/ui/AlertError'
import { AlphaNotice } from 'components/ui/AlphaNotice'
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
import { useVectorBucketsQuery } from 'data/storage/vector-buckets-query'
import { VectorBucket as VectorBucketIcon } from 'icons'
@@ -12,7 +13,6 @@ import { BASE_PATH } from 'lib/constants'
import { Card, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from 'ui'
import { Input } from 'ui-patterns/DataInputs/Input'
import { TimestampInfo } from 'ui-patterns/TimestampInfo'
import { AlphaNotice } from '../AlphaNotice'
import { EmptyBucketState } from '../EmptyBucketState'
import { CreateVectorBucketDialog } from './CreateVectorBucketDialog'
@@ -54,7 +54,10 @@ export const VectorsBuckets = () => {
return (
<ScaffoldSection isFullWidth>
<AlphaNotice type="vector" />
<AlphaNotice
entity="Vector buckets"
feedbackUrl="https://github.com/orgs/supabase/discussions/40815"
/>
{isLoadingBuckets && <GenericSkeletonLoader />}

View File

@@ -2,6 +2,7 @@ import { useRouter } from 'next/router'
import { PropsWithChildren } from 'react'
import { useIsColumnLevelPrivilegesEnabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext'
import { useIsETLPrivateAlpha } from 'components/interfaces/Database/Replication/useIsETLPrivateAlpha'
import { ProductMenu } from 'components/ui/ProductMenu'
import { useDatabaseExtensionsQuery } from 'data/database-extensions/database-extensions-query'
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
@@ -30,6 +31,7 @@ const DatabaseProductMenu = () => {
const pgNetExtensionExists = (data ?? []).find((ext) => ext.name === 'pg_net') !== undefined
const pitrEnabled = addons?.selected_addons.find((addon) => addon.type === 'pitr') !== undefined
const columnLevelPrivileges = useIsColumnLevelPrivilegesEnabled()
const enablePgReplicate = useIsETLPrivateAlpha()
const {
databaseReplication: showPgReplicate,
@@ -46,6 +48,7 @@ const DatabaseProductMenu = () => {
pitrEnabled,
columnLevelPrivileges,
showPgReplicate,
enablePgReplicate,
showRoles,
showWrappers,
})}

View File

@@ -11,6 +11,7 @@ export const generateDatabaseMenu = (
pitrEnabled: boolean
columnLevelPrivileges: boolean
showPgReplicate: boolean
enablePgReplicate: boolean
showRoles: boolean
showWrappers: boolean
}
@@ -21,6 +22,7 @@ export const generateDatabaseMenu = (
pitrEnabled,
columnLevelPrivileges,
showPgReplicate,
enablePgReplicate,
showRoles,
showWrappers,
} = flags || {}
@@ -110,6 +112,7 @@ export const generateDatabaseMenu = (
name: 'Replication',
key: 'replication',
url: `/project/${ref}/database/replication`,
label: enablePgReplicate ? 'New' : undefined,
items: [],
},
]

View File

@@ -5,7 +5,12 @@ import { BASE_PATH } from 'lib/constants'
import { Badge, Button } from 'ui'
import { Admonition } from 'ui-patterns'
export const AlphaNotice = ({ type }: { type: 'analytics' | 'vector' }) => {
interface AlphaNoticeProps {
entity: string
feedbackUrl: string
}
export const AlphaNotice = ({ entity, feedbackUrl }: AlphaNoticeProps) => {
return (
<Admonition showIcon={false} type="tip" className="relative mb-6 overflow-hidden">
<div className="absolute -inset-16 z-0 opacity-50">
@@ -28,25 +33,16 @@ export const AlphaNotice = ({ type }: { type: 'analytics' | 'vector' }) => {
<Badge variant="success" className="-ml-0.5 uppercase">
New
</Badge>
<p className="text-sm font-medium">Introducing {type} buckets</p>
<p className="text-sm font-medium">Introducing {entity.toLocaleLowerCase()}</p>
</div>
<p className="text-sm text-foreground-lighter text-balance">
<span className="capitalize">{type}</span> buckets are now in private alpha. Expect
rapid changes, limited features, and possible breaking updates. Please share feedback as
we refine the experience and expand access.
{entity} {entity.endsWith('s') ? 'are' : 'is'} now in private alpha. Expect rapid
changes, limited features, and possible breaking updates. Please share feedback as we
refine the experience and expand access.
</p>
</div>
<Button asChild type="default" icon={<ExternalLink strokeWidth={1.5} />} className="mt-2">
<Link
target="_blank"
rel="noopener noreferrer"
href={
type === 'analytics'
? 'https://github.com/orgs/supabase/discussions/40116'
: // [Joshen] To update with Vector specific GH discussion
'https://github.com/orgs/supabase/discussions/40815'
}
>
<Link target="_blank" rel="noopener noreferrer" href={feedbackUrl}>
Share feedback
</Link>
</Button>

View File

@@ -38,7 +38,7 @@ const ProductMenuItem = ({
>
<span className="truncate flex-1">{name}</span>
{label !== undefined && (
<Badge variant="warning" size="tiny">
<Badge variant={label.toLowerCase() === 'new' ? 'default' : 'warning'} size="small">
{label}
</Badge>
)}

View File

@@ -5,6 +5,7 @@ import { useIsETLPrivateAlpha } from 'components/interfaces/Database/Replication
import DatabaseLayout from 'components/layouts/DatabaseLayout/DatabaseLayout'
import DefaultLayout from 'components/layouts/DefaultLayout'
import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold'
import { AlphaNotice } from 'components/ui/AlphaNotice'
import { FormHeader } from 'components/ui/Forms/FormHeader'
import { UnknownInterface } from 'components/ui/UnknownInterface'
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
@@ -27,10 +28,18 @@ const DatabaseReplicationPage: NextPageWithLayout = () => {
<ScaffoldContainer>
<ScaffoldSection>
<div className="col-span-12">
<FormHeader
className="[&>div>p]:max-w-full"
title="Replication"
description="Automatically replicate your database changes to external data warehouses and analytics platforms in real-time"
<div className="w-full mb-6">
<div className="flex items-center gap-x-2 mb-1">
<h3 className="text-foreground text-xl prose">Replication</h3>
</div>
<p className="prose text-sm max-w-full">
Automatically replicate your database changes to external data warehouses and
analytics platforms in real-time
</p>
</div>
<AlphaNotice
entity="Replication"
feedbackUrl="https://github.com/orgs/supabase/discussions/39416"
/>
<Destinations />
</div>