trim redundant wizard foundations

This commit is contained in:
Danny White committed 2026-08-31 16:53:47 +10:00
1 parent fcb6007588
commit 442ace2c42
7 files changed
+176 -177

No files matched your search

@@ -127,7 +127,7 @@ Supabase UI copy is:
### Use title case for page titles and global navigation
Use title case for page names in the main nav and document titles (e.g. "Database Settings", "Project Settings"). This includes browser tab titles set via the `browserTitle` prop on layout components (e.g. "New Pipeline", not "New pipeline").
Use title case for page names in the main nav and document titles (e.g. "Database Settings", "Project Settings"). This distinguishes the page as a destination from section labels and in-page headings, which use sentence case.
### Use declarative page descriptions
@@ -252,7 +252,7 @@ export default function FormPatternsPageLayout() {
<InputGroup>
<FormInputGroupInput {...field} type="number" min={5} max={30} />
<InputGroupAddon align="inline-end">
<InputGroupText>milliseconds</InputGroupText>
<InputGroupText className="font-mono">ms</InputGroupText>
</InputGroupAddon>
</InputGroup>
</FormControl>
@@ -265,7 +265,7 @@ export default function FormPatternsSidePanel() {
<InputGroup>
<FormInputGroupInput {...field} type="number" min={5} max={30} />
<InputGroupAddon align="inline-end">
<InputGroupText>milliseconds</InputGroupText>
<InputGroupText>ms</InputGroupText>
</InputGroupAddon>
</InputGroup>
</FormControl>
@@ -40,12 +40,12 @@ export const getDestinationTypeCreateDescription = (type: DestinationType) => {
export const BIGQUERY_PROJECT_ID_FIELD_COPY = {
label: 'Project ID',
description: 'Google Cloud project ID where data will be sent.',
description: 'The Google Cloud project ID where data will be sent',
} as const
export const BIGQUERY_DATASET_ID_FIELD_COPY = {
label: 'Dataset ID',
description: 'BigQuery dataset where replicated tables will be created.',
description: 'The BigQuery dataset where replicated tables will be created',
} as const
export const ANALYTICS_BUCKET_BUCKET_FIELD_COPY = {
@@ -1,167 +0,0 @@
import { useParams } from 'common'
import { Check, Loader2, Plus } from 'lucide-react'
import { useId, useState } from 'react'
import { ControllerRenderProps } from 'react-hook-form'
import {
cn,
ComboboxTrigger,
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
Popover,
PopoverContent,
PopoverTrigger,
ScrollArea,
} from 'ui'
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
import type { DestinationPanelSchemaType } from './DestinationForm.schema'
import { useReplicationPublicationsQuery } from '@/data/replication/publications-query'
interface PublicationSelectProps {
sourceId?: number
field: ControllerRenderProps<DestinationPanelSchemaType, 'publicationName'>
onNewPublicationClick: () => void
}
export const PublicationSelect = ({
sourceId,
field,
onNewPublicationClick,
}: PublicationSelectProps) => {
const { ref: projectRef } = useParams()
const listboxId = useId()
const [open, setOpen] = useState(false)
const selectedPublication = field.value ?? ''
const {
data: publications = [],
isPending,
isFetching,
refetch: refetchPublications,
} = useReplicationPublicationsQuery({ projectRef, sourceId })
const isLoadingPublications = isPending || isFetching
const showLoadingState = isLoadingPublications && publications.length === 0
const handlePublicationSelect = (publicationName: string) => {
setOpen(false)
field.onChange(publicationName)
}
return (
<Popover
modal={false}
open={open}
onOpenChange={(nextOpen) => {
setOpen(nextOpen)
if (nextOpen) {
if (typeof projectRef !== 'undefined' && typeof sourceId !== 'undefined') {
refetchPublications()
}
}
if (!nextOpen) {
field.onBlur()
}
}}
>
<PopoverTrigger asChild>
<ComboboxTrigger
aria-expanded={open}
aria-controls={listboxId}
data-state={open ? 'open' : 'closed'}
disabled={showLoadingState}
name={field.name}
onBlur={field.onBlur}
className={cn(!selectedPublication && !showLoadingState && 'text-foreground-lighter')}
icon={
showLoadingState ? (
<Loader2 className="h-4 w-4 shrink-0 animate-spin text-foreground-lighter" />
) : undefined
}
>
{showLoadingState
? 'Loading publications...'
: selectedPublication || 'Select publication'}
</ComboboxTrigger>
</PopoverTrigger>
<PopoverContent id={listboxId} sameWidthAsTrigger className="p-0" align="start">
<Command>
<CommandInput placeholder="Find publication..." className="text-xs" />
<CommandList>
<CommandEmpty>No publications found</CommandEmpty>
{showLoadingState ? (
<div className="p-2">
<GenericSkeletonLoader className="w-full" />
</div>
) : (
<>
{publications.length === 0 ? (
<div className="text-foreground-lighter text-xs py-3 px-2 space-y-0.5">
<p>No publications available</p>
<p className="text-foreground-muted">Publications with no tables are hidden</p>
</div>
) : (
<CommandGroup>
<ScrollArea
className={publications.length > 7 ? 'h-[210px]' : ''}
onWheel={(event) => event.stopPropagation()}
>
{publications.map((pub) => (
<CommandItem
key={pub.name}
value={pub.name}
className="cursor-pointer [&>span]:top-2.5"
onSelect={() => {
handlePublicationSelect(pub.name)
}}
>
<div className="flex w-full items-start justify-between gap-2">
<div className="min-w-0">
<p>{pub.name}</p>
<p className="text-foreground-lighter">
{pub.tables.length} {pub.tables.length === 1 ? 'table' : 'tables'}
</p>
</div>
<Check
className={cn(
'mt-0.5 h-4 w-4 shrink-0 text-brand',
selectedPublication === pub.name ? 'opacity-100' : 'opacity-0'
)}
strokeWidth={2}
/>
</div>
</CommandItem>
))}
</ScrollArea>
</CommandGroup>
)}
<CommandSeparator />
<CommandGroup>
<CommandItem
value="new-publication"
className="cursor-pointer"
onSelect={() => {
setOpen(false)
onNewPublicationClick()
}}
>
<Plus size={14} strokeWidth={1.5} className="mr-2" />
New publication
</CommandItem>
</CommandGroup>
</>
)}
</CommandList>
</Command>
</PopoverContent>
</Popover>
)
}
@@ -6,8 +6,7 @@ import { Admonition } from 'ui-patterns/Admonition'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import type { DestinationPanelSchemaType } from './DestinationForm.schema'
import { PUBLICATION_FIELD_COPY } from './DestinationFormFieldCopy'
import { PublicationSelect } from './PublicationSelect'
import { PublicationsComboBox } from './PublicationsComboBox'
import { useReplicationPublicationsQuery } from '@/data/replication/publications-query'
import { useReplicationSourceId } from '@/data/replication/sources-query'
@@ -41,11 +40,11 @@ export const PublicationSelection = ({
render={({ field }) => (
<FormItemLayout
layout="horizontal"
label={PUBLICATION_FIELD_COPY.label}
description={PUBLICATION_FIELD_COPY.description}
label="Publication"
description="Tables in the selected publication will be replicated to this destination."
>
<FormControl>
<PublicationSelect
<PublicationsComboBox
field={{
...field,
onChange: (value) => {
@@ -0,0 +1,167 @@
import { useParams } from 'common'
import { Check, ChevronsUpDown, Loader2, Plus } from 'lucide-react'
import { useEffect, useState } from 'react'
import { ControllerRenderProps } from 'react-hook-form'
import {
Badge,
Button,
cn,
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
Popover,
PopoverContent,
PopoverTrigger,
ScrollArea,
} from 'ui'
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
import type { DestinationPanelSchemaType } from './DestinationForm.schema'
import { useReplicationPublicationsQuery } from '@/data/replication/publications-query'
interface PublicationsComboBoxProps {
sourceId?: number
field: ControllerRenderProps<DestinationPanelSchemaType, 'publicationName'>
onNewPublicationClick: () => void
}
export const PublicationsComboBox = ({
sourceId,
field,
onNewPublicationClick,
}: PublicationsComboBoxProps) => {
const { ref: projectRef } = useParams()
const [searchTerm, setSearchTerm] = useState('')
const [dropdownOpen, setDropdownOpen] = useState(false)
const [selectedPublication, setSelectedPublication] = useState<string>(field?.value || '')
const {
data: publications = [],
isPending,
isFetching,
refetch: refetchPublications,
} = useReplicationPublicationsQuery({ projectRef, sourceId })
const isLoadingPublications = isPending || isFetching
const showLoadingState = isLoadingPublications && publications.length === 0
function handlePublicationSelect(pub: string) {
setSelectedPublication(pub)
setDropdownOpen(false)
field.onChange(pub)
}
useEffect(() => {
setSelectedPublication(field?.value || '')
}, [field?.value])
return (
<Popover
modal={false}
open={dropdownOpen}
onOpenChange={(open) => {
setDropdownOpen(open)
if (open) {
if (typeof projectRef !== 'undefined' && typeof sourceId !== 'undefined') {
refetchPublications()
}
}
if (!open && field?.onBlur) {
field.onBlur()
}
}}
>
<PopoverTrigger asChild>
<Button
variant="default"
size="medium"
className={cn(
'w-full [&>span]:w-full text-left',
!selectedPublication && 'text-foreground-muted'
)}
iconRight={showLoadingState ? <Loader2 className="animate-spin" /> : <ChevronsUpDown />}
name={field.name}
onBlur={field.onBlur}
>
{selectedPublication || 'Select publication'}
</Button>
</PopoverTrigger>
<PopoverContent sameWidthAsTrigger className="p-0" align="start">
<Command>
<CommandInput
placeholder="Find publication..."
className="text-xs"
value={searchTerm}
onValueChange={setSearchTerm}
/>
<CommandList>
<CommandEmpty>No publications found</CommandEmpty>
{showLoadingState && (
<div className="flex items-center gap-2 p-2 pb-0 text-center justify-center">
<GenericSkeletonLoader className="w-full" />
</div>
)}
<CommandGroup>
{publications.length === 0 && !showLoadingState && (
<div className="text-foreground-lighter text-xs py-3 px-2 space-y-0.5">
<p>No publications available</p>
<p className="text-foreground-muted">Publications with no tables are hidden</p>
</div>
)}
<ScrollArea
className={publications.length > 7 ? 'h-[210px]' : ''}
onWheel={(e) => e.stopPropagation()}
>
{publications.map((pub) => (
<CommandItem
key={pub.name}
className="cursor-pointer flex items-center justify-between space-x-2 w-full"
onSelect={() => {
handlePublicationSelect(pub.name)
}}
onClick={() => {
handlePublicationSelect(pub.name)
}}
>
<span>{pub.name}</span>
<div className="flex items-center gap-2">
<Badge
variant="default"
className="rounded-full px-2 py-0.5 text-[10px] font-normal border border-border bg-surface-100"
>
{pub.tables.length} {pub.tables.length === 1 ? 'table' : 'tables'}
</Badge>
{selectedPublication === pub.name && (
<Check className="text-brand" strokeWidth={2} size={13} />
)}
</div>
</CommandItem>
))}
</ScrollArea>
</CommandGroup>
<CommandSeparator />
<CommandGroup>
<CommandItem
className="cursor-pointer w-full"
onSelect={onNewPublicationClick}
onClick={onNewPublicationClick}
>
<Plus size={14} strokeWidth={1.5} className="mr-2" />
<p>New publication</p>
</CommandItem>
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
)
}