mirror of
https://github.com/supabase/supabase.git
synced 2026-10-07 10:25:06 +03:00
trim redundant wizard foundations
This commit is contained in:
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>
|
||||
|
||||
+2
-2
@@ -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 = {
|
||||
|
||||
-167
@@ -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>
|
||||
)
|
||||
}
|
||||
+4
-5
@@ -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) => {
|
||||
|
||||
+167
@@ -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>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user