diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/AdvancedSettings.tsx b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/AdvancedSettings.tsx index 40eac139cf7..4c706a8ca98 100644 --- a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/AdvancedSettings.tsx +++ b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/AdvancedSettings.tsx @@ -55,7 +55,7 @@ export const AdvancedSettings = ({
Advanced settings - Optional settings to control the pipeline in more depth + Customize how the pipeline syncs and replicates data.
@@ -67,7 +67,7 @@ export const AdvancedSettings = ({ @@ -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)} /> milliseconds diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/AnalyticsBucket/Fields.tsx b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/AnalyticsBucket/Fields.tsx index 5e70348f9eb..2d7f789dfd8 100644 --- a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/AnalyticsBucket/Fields.tsx +++ b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/AnalyticsBucket/Fields.tsx @@ -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 }) => ( ( @@ -75,9 +79,9 @@ export const BigQueryFields = ({ name="datasetId" render={({ field }) => ( diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/ClickHouse/Fields.tsx b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/ClickHouse/Fields.tsx index decfb4b2da5..e4bd1f3988f 100644 --- a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/ClickHouse/Fields.tsx +++ b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/ClickHouse/Fields.tsx @@ -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 }) => ( @@ -74,17 +79,18 @@ export const ClickHouseFields = ({ field.onChange(event.target.value)} actions={
@@ -109,8 +115,8 @@ export const ClickHouseFields = ({ render={({ field }) => ( @@ -125,18 +131,20 @@ export const ClickHouseFields = ({ render={({ field }) => ( diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/DestinationFormFieldCopy.ts b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/DestinationFormFieldCopy.ts new file mode 100644 index 00000000000..a4e144d3594 --- /dev/null +++ b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/DestinationFormFieldCopy.ts @@ -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 diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/DestinationNameInput.tsx b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/DestinationNameInput.tsx index af5789b8934..54ff10e3a9f 100644 --- a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/DestinationNameInput.tsx +++ b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/DestinationNameInput.tsx @@ -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 = { + BigQuery: 'Analytics Pipeline', + 'Analytics Bucket': 'Iceberg Pipeline', + DuckLake: 'Lakehouse Pipeline', + Snowflake: 'Data Warehouse Pipeline', + ClickHouse: 'Live Analytics Pipeline', +} type DestinationNameInputProps = { form: UseFormReturn + destinationType: DestinationType } -export const DestinationNameInput = ({ form }: DestinationNameInputProps) => { +export const DestinationNameInput = ({ form, destinationType }: DestinationNameInputProps) => { return ( (

Catalog

- 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.

@@ -197,14 +204,11 @@ const DuckLakeSupabaseFields = ({ form }: { form: UseFormReturn ( {renderRegionWarning(field.value)} - - Pipelines connects to this project's Postgres instance to store the DuckLake - catalog - + {DUCKLAKE_CATALOG_PROJECT_FIELD_COPY.description} } > @@ -226,7 +230,7 @@ const DuckLakeSupabaseFields = ({ form }: { form: UseFormReturn @@ -273,11 +277,11 @@ const DuckLakeSupabaseFields = ({ form }: { form: UseFormReturn ( {renderRegionWarning(field.value)} - The project whose object storage holds the DuckLake data files + {DUCKLAKE_STORAGE_PROJECT_FIELD_COPY.description} } > @@ -302,8 +306,8 @@ const DuckLakeSupabaseFields = ({ form }: { form: UseFormReturn (
@@ -380,7 +384,7 @@ const DuckLakeCustomFields = ({

Catalog

- Configure the PostgreSQL DuckLake catalog and S3-compatible storage for replicated data + Configure the Postgres DuckLake catalog and S3-compatible storage for replicated data.

@@ -391,11 +395,11 @@ const DuckLakeCustomFields = ({ render={({ field }) => ( @@ -429,8 +433,8 @@ const DuckLakeCustomFields = ({ render={({ field }) => ( @@ -446,7 +450,7 @@ const DuckLakeCustomFields = ({ @@ -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 = ({ @@ -553,7 +557,7 @@ const DuckLakeCustomFields = ({ @@ -569,7 +573,7 @@ const DuckLakeCustomFields = ({ diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/Snowflake/Fields.tsx b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/Snowflake/Fields.tsx index 8a7cff2a386..137f9fde687 100644 --- a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/Snowflake/Fields.tsx +++ b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/Snowflake/Fields.tsx @@ -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 }) => ( @@ -111,8 +116,8 @@ export const SnowflakeFields = ({ render={({ field }) => ( @@ -127,8 +132,8 @@ export const SnowflakeFields = ({ render={({ field }) => ( diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/index.tsx b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/index.tsx index 29ac41fd49f..59a22fac1d3 100644 --- a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/index.tsx +++ b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationForm/index.tsx @@ -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 = ({

Destination details

- + setPublicationPanelVisible(true)} diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationPanel.tsx b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationPanel.tsx index 17821243c46..15ca208b799 100644 --- a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationPanel.tsx +++ b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationPanel.tsx @@ -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> = { + 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) { diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationTypeSelection.tsx b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationTypeSelection.tsx index 4747a703566..f7c2eeb0279 100644 --- a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationTypeSelection.tsx +++ b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationTypeSelection.tsx @@ -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), },