| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- import { Check, ChevronsUpDown } from 'lucide-react'
- import { useMemo, useState } from 'react'
- import {
- Button,
- cn,
- Command,
- CommandEmpty,
- CommandGroup,
- CommandInput,
- CommandItem,
- CommandList,
- Popover,
- PopoverContent,
- PopoverTrigger,
- ScrollArea,
- } from 'ui'
- import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'
- import { useSchemasQuery } from '@/data/database/schemas-query'
- import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
- import { INTERNAL_SCHEMAS } from '@/hooks/useProtectedSchemas'
- import { pluralize } from '@/lib/helpers'
- interface ExposedSchemaSelectorProps {
- disabled?: boolean
- selectedSchemas: string[]
- onToggleSchema: (schema: string) => void
- }
- export const ExposedSchemaSelector = ({
- disabled = false,
- selectedSchemas,
- onToggleSchema,
- }: ExposedSchemaSelectorProps) => {
- const [open, setOpen] = useState(false)
- const { data: project } = useSelectedProjectQuery()
- const {
- data: allSchemas,
- isPending,
- isError,
- isSuccess,
- } = useSchemasQuery({
- projectRef: project?.ref,
- connectionString: project?.connectionString,
- })
- const schemas = useMemo(
- () =>
- (allSchemas ?? [])
- .filter((s) => {
- if (s.name === 'graphql_public') return true
- return !INTERNAL_SCHEMAS.includes(s.name)
- })
- .sort((a, b) => a.name.localeCompare(b.name)),
- [allSchemas]
- )
- const missingExposedSchema = useMemo(
- () => selectedSchemas.filter((schema) => !schemas.some((s) => s.name === schema)),
- [schemas, selectedSchemas]
- )
- const selectedSet = useMemo(() => new Set(selectedSchemas), [selectedSchemas])
- const selectedCount = schemas.filter((s) => selectedSet.has(s.name)).length
- return (
- <Popover open={open} onOpenChange={setOpen} modal={false}>
- <PopoverTrigger asChild>
- <Button
- size="small"
- disabled={disabled}
- type="default"
- className="w-full [&>span]:w-full pr-1! space-x-1"
- iconRight={<ChevronsUpDown className="text-foreground-muted" strokeWidth={2} size={14} />}
- >
- <div className="w-full flex gap-1">
- <p className="text-foreground-lighter">
- {isSuccess
- ? `${selectedCount} of ${schemas.length} ${pluralize(schemas.length, 'schema')} exposed`
- : 'Loading schemas...'}
- </p>
- </div>
- </Button>
- </PopoverTrigger>
- <PopoverContent
- className="p-0 min-w-[200px] pointer-events-auto"
- side="bottom"
- align="start"
- sameWidthAsTrigger
- >
- <Command>
- <CommandInput className="text-xs" placeholder="Find schema..." />
- <CommandList>
- <CommandGroup>
- {isPending ? (
- <>
- <div className="px-2 py-1">
- <ShimmeringLoader className="py-2" />
- </div>
- <div className="px-2 py-1 w-4/5">
- <ShimmeringLoader className="py-2" />
- </div>
- </>
- ) : isError ? (
- <div className="flex items-center py-3 justify-center">
- <p className="text-xs text-foreground-lighter">Failed to retrieve schemas</p>
- </div>
- ) : (
- <>
- <CommandEmpty>
- <p className="text-xs text-center text-foreground-lighter py-3">
- No schemas found
- </p>
- </CommandEmpty>
- <ScrollArea className={schemas.length > 7 ? 'h-[210px]' : ''}>
- {missingExposedSchema.map((schema) => (
- <CommandItem
- key={schema}
- value={schema}
- className="cursor-pointer w-full"
- onSelect={() => {
- onToggleSchema(schema)
- }}
- >
- <div className="w-full flex flex-col">
- <div className="w-full flex items-center gap-x-2">
- <Check size={16} className="text-brand shrink-0" />
- <span className="truncate">{schema}</span>
- </div>
- <span className="pl-6 text-destructive text-[11px]">
- This schema does not exist
- </span>
- </div>
- </CommandItem>
- ))}
- {schemas.map((schema) => {
- const isExposed = selectedSet.has(schema.name)
- return (
- <CommandItem
- key={schema.id}
- value={schema.name}
- className="cursor-pointer w-full"
- onSelect={() => {
- onToggleSchema(schema.name)
- }}
- >
- <div
- className={cn('w-full flex items-center gap-x-2', !isExposed && 'ml-6')}
- >
- {isExposed && <Check size={16} className="text-brand shrink-0" />}
- <span className="truncate">{schema.name}</span>
- </div>
- </CommandItem>
- )
- })}
- </ScrollArea>
- </>
- )}
- </CommandGroup>
- </CommandList>
- </Command>
- </PopoverContent>
- </Popover>
- )
- }
|