feat(studio): refine pipeline creation copy (#50751)

## Problem

The pipeline creation sheet uses generic examples, inconsistent
destination terminology, and Advanced settings copy that does not
explain what is being overridden. Its Docs button also sends most
destinations to the general Pipelines guide.

This PR is stacked on #50719 so the shared copy builds on the focused
Snowflake field improvements.

## Solution

Adds destination-specific pipeline-name examples and field descriptions,
clarifies destination summaries and Advanced settings, uses
human-readable ClickHouse engine names, shortens the primary action to
**Start pipeline**, and links the Docs button to the selected
destination guide.

## Review instructions

1. Open **[Database
Replication](https://studio-staging-git-dnywh-studiorefine-pipeline-1eaf59-supabase.vercel.app/dashboard/project/_/database/replication)**
and click **Add pipeline**.
2. Switch between destination types and confirm the pipeline-name
example, destination summary, and field descriptions update
appropriately.
3. Open **Advanced settings** and confirm the batch wait-time default is
shown in the description while the input placeholder is `10000`.
4. Select each supported destination and confirm **Docs** opens its
matching destination guide.
5. Select ClickHouse and confirm the engine choices read
**ReplacingMergeTree** and **MergeTree**.

## Checklist

- [x] I have read
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
- [x] If I wrote a new docs topic or edited an existing topic, I used
the `/write-the-docs` or `/edit-the-docs` skill, which references
[WORD_LIST](https://github.com/supabase/supabase/blob/master/apps/docs/WORD_LIST.md)
and the docs
[CONTRIBUTING](https://github.com/supabase/supabase/blob/master/apps/docs/CONTRIBUTING.md)
guide


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

## Summary by CodeRabbit

* **Updates**
* Clarified replication settings, destination field descriptions, and
batch wait-time guidance.
* Added destination-specific pipeline name placeholders and updated the
labels for ClickHouse engine options.
  * Clarified ClickHouse password guidance for new destinations.
* Updated new-pipeline buttons to say “Start pipeline” or “Start
pipeline anyway.”
* Added destination-specific documentation links for BigQuery,
ClickHouse, DuckLake, and Snowflake.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Danny White authored and GitHub committed 2026-09-28 10:59:27 +10:00
1 parent 9fb173e827
commit fd0918833f
11 files changed
+197 -73

No files matched your search

@@ -55,7 +55,7 @@ export const AdvancedSettings = ({
<div className="flex flex-col items-start gap-0.5">
<span className="text-sm font-medium">Advanced settings</span>
<span className="text-sm text-foreground-lighter font-normal">
Optional settings to control the pipeline in more depth
Customize how the pipeline syncs and replicates data.
</span>
</div>
</AccordionTrigger>
@@ -67,7 +67,7 @@ export const AdvancedSettings = ({
<FormItemLayout
layout="horizontal"
label="Batch wait time"
description="How long the pipeline waits before sending a partially filled batch."
description="Maximum time before sending a partially filled batch."
>
<FormControl>
<InputGroup>
@@ -78,7 +78,7 @@ export const AdvancedSettings = ({
step={1}
value={field.value ?? ''}
onChange={handleNumberChange(field)}
placeholder={`Default: ${DEFAULT_MAX_FILL_MS}`}
placeholder={String(DEFAULT_MAX_FILL_MS)}
/>
<InputGroupAddon align="inline-end">
<InputGroupText>milliseconds</InputGroupText>
@@ -25,6 +25,10 @@ import {
STORED_SECRET_PLACEHOLDER,
} from '../DestinationForm.constants'
import type { DestinationPanelSchemaType } from '../DestinationForm.schema'
import {
ANALYTICS_BUCKET_BUCKET_FIELD_COPY,
ANALYTICS_BUCKET_NAMESPACE_FIELD_COPY,
} from '../DestinationFormFieldCopy'
import {
isMetadataListErrorVisible,
isMetadataListLoading,
@@ -155,9 +159,9 @@ export const AnalyticsBucketFields = ({
name="warehouseName"
render={({ field }) => (
<FormItemLayout
label="Bucket"
label={ANALYTICS_BUCKET_BUCKET_FIELD_COPY.label}
layout="horizontal"
description="The Analytics Bucket where data will be stored"
description={ANALYTICS_BUCKET_BUCKET_FIELD_COPY.description}
>
<FormControl>
<Select
@@ -207,9 +211,9 @@ export const AnalyticsBucketFields = ({
name="namespace"
render={({ field }) => (
<FormItemLayout
label="Namespace"
label={ANALYTICS_BUCKET_NAMESPACE_FIELD_COPY.label}
layout="horizontal"
description="The namespace within the bucket where tables will be organized"
description={ANALYTICS_BUCKET_NAMESPACE_FIELD_COPY.description}
>
<FormControl>
<Select
@@ -6,6 +6,10 @@ import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import { STORED_SECRET_PLACEHOLDER } from '../DestinationForm.constants'
import type { DestinationPanelSchemaType } from '../DestinationForm.schema'
import {
BIGQUERY_DATASET_ID_FIELD_COPY,
BIGQUERY_PROJECT_ID_FIELD_COPY,
} from '../DestinationFormFieldCopy'
import { MAX_SERVICE_ACCOUNT_KEY_LENGTH, readServiceAccountFile } from './BigQuery.utils'
export const BigQueryFields = ({
@@ -60,8 +64,8 @@ export const BigQueryFields = ({
render={({ field }) => (
<FormItemLayout
layout="horizontal"
label="Project ID"
description="The Google Cloud project ID where data will be sent"
label={BIGQUERY_PROJECT_ID_FIELD_COPY.label}
description={BIGQUERY_PROJECT_ID_FIELD_COPY.description}
>
<FormControl>
<Input {...field} placeholder="my-gcp-project" />
@@ -75,9 +79,9 @@ export const BigQueryFields = ({
name="datasetId"
render={({ field }) => (
<FormItemLayout
label="Dataset ID"
label={BIGQUERY_DATASET_ID_FIELD_COPY.label}
layout="horizontal"
description="The BigQuery dataset where replicated tables will be created"
description={BIGQUERY_DATASET_ID_FIELD_COPY.description}
>
<FormControl>
<Input {...field} placeholder="my_dataset" />
@@ -16,6 +16,11 @@ import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import { STORED_SECRET_PLACEHOLDER } from '../DestinationForm.constants'
import type { DestinationPanelSchemaType } from '../DestinationForm.schema'
import {
CLICKHOUSE_DATABASE_FIELD_COPY,
CLICKHOUSE_ENGINE_FIELD_COPY,
CLICKHOUSE_URL_FIELD_COPY,
} from '../DestinationFormFieldCopy'
export const ClickHouseFields = ({
form,
@@ -37,8 +42,8 @@ export const ClickHouseFields = ({
render={({ field }) => (
<FormItemLayout
layout="horizontal"
label="URL"
description="HTTPS endpoint for your ClickHouse server, including port"
label={CLICKHOUSE_URL_FIELD_COPY.label}
description={CLICKHOUSE_URL_FIELD_COPY.description}
>
<FormControl>
<Input
@@ -58,7 +63,7 @@ export const ClickHouseFields = ({
<FormItemLayout
layout="horizontal"
label="User"
description="ClickHouse user with permission to write to the target database"
description="ClickHouse user with permission to write to the target database."
>
<FormControl>
<Input {...field} value={field.value ?? ''} placeholder="default" />
@@ -74,17 +79,18 @@ export const ClickHouseFields = ({
<FormItemLayout
layout="horizontal"
label="Password"
labelOptional="Optional"
description={
editMode
? 'Stored password is hidden. Enter a new password to replace it.'
: 'Omit for passwordless access'
: 'Leave blank for passwordless access.'
}
>
<FormControl>
<PasswordInput
value={field.value ?? ''}
type={showPassword ? 'text' : 'password'}
placeholder={editMode ? STORED_SECRET_PLACEHOLDER : 'Optional'}
placeholder={editMode ? STORED_SECRET_PLACEHOLDER : undefined}
onChange={(event) => field.onChange(event.target.value)}
actions={
<div className="flex items-center justify-center">
@@ -109,8 +115,8 @@ export const ClickHouseFields = ({
render={({ field }) => (
<FormItemLayout
layout="horizontal"
label="Database"
description="The ClickHouse database where replicated tables will be created"
label={CLICKHOUSE_DATABASE_FIELD_COPY.label}
description={CLICKHOUSE_DATABASE_FIELD_COPY.description}
>
<FormControl>
<Input {...field} value={field.value ?? ''} placeholder="default" />
@@ -125,18 +131,20 @@ export const ClickHouseFields = ({
render={({ field }) => (
<FormItemLayout
layout="horizontal"
label="Table engine"
description="Server defaults to replacing_merge_tree when unset"
label={CLICKHOUSE_ENGINE_FIELD_COPY.label}
description={CLICKHOUSE_ENGINE_FIELD_COPY.description}
>
<FormControl>
<Select
value={field.value ?? 'replacing_merge_tree'}
onValueChange={field.onChange}
>
<SelectTrigger>{field.value ?? 'replacing_merge_tree'}</SelectTrigger>
<SelectTrigger>
{field.value === 'merge_tree' ? 'MergeTree' : 'ReplacingMergeTree'}
</SelectTrigger>
<SelectContent>
<SelectItem value="replacing_merge_tree">replacing_merge_tree</SelectItem>
<SelectItem value="merge_tree">merge_tree</SelectItem>
<SelectItem value="replacing_merge_tree">ReplacingMergeTree</SelectItem>
<SelectItem value="merge_tree">MergeTree</SelectItem>
</SelectContent>
</Select>
</FormControl>
@@ -0,0 +1,81 @@
export const PIPELINE_NAME_FIELD_COPY = {
label: 'Pipeline name',
description: 'Used to identify this pipeline in Supabase.',
} as const
export const BIGQUERY_PROJECT_ID_FIELD_COPY = {
label: 'Project ID',
description: 'The Google Cloud project ID where data will be sent.',
} as const
export const BIGQUERY_DATASET_ID_FIELD_COPY = {
label: 'Dataset ID',
description: 'The BigQuery dataset where replicated tables will be created.',
} as const
export const ANALYTICS_BUCKET_BUCKET_FIELD_COPY = {
label: 'Bucket',
description: 'The Analytics Bucket where data will be stored.',
} as const
export const ANALYTICS_BUCKET_NAMESPACE_FIELD_COPY = {
label: 'Namespace',
description: 'The namespace within the bucket where tables will be organized.',
} as const
export const DUCKLAKE_CATALOG_PROJECT_FIELD_COPY = {
label: 'Catalog project',
description:
"Pipelines connects to this project's Postgres instance to store the DuckLake catalog.",
} as const
export const DUCKLAKE_STORAGE_PROJECT_FIELD_COPY = {
label: 'Storage project',
description: 'The project whose object storage holds the DuckLake data files.',
} as const
export const DUCKLAKE_BUCKET_FIELD_COPY = {
label: 'Bucket',
description: 'The bucket in which DuckLake data files will be stored.',
} as const
export const DUCKLAKE_CATALOG_URL_FIELD_COPY = {
label: 'Catalog URL',
createDescription: 'A Postgres connection string for the DuckLake catalog.',
editDescription: 'Stored catalog URL is hidden. Enter a new URL to replace it.',
} as const
export const DUCKLAKE_DATA_PATH_FIELD_COPY = {
label: 'Data path',
description: 'An S3 path where DuckLake data files will be written.',
} as const
export const SNOWFLAKE_ACCOUNT_ID_FIELD_COPY = {
label: 'Account ID',
description: 'Snowflake organization and account identifiers joined with a hyphen.',
} as const
export const SNOWFLAKE_DATABASE_FIELD_COPY = {
label: 'Database',
description: 'Snowflake database where replicated tables are created.',
} as const
export const SNOWFLAKE_SCHEMA_FIELD_COPY = {
label: 'Schema',
description: 'An empty Snowflake schema where replicated tables are created.',
} as const
export const CLICKHOUSE_URL_FIELD_COPY = {
label: 'HTTPS URL',
description: 'The HTTPS endpoint for your ClickHouse server, including port.',
} as const
export const CLICKHOUSE_DATABASE_FIELD_COPY = {
label: 'Database',
description: 'The ClickHouse database where replicated tables will be created.',
} as const
export const CLICKHOUSE_ENGINE_FIELD_COPY = {
label: 'Table engine',
description: 'Defaults to ReplacingMergeTree.',
} as const
@@ -2,28 +2,39 @@ import type { UseFormReturn } from 'react-hook-form'
import { FormControl, FormField, Input } from 'ui'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import type { DestinationType } from '../DestinationPanel.types'
import type { DestinationPanelSchemaType } from './DestinationForm.schema'
import { PIPELINE_NAME_FIELD_COPY } from './DestinationFormFieldCopy'
const PIPELINE_NAME_PLACEHOLDERS: Record<DestinationType, string> = {
BigQuery: 'Analytics Pipeline',
'Analytics Bucket': 'Iceberg Pipeline',
DuckLake: 'Lakehouse Pipeline',
Snowflake: 'Data Warehouse Pipeline',
ClickHouse: 'Live Analytics Pipeline',
}
type DestinationNameInputProps = {
form: UseFormReturn<DestinationPanelSchemaType>
destinationType: DestinationType
}
export const DestinationNameInput = ({ form }: DestinationNameInputProps) => {
export const DestinationNameInput = ({ form, destinationType }: DestinationNameInputProps) => {
return (
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItemLayout
label="Name"
label={PIPELINE_NAME_FIELD_COPY.label}
layout="horizontal"
description="Used to identify this pipeline in Supabase."
description={PIPELINE_NAME_FIELD_COPY.description}
>
<FormControl>
<Input
{...field}
autoFocus
placeholder="My destination"
placeholder={PIPELINE_NAME_PLACEHOLDERS[destinationType]}
data-1p-ignore
data-lpignore="true"
data-form-type="other"
@@ -28,6 +28,13 @@ import { SelectionListState } from 'ui-patterns/SelectionListState'
import { DEFAULT_DUCKLAKE_POOL_SIZE, STORED_SECRET_PLACEHOLDER } from '../DestinationForm.constants'
import type { DestinationPanelSchemaType } from '../DestinationForm.schema'
import {
DUCKLAKE_BUCKET_FIELD_COPY,
DUCKLAKE_CATALOG_PROJECT_FIELD_COPY,
DUCKLAKE_CATALOG_URL_FIELD_COPY,
DUCKLAKE_DATA_PATH_FIELD_COPY,
DUCKLAKE_STORAGE_PROJECT_FIELD_COPY,
} from '../DestinationFormFieldCopy'
import {
isMetadataListErrorVisible,
isMetadataListLoading,
@@ -57,7 +64,7 @@ const DUCKLAKE_MODE_OPTIONS = [
value: DUCKLAKE_MODE_CUSTOM,
icon: SlidersHorizontal,
label: 'Custom parameters',
description: 'Bring your own PostgreSQL catalog and S3-compatible object storage credentials.',
description: 'Bring your own Postgres catalog and S3-compatible object storage credentials.',
},
] as const
@@ -187,7 +194,7 @@ const DuckLakeSupabaseFields = ({ form }: { form: UseFormReturn<DestinationPanel
<div className="flex flex-col gap-y-1">
<p className="text-sm font-medium text-foreground">Catalog</p>
<p className="text-sm text-foreground-light">
The selected project's Postgres database is used as the PostgreSQL DuckLake catalog
The selected project's Postgres database is used as the DuckLake catalog.
</p>
</div>
@@ -197,14 +204,11 @@ const DuckLakeSupabaseFields = ({ form }: { form: UseFormReturn<DestinationPanel
render={({ field }) => (
<FormItemLayout
layout="horizontal"
label="Catalog project"
label={DUCKLAKE_CATALOG_PROJECT_FIELD_COPY.label}
description={
<div className="flex flex-col gap-y-2">
{renderRegionWarning(field.value)}
<span>
Pipelines connects to this project's Postgres instance to store the DuckLake
catalog
</span>
<span>{DUCKLAKE_CATALOG_PROJECT_FIELD_COPY.description}</span>
</div>
}
>
@@ -226,7 +230,7 @@ const DuckLakeSupabaseFields = ({ form }: { form: UseFormReturn<DestinationPanel
<FormItemLayout
layout="horizontal"
label="Pool size"
description="Number of concurrent DuckDB connections to the catalog"
description="Number of concurrent DuckDB connections to the catalog."
>
<FormControl>
<Input
@@ -251,7 +255,7 @@ const DuckLakeSupabaseFields = ({ form }: { form: UseFormReturn<DestinationPanel
<FormItemLayout
layout="horizontal"
label="Metadata schema"
description="Schema used for DuckLake metadata tables in the catalog's Postgres"
description="Schema used for DuckLake metadata tables in the catalog's Postgres."
>
<FormControl>
<Input {...field} placeholder="ducklake" value={field.value ?? ''} />
@@ -273,11 +277,11 @@ const DuckLakeSupabaseFields = ({ form }: { form: UseFormReturn<DestinationPanel
render={({ field }) => (
<FormItemLayout
layout="horizontal"
label="Storage project"
label={DUCKLAKE_STORAGE_PROJECT_FIELD_COPY.label}
description={
<div className="flex flex-col gap-y-2">
{renderRegionWarning(field.value)}
<span>The project whose object storage holds the DuckLake data files</span>
<span>{DUCKLAKE_STORAGE_PROJECT_FIELD_COPY.description}</span>
</div>
}
>
@@ -302,8 +306,8 @@ const DuckLakeSupabaseFields = ({ form }: { form: UseFormReturn<DestinationPanel
render={({ field }) => (
<FormItemLayout
layout="horizontal"
label="Bucket"
description="The bucket in which DuckLake data files will be stored."
label={DUCKLAKE_BUCKET_FIELD_COPY.label}
description={DUCKLAKE_BUCKET_FIELD_COPY.description}
>
<div className="flex items-center gap-x-2">
<div className="grow">
@@ -380,7 +384,7 @@ const DuckLakeCustomFields = ({
<div className="flex flex-col gap-y-1">
<p className="text-sm font-medium text-foreground">Catalog</p>
<p className="text-sm text-foreground-light">
Configure the PostgreSQL DuckLake catalog and S3-compatible storage for replicated data
Configure the Postgres DuckLake catalog and S3-compatible storage for replicated data.
</p>
</div>
@@ -391,11 +395,11 @@ const DuckLakeCustomFields = ({
render={({ field }) => (
<FormItemLayout
layout="horizontal"
label="Catalog URL"
label={DUCKLAKE_CATALOG_URL_FIELD_COPY.label}
description={
editMode
? 'Stored catalog URL is hidden. Enter a new URL to replace it.'
: 'A PostgreSQL connection string for the DuckLake catalog'
? DUCKLAKE_CATALOG_URL_FIELD_COPY.editDescription
: DUCKLAKE_CATALOG_URL_FIELD_COPY.createDescription
}
>
<FormControl>
@@ -429,8 +433,8 @@ const DuckLakeCustomFields = ({
render={({ field }) => (
<FormItemLayout
layout="horizontal"
label="Data path"
description="An S3 path where DuckLake data files will be written"
label={DUCKLAKE_DATA_PATH_FIELD_COPY.label}
description={DUCKLAKE_DATA_PATH_FIELD_COPY.description}
>
<FormControl>
<Input {...field} placeholder="s3://bucket/path" value={field.value ?? ''} />
@@ -446,7 +450,7 @@ const DuckLakeCustomFields = ({
<FormItemLayout
layout="horizontal"
label="Pool size"
description="Number of concurrent DuckDB connections to use"
description="Number of concurrent DuckDB connections to use."
>
<FormControl>
<Input
@@ -485,7 +489,7 @@ const DuckLakeCustomFields = ({
description={
editMode
? 'Stored access key ID is hidden. Enter a new key ID to replace it.'
: 'Required access key ID for the object storage provider'
: 'Required access key ID for the object storage provider.'
}
>
<FormControl>
@@ -509,7 +513,7 @@ const DuckLakeCustomFields = ({
description={
editMode
? 'Stored secret access key is hidden. Enter a new secret to replace it.'
: 'Required secret access key for the object storage provider'
: 'Required secret access key for the object storage provider.'
}
className="relative"
>
@@ -537,7 +541,7 @@ const DuckLakeCustomFields = ({
<FormItemLayout
layout="horizontal"
label="S3 region"
description="Required region for the object storage provider"
description="Required region for the object storage provider."
>
<FormControl>
<Input {...field} placeholder="us-east-1" value={field.value ?? ''} />
@@ -553,7 +557,7 @@ const DuckLakeCustomFields = ({
<FormItemLayout
layout="horizontal"
label="S3 endpoint"
description="Required endpoint without the protocol scheme, for example `127.0.0.1:5000/s3`"
description="Required endpoint without the protocol scheme, for example `127.0.0.1:5000/s3`."
>
<FormControl>
<Input {...field} placeholder="127.0.0.1:5000/s3" value={field.value ?? ''} />
@@ -569,7 +573,7 @@ const DuckLakeCustomFields = ({
<FormItemLayout
layout="horizontal"
label="S3 URL style"
description="Choose `path` for MinIO/Supabase-style endpoints or `vhost` for AWS-style virtual host addressing"
description="Choose `path` for MinIO/Supabase-style endpoints or `vhost` for AWS-style virtual host addressing."
>
<FormControl>
<Select value={field.value ?? 'path'} onValueChange={field.onChange}>
@@ -591,7 +595,7 @@ const DuckLakeCustomFields = ({
<FormItemLayout
layout="horizontal"
label="Use SSL"
description="Whether to use SSL when connecting to the S3-compatible endpoint"
description="Whether to use SSL when connecting to the S3-compatible endpoint."
>
<FormControl>
<Select
@@ -625,7 +629,7 @@ const DuckLakeCustomFields = ({
<FormItemLayout
layout="horizontal"
label="Metadata schema"
description="Schema used for DuckLake metadata tables in PostgreSQL"
description="Schema used for DuckLake metadata tables in Postgres."
>
<FormControl>
<Input {...field} placeholder="ducklake" value={field.value ?? ''} />
@@ -7,6 +7,11 @@ import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import { STORED_SECRET_PLACEHOLDER } from '../DestinationForm.constants'
import type { DestinationPanelSchemaType } from '../DestinationForm.schema'
import {
SNOWFLAKE_ACCOUNT_ID_FIELD_COPY,
SNOWFLAKE_DATABASE_FIELD_COPY,
SNOWFLAKE_SCHEMA_FIELD_COPY,
} from '../DestinationFormFieldCopy'
import { MAX_PRIVATE_KEY_LENGTH, readPrivateKeyFile } from './Snowflake.utils'
export const SnowflakeFields = ({
@@ -70,8 +75,8 @@ export const SnowflakeFields = ({
render={({ field }) => (
<FormItemLayout
layout="horizontal"
label="Account ID"
description="Snowflake organization and account identifiers joined with a hyphen."
label={SNOWFLAKE_ACCOUNT_ID_FIELD_COPY.label}
description={SNOWFLAKE_ACCOUNT_ID_FIELD_COPY.description}
>
<FormControl>
<Input {...field} placeholder="MYORG-MYACCOUNT" value={field.value ?? ''} />
@@ -111,8 +116,8 @@ export const SnowflakeFields = ({
render={({ field }) => (
<FormItemLayout
layout="horizontal"
label="Database"
description="Snowflake database where replicated tables are created."
label={SNOWFLAKE_DATABASE_FIELD_COPY.label}
description={SNOWFLAKE_DATABASE_FIELD_COPY.description}
>
<FormControl>
<Input {...field} placeholder="PIPELINES_DB" value={field.value ?? ''} />
@@ -127,8 +132,8 @@ export const SnowflakeFields = ({
render={({ field }) => (
<FormItemLayout
layout="horizontal"
label="Schema"
description="An empty Snowflake schema where replicated tables are created."
label={SNOWFLAKE_SCHEMA_FIELD_COPY.label}
description={SNOWFLAKE_SCHEMA_FIELD_COPY.description}
>
<FormControl>
<Input {...field} placeholder="REPLICATED" value={field.value ?? ''} />
@@ -287,10 +287,10 @@ export const DestinationForm = ({
return existingDestination?.enabled ? 'Apply and restart pipeline' : 'Apply changes'
} else {
if (hasRunValidation && validationWarnings.length > 0 && !hasValidationFailures) {
return 'Create and start pipeline anyway'
return 'Start pipeline anyway'
}
return 'Create and start pipeline'
return 'Start pipeline'
}
}
@@ -474,7 +474,7 @@ export const DestinationForm = ({
<p className="text-sm font-medium text-foreground">Destination details</p>
<div className="flex flex-col gap-y-4">
<DestinationNameInput form={form} />
<DestinationNameInput form={form} destinationType={selectedType} />
<PublicationSelection
form={form}
onSelectNewPublication={() => setPublicationPanelVisible(true)}
@@ -31,6 +31,14 @@ import { checkLocalETLNotSetUp } from '@/data/replication/utils'
import { useConfirmOnClose } from '@/hooks/ui/useConfirmOnClose'
import { DOCS_URL } from '@/lib/constants'
const DESTINATION_DOCS_PATHS: Partial<Record<DestinationType, string>> = {
BigQuery: '/guides/database/replication/pipelines/bigquery#configure-bigquery-as-a-destination',
ClickHouse:
'/guides/database/replication/pipelines/clickhouse#configure-clickhouse-as-a-destination',
DuckLake: '/guides/database/replication/pipelines/ducklake#choose-a-configuration-mode',
Snowflake: '/guides/database/replication/pipelines/snowflake#prepare-snowflake-resources',
}
export const DestinationPanel = () => {
const { ref: projectRef } = useParams()
const enablePgReplicate = useIsETLPrivateAlpha()
@@ -97,10 +105,10 @@ export const DestinationPanel = () => {
onClose,
})
const docsUrl =
destinationType === 'BigQuery'
? `${DOCS_URL}/guides/database/replication/bigquery#configure-bigquery-as-a-destination`
: `${DOCS_URL}/guides/database/replication/pipelines#step-3-configure-a-destination`
const docsUrl = `${DOCS_URL}${
(destinationType && DESTINATION_DOCS_PATHS[destinationType]) ??
'/guides/database/replication/pipelines#step-3-configure-a-destination'
}`
useEffect(() => {
if (edit !== null && invalidExistingDestination) {
@@ -74,7 +74,7 @@ export const DestinationTypeSelection = () => {
{
value: 'BigQuery',
label: 'BigQuery',
description: "Replicate changes to Google Cloud's data warehouse for analytics and BI",
description: 'Replicate changes to BigQuery for analytics and BI',
stage: 'Public Alpha',
enabled: isOptionVisible('BigQuery', etlEnableBigQuery),
},
@@ -86,22 +86,21 @@ export const DestinationTypeSelection = () => {
{
value: 'DuckLake',
label: 'DuckLake',
description: 'Replicate changes to a DuckLake catalog backed by S3-compatible storage',
description: 'Replicate changes to DuckLake for open lakehouse storage',
stage: 'Early Access',
enabled: isOptionVisible('DuckLake', etlEnableDucklake),
},
{
value: 'Snowflake',
label: 'Snowflake',
description:
'Replicate changes to Snowflake for warehouse analytics and downstream data workflows',
description: 'Replicate changes to Snowflake for cloud data warehousing',
stage: 'Early Access',
enabled: isOptionVisible('Snowflake', etlEnableSnowflake),
},
{
value: 'ClickHouse',
label: 'ClickHouse',
description: 'Stream changes to a ClickHouse cluster for fast columnar analytics',
description: 'Replicate changes to ClickHouse for real-time analytics',
stage: 'Early Access',
enabled: isOptionVisible('ClickHouse', etlEnableClickHouse),
},
@@ -113,7 +112,7 @@ export const DestinationTypeSelection = () => {
{
value: 'Analytics Bucket',
label: 'Analytics Bucket',
description: 'Write Apache Iceberg tables to Supabase Storage for analytics workflows',
description: 'Replicate changes to Supabase Storage as Apache Iceberg tables',
stage: 'Deprecated',
enabled: isOptionVisible('Analytics Bucket', etlEnableIceberg),
},