Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/design-system/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"scripts": {
"preinstall": "npx only-allow pnpm",
"dev": "next dev --turbopack --port 3003",
"build": "pnpm run content:build && pnpm run build:registry && next build",
"build": "pnpm run content:build && pnpm run build:registry && next build --turbopack",
"build:registry": "tsx --tsconfig ./tsconfig.scripts.json ./scripts/build-registry.mts && prettier --log-level silent --write \"registry/**/*.{ts,tsx,mdx}\" --cache",
"start": "next start",
"lint": "next lint",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import * as z from 'zod'

import { useParams } from 'common'
import { useCreateDestinationPipelineMutation } from 'data/replication/create-destination-pipeline-mutation'
import { useCreateTenantSourceMutation } from 'data/replication/create-tenant-source-mutation'
import { useReplicationDestinationByIdQuery } from 'data/replication/destination-by-id-query'
import { useReplicationPipelineByIdQuery } from 'data/replication/pipeline-by-id-query'
import { useReplicationPublicationsQuery } from 'data/replication/publications-query'
Expand All @@ -21,9 +20,6 @@ import {
AccordionContent_Shadcn_,
AccordionItem_Shadcn_,
AccordionTrigger_Shadcn_,
Alert_Shadcn_,
AlertDescription_Shadcn_,
AlertTitle_Shadcn_,
Button,
Form_Shadcn_,
FormControl_Shadcn_,
Expand All @@ -43,7 +39,6 @@ import {
SheetSection,
SheetTitle,
TextArea_Shadcn_,
WarningIcon,
} from 'ui'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import NewPublicationPanel from './NewPublicationPanel'
Expand All @@ -60,7 +55,6 @@ const FormSchema = z.object({
datasetId: z.string().min(1, 'Dataset id is required'),
serviceAccountKey: z.string().min(1, 'Service account key is required'),
publicationName: z.string().min(1, 'Publication is required'),
maxSize: z.number().min(1, 'Max Size must be greater than 0').int().optional(),
maxFillMs: z.number().min(1, 'Max Fill milliseconds should be greater than 0').int().optional(),
maxStalenessMins: z.number().nonnegative().optional(),
})
Expand Down Expand Up @@ -90,9 +84,6 @@ export const DestinationPanel = ({
const editMode = !!existingDestination
const [publicationPanelVisible, setPublicationPanelVisible] = useState(false)

const { mutateAsync: createTenantSource, isLoading: creatingTenantSource } =
useCreateTenantSourceMutation()

const { mutateAsync: createDestinationPipeline, isLoading: creatingDestinationPipeline } =
useCreateDestinationPipelineMutation({
onSuccess: () => form.reset(defaultValues),
Expand Down Expand Up @@ -129,7 +120,6 @@ export const DestinationPanel = ({
// For now, the password will always be set as empty for security reasons.
serviceAccountKey: destinationData?.config?.big_query?.service_account_key ?? '',
publicationName: pipelineData?.config.publication_name ?? '',
maxSize: pipelineData?.config?.batch?.max_size,
maxFillMs: pipelineData?.config?.batch?.max_fill_ms,
maxStalenessMins: destinationData?.config?.big_query?.max_staleness_mins,
}),
Expand Down Expand Up @@ -162,9 +152,8 @@ export const DestinationPanel = ({
}

const batchConfig: any = {}
if (!!data.maxSize) batchConfig.maxSize = data.maxSize
if (!!data.maxFillMs) batchConfig.maxFillMs = data.maxFillMs
const hasBothBatchFields = Object.keys(batchConfig).length === 2
const hasBatchFields = Object.keys(batchConfig).length > 0

await updateDestinationPipeline({
destinationId: existingDestination.destinationId,
Expand All @@ -174,7 +163,7 @@ export const DestinationPanel = ({
destinationConfig: { bigQuery: bigQueryConfig },
pipelineConfig: {
publicationName: data.publicationName,
...(hasBothBatchFields ? { batch: batchConfig } : {}),
...(hasBatchFields ? { batch: batchConfig } : {}),
},
sourceId,
})
Expand Down Expand Up @@ -209,9 +198,8 @@ export const DestinationPanel = ({
}

const batchConfig: any = {}
if (!!data.maxSize) batchConfig.maxSize = data.maxSize
if (!!data.maxFillMs) batchConfig.maxFillMs = data.maxFillMs
const hasBothBatchFields = Object.keys(batchConfig).length === 2
const hasBatchFields = Object.keys(batchConfig).length > 0

const { pipeline_id: pipelineId } = await createDestinationPipeline({
projectRef,
Expand All @@ -220,7 +208,7 @@ export const DestinationPanel = ({
sourceId,
pipelineConfig: {
publicationName: data.publicationName,
...(hasBothBatchFields ? { batch: batchConfig } : {}),
...(hasBatchFields ? { batch: batchConfig } : {}),
},
})
// Set request status only right before starting, then fire and close
Expand All @@ -235,11 +223,6 @@ export const DestinationPanel = ({
}
}

const onEnableReplication = async () => {
if (!projectRef) return console.error('Project ref is required')
await createTenantSource({ projectRef })
}

useEffect(() => {
if (editMode && destinationData && pipelineData) {
form.reset(defaultValues)
Expand All @@ -253,7 +236,7 @@ export const DestinationPanel = ({
}
}, [visible, defaultValues, form])

return sourceId ? (
return (
<>
<Sheet open={visible} onOpenChange={onClose}>
<SheetContent showClose={false} size="default">
Expand Down Expand Up @@ -399,31 +382,6 @@ export const DestinationPanel = ({
Advanced Settings
</AccordionTrigger_Shadcn_>
<AccordionContent_Shadcn_ asChild className="!pb-0">
<FormField_Shadcn_
control={form.control}
name="maxSize"
render={({ field }) => (
<FormItemLayout
className="mb-4"
label="Max size"
layout="vertical"
description="The maximum size of the data to send. Leave empty to use default value."
>
<FormControl_Shadcn_>
<Input_Shadcn_
{...field}
type="number"
value={field.value ?? ''}
onChange={(e) => {
const val = e.target.value
field.onChange(val === '' ? undefined : Number(val))
}}
placeholder="Leave empty for default"
/>
</FormControl_Shadcn_>
</FormItemLayout>
)}
/>
<FormField_Shadcn_
control={form.control}
name="maxFillMs"
Expand Down Expand Up @@ -496,47 +454,12 @@ export const DestinationPanel = ({
</div>
</SheetContent>
</Sheet>

<NewPublicationPanel
visible={publicationPanelVisible}
sourceId={sourceId}
onClose={() => setPublicationPanelVisible(false)}
/>
</>
) : (
<Sheet open={visible} onOpenChange={onClose}>
<SheetContent showClose={false} size="default">
<div className="flex flex-col h-full" tabIndex={-1}>
<SheetHeader>
<SheetTitle>Create a new destination</SheetTitle>
</SheetHeader>
<SheetSection className="flex-grow overflow-auto">
<Alert_Shadcn_>
<WarningIcon />
<AlertTitle_Shadcn_>
{/* Pricing to be decided yet */}
Enabling replication will cost additional $xx.xx
</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_>
<span></span>
<div className="flex items-center gap-x-2 mt-3">
<Button
type="default"
loading={creatingTenantSource}
onClick={onEnableReplication}
>
Enable replication
</Button>
</div>
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
</SheetSection>
<SheetFooter>
<Button disabled={creatingTenantSource} type="default" onClick={onClose}>
Cancel
</Button>
</SheetFooter>
</div>
</SheetContent>
</Sheet>
)
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { useQueryClient } from '@tanstack/react-query'
import { Plus, Search } from 'lucide-react'
import { useEffect, useRef, useState } from 'react'

import { useParams } from 'common'
import Table from 'components/to-be-cleaned/Table'
import AlertError from 'components/ui/AlertError'
import { AlertError } from 'components/ui/AlertError'
import { DocsButton } from 'components/ui/DocsButton'
import { useReplicationDestinationsQuery } from 'data/replication/destinations-query'
import { replicationKeys } from 'data/replication/keys'
import { fetchReplicationPipelineVersion } from 'data/replication/pipeline-version-query'
import { useReplicationPipelinesQuery } from 'data/replication/pipelines-query'
import { useReplicationSourcesQuery } from 'data/replication/sources-query'
import { fetchReplicationPipelineVersion } from 'data/replication/pipeline-version-query'
import { replicationKeys } from 'data/replication/keys'
import { useQueryClient } from '@tanstack/react-query'
import { Button, cn, Input_Shadcn_ } from 'ui'
import { GenericSkeletonLoader } from 'ui-patterns'
import { DestinationPanel } from './DestinationPanel'
import { DestinationRow } from './DestinationRow'
import { EnableReplicationModal } from './EnableReplicationModal'
import { PIPELINE_ERROR_MESSAGES } from './Pipeline.utils'

export const Destinations = () => {
Expand All @@ -26,11 +28,13 @@ export const Destinations = () => {
error: sourcesError,
isLoading: isSourcesLoading,
isError: isSourcesError,
isSuccess: isSourcesSuccess,
} = useReplicationSourcesQuery({
projectRef,
})

const sourceId = sourcesData?.sources.find((s) => s.name === projectRef)?.id
const replicationNotEnabled = isSourcesSuccess && !sourceId

const {
data: destinationsData,
Expand All @@ -52,7 +56,7 @@ export const Destinations = () => {
projectRef,
})

const anyDestinations = isDestinationsSuccess && destinationsData.destinations.length > 0
const hasDestinations = isDestinationsSuccess && destinationsData.destinations.length > 0

const filteredDestinations =
filterString.length === 0
Expand Down Expand Up @@ -103,9 +107,11 @@ export const Destinations = () => {
/>
</div>
</div>
<Button type="default" icon={<Plus />} onClick={() => setShowNewDestinationPanel(true)}>
Add destination
</Button>
{!!sourceId && (
<Button type="default" icon={<Plus />} onClick={() => setShowNewDestinationPanel(true)}>
Add destination
</Button>
)}
</div>
</div>

Expand All @@ -119,7 +125,21 @@ export const Destinations = () => {
/>
)}

{anyDestinations ? (
{replicationNotEnabled ? (
<div className="border rounded-md p-4 md:p-12 flex flex-col gap-y-4">
<div className="flex flex-col gap-y-1">
<h3>Run analysis on your data via integrations with Replication</h3>
<p className="text-sm text-foreground-light">
Enable replication on your project to send data to your first destination
</p>
</div>
<div className="flex gap-x-2">
<EnableReplicationModal />
{/* [Joshen] Placeholder for when we have documentation */}
<DocsButton href="https://supabase.com/docs" />
</div>
</div>
) : hasDestinations ? (
<Table
head={[
<Table.th key="name">Name</Table.th>,
Expand Down Expand Up @@ -147,7 +167,7 @@ export const Destinations = () => {
/>
)
})}
></Table>
/>
) : (
!isSourcesLoading &&
!isDestinationsLoading &&
Expand Down Expand Up @@ -180,7 +200,7 @@ export const Destinations = () => {
{!isSourcesLoading &&
!isDestinationsLoading &&
filteredDestinations.length === 0 &&
anyDestinations && (
hasDestinations && (
<div className="text-center py-8 text-foreground-light">
<p>No destinations match "{filterString}"</p>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { useState } from 'react'
import { toast } from 'sonner'

import { useParams } from 'common'
import { useCreateTenantSourceMutation } from 'data/replication/create-tenant-source-mutation'
import {
Button,
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogSection,
DialogSectionSeparator,
DialogTitle,
DialogTrigger,
} from 'ui'
import { Admonition } from 'ui-patterns'

export const EnableReplicationModal = () => {
const { ref: projectRef } = useParams()
const [open, setOpen] = useState(false)

const { mutateAsync: createTenantSource, isLoading: creatingTenantSource } =
useCreateTenantSourceMutation({
onSuccess: () => {
toast.success('Replication has been successfully enabled!')
setOpen(false)
},
onError: (error) => {
toast.error(`Failed to enable replication: ${error.message}`)
},
})

const onEnableReplication = async () => {
if (!projectRef) return console.error('Project ref is required')
await createTenantSource({ projectRef })
}

return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button type="primary" className="w-min">
Enable replication
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Confirm to enable Replication</DialogTitle>
</DialogHeader>
<DialogSectionSeparator />
<DialogSection className="flex flex-col gap-y-2 !p-0">
<Admonition
type="warning"
className="rounded-none border-0 mb-0"
title="Replication is currently in Alpha"
>
<p className="text-sm !leading-normal">
This feature is in active development and may change as we gather feedback.
Availability and behavior can evolve while in Alpha.
</p>
<p className="text-sm !leading-normal">
Pricing has not been finalized yet. You can enable replication now; we’ll announce
pricing later and notify you before any charges apply.
</p>
</Admonition>
</DialogSection>
<DialogFooter>
<Button type="default" disabled={creatingTenantSource}>
Cancel
</Button>
<Button type="primary" loading={creatingTenantSource} onClick={onEnableReplication}>
Enable replication
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}
Loading
Loading