import { zodResolver } from '@hookform/resolvers/zod' import { useParams } from 'common' import { Plus, X } from 'lucide-react' import { Fragment, useState } from 'react' import { SubmitHandler, useFieldArray, useForm } from 'react-hook-form' import { toast } from 'sonner' import { Button, DialogSectionSeparator, Form, FormControl, FormField, FormInputGroupInput, Input, InputGroup, InputGroupAddon, Select, SelectContent, SelectItem, SelectSeparator, SelectTrigger, SelectValue, Sheet, SheetContent, SheetFooter, SheetHeader, SheetSection, SheetTitle, } from 'ui' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' import { z } from 'zod' import { COLUMN_TYPE_FIELDS, COLUMN_TYPES } from './CreateTableSheet.constants' import { createFormSchema } from './CreateTableSheet.schema' import { useIcebergNamespaceCreateMutation } from '@/data/storage/iceberg-namespace-create-mutation' import { NamespaceTableFields, useIcebergNamespaceTableCreateMutation, } from '@/data/storage/iceberg-namespace-table-create-mutation' import { useIcebergNamespaceTablesQuery } from '@/data/storage/iceberg-namespace-tables-query' import { useIcebergNamespacesQuery } from '@/data/storage/iceberg-namespaces-query' const formId = 'create-namespace-table' const NEW_NAMESPACE_MARKER = 'new-namespace' interface CreateTableSheetProps { open: boolean onOpenChange: (value: boolean) => void } export const CreateTableSheet = ({ open, onOpenChange }: CreateTableSheetProps) => { const { ref: projectRef, bucketId } = useParams() const [isCreating, setIsCreating] = useState(false) const FormSchema = createFormSchema() const defaultValues = { namespace: '', newNamespace: undefined, name: '', columns: [{ name: '', type: 'string' as any }], } const form = useForm>({ resolver: zodResolver(FormSchema as any), defaultValues, mode: 'onChange', }) const { namespace } = form.watch() const { fields: columns, append: appendColumn, remove: removeColumn, } = useFieldArray({ control: form.control, name: 'columns' }) const { data: namespaces = [] } = useIcebergNamespacesQuery({ projectRef, warehouse: bucketId }) const { data: tables = [] } = useIcebergNamespaceTablesQuery( { projectRef, warehouse: bucketId, namespace, }, { enabled: namespace !== NEW_NAMESPACE_MARKER } ) const { mutateAsync: createNamespace } = useIcebergNamespaceCreateMutation() const { mutateAsync: createTable } = useIcebergNamespaceTableCreateMutation() const onSubmit: SubmitHandler> = async (values) => { if (!bucketId) return console.error('Bucket ID is missing') if (namespaces.includes(values.newNamespace ?? '')) { return form.setError('newNamespace', { message: 'Namespace name already exists' }) } if (tables.includes(values.name ?? '')) { return form.setError('name', { message: 'Table name already exists' }) } const isCreatingNewNamespace = values.namespace === NEW_NAMESPACE_MARKER && !!values.newNamespace try { setIsCreating(true) if (isCreatingNewNamespace) { await createNamespace({ projectRef, warehouse: bucketId, namespace: values.newNamespace as string, }) } const fields = values.columns.map((column, idx) => { return { id: idx + 1, name: column.name, type: column.type === 'decimal' ? `decimal(${column.precision}, ${column.scale})` : column.type === 'fixed' ? `fixed[${column.length}]` : column.type, required: false, } }) as NamespaceTableFields await createTable({ projectRef, warehouse: bucketId, namespace: isCreatingNewNamespace ? (values.newNamespace as string) : values.namespace, name: values.name, fields, }) toast.success(`Successfully created table in ${values.newNamespace ?? values.namespace}!`) onOpenChange(false) form.reset(defaultValues) } catch (error) { } finally { setIsCreating(false) } } return (
Create a new table
( )} /> {namespace === NEW_NAMESPACE_MARKER && ( ( )} /> )}
{!!namespace && (
( )} />

Columns

{columns.length === 0 ? (
Add a column to your table
) : ( <>

Name

Type

{columns.map((_, idx) => { const columnType = form.watch(`columns.${idx}.type`) const additionalFields = COLUMN_TYPE_FIELDS[columnType as keyof typeof COLUMN_TYPE_FIELDS] ?? [] return (
( )} /> ( )} />
{additionalFields.length > 0 && (
{additionalFields.map((x) => ( ( {x.name} field.onChange( isNaN(event.target.valueAsNumber) ? null : event.target.valueAsNumber ) } /> )} /> ))}
)}
) })} )}
)} ) }