Files
supabase/studio/components/interfaces/Database/Indexes/CreateIndexSidePanel.tsx
T
8057309e51 chore: upgrade next 13 + react 18 (#17839)
* update deps + image codemod (studio)

* update next links (studio)

* update deps

* update links (ui)

* remove next-transpile-modules

* move next-themes dependency

* chore: update ConfirmDialog

* chore: remove old ConfirmModal js file. migrated to TS

* dependency wrangling

* remove empty page

* update next links (www)

* First run bump react-data-grid-v7 beta 4

* fix package-lock.json

* more deps wrangling

* update recharts

* update sentry options

* fix some broken things in www

* studio fixes

* fix graphiql

* fix studio build

* fix menu hydration

* small build error

* update turbo

* fix www typescript errors

* docs image codemod

* links codemod docs

* fix docs typescript errors

* move useConsent to ui to prevent circular deps

* Fix links

* Fix homepage

* Fix links

* move studio/ to apps/

* Revert "move studio/ to apps/"

This reverts commit 1b0a985fcb.

* disable outputFileTracingRoot

* remove outputFileTracingRoot

* fix homepage product cards

* fix PrivacySettings links

* Fix links

* Fix the build for www.

* Minor fixes for JWTGenerator.

* Fix the docs and ui tests.

* Revert codehike back to 0.8.3

* remove ConfirmAlert()

* reenable babel because mobx hates me

* fix blog image and comparison page avatar

* Fix svg errors

* update image synthax

* Fix code hike

* Move the button in a div so that it doesn't inherit its parent height and make the button look weird.

* When components are defined in a component, they get recreated on each render. This makes them unstable in certain cases and causes infinite rerenders.

* Replace the next/head usage with next/script.

* Chore/upgrade next 13 fix table editor (#18431)

* fix table editor styling and fix row deletion logic

* Fix deleting selected rows from header, and fix checkboxes not clearing up

* Fix deleting all rows when filter applied, and fix deleting all rows

* Fix grid size styling issue

* Fix TS error

* Hydration errors

* studio org pages fixes

* fix more studio links

* audit logs fixes

* dropdown icon styling fixes

* fix some images in www

* upgrade to next 14

* try new sentry wrapper for api

* see if this is even invoked

* Revert "see if this is even invoked"

This reverts commit 86c3973ffa.

* Revert "try new sentry wrapper for api"

This reverts commit f67623ebad.

* Revert "upgrade to next 14"

This reverts commit a24dd6131e.

* chore: allow node version 19/20

* Try to fix the LogTable so that it renders with the newer "react-data-grid" version.

* Fix type errors in the log renderer code.

* Fix the replication screen.

* Add the CSS for the GraphiQL.

* Fix SQL editor results rendering

* Lint

* Fix SQL editor results height issue

* Fix auth RLS not invalidating RQ when toggling RLS

* Fix database tables new/edit column regressed

* Fix migrations page empty state if migrations schema not yet created

* Fix API side panel docs temp remove postgrest text for column description PK and FK

* Fix + improve timeout handling in SQL editor

---------

Co-authored-by: Jonathan Summers-Muir <MildTomato@users.noreply.github.com>
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
Co-authored-by: Francesco Sansalvadore <f.sansalvadore@gmail.com>
Co-authored-by: Terry Sutton <saltcod@gmail.com>
Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
Co-authored-by: Kevin Grüneberg <k.grueneberg1994@gmail.com>
2023-10-31 05:51:46 +00:00

247 lines
8.0 KiB
TypeScript

import Link from 'next/link'
import { useEffect, useMemo, useState } from 'react'
import { Button, Input, Listbox, SidePanel } from 'ui'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import CodeEditor from 'components/ui/CodeEditor'
import MultiSelect, { MultiSelectOption } from 'components/ui/MultiSelect'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
import { useIndexesQuery } from 'data/database/indexes-query'
import { useSchemasQuery } from 'data/database/schemas-query'
import { useTableColumnsQuery } from 'data/database/table-columns-query'
import { useEntityTypesQuery } from 'data/entity-types/entity-types-infinite-query'
import { useExecuteSqlMutation } from 'data/sql/execute-sql-mutation'
import { useStore } from 'hooks'
import { INDEX_TYPES } from './Indexes.constants'
interface CreateIndexSidePanelProps {
visible: boolean
onClose: () => void
}
const CreateIndexSidePanel = ({ visible, onClose }: CreateIndexSidePanelProps) => {
const { ui } = useStore()
const { project } = useProjectContext()
const [selectedSchema, setSelectedSchema] = useState('public')
const [selectedEntity, setSelectedEntity] = useState('---')
const [selectedColumns, setSelectedColumns] = useState<string[]>([])
const [selectedIndexType, setSelectedIndexType] = useState<string>(INDEX_TYPES[0].value)
const { refetch: refetchIndexes } = useIndexesQuery({
schema: selectedSchema,
projectRef: project?.ref,
connectionString: project?.connectionString,
})
const { data: schemas } = useSchemasQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
})
const { data: entities } = useEntityTypesQuery(
{
schema: selectedSchema,
sort: 'alphabetical',
search: undefined,
projectRef: project?.ref,
connectionString: project?.connectionString,
},
{ keepPreviousData: true }
)
const {
data: tableColumns,
isLoading: isLoadingTableColumns,
isSuccess: isSuccessTableColumns,
} = useTableColumnsQuery({
schema: selectedSchema,
table: selectedEntity,
projectRef: project?.ref,
connectionString: project?.connectionString,
})
const { mutate: execute, isLoading: isExecuting } = useExecuteSqlMutation({
onSuccess: async () => {
await refetchIndexes()
onClose()
ui.setNotification({ category: 'success', message: `Successfully created index` })
},
onError: (error) => {
ui.setNotification({
error,
category: 'error',
message: `Failed to create index: ${error.message}`,
})
},
})
const entityTypes = useMemo(
() => entities?.pages.flatMap((page) => page.data.entities) || [],
[entities?.pages]
)
const columns = tableColumns?.result[0]?.columns ?? []
const columnOptions: MultiSelectOption[] = columns.map((column) => {
return { id: column.attname, value: column.attname, name: column.attname, disabled: false }
})
const generatedSQL = `
CREATE INDEX ON "${selectedSchema}"."${selectedEntity}" USING ${selectedIndexType} (${selectedColumns.join(
', '
)});
`.trim()
const onSaveIndex = () => {
if (!project) return console.error('Project is required')
execute({
projectRef: project.ref,
connectionString: project.connectionString,
sql: generatedSQL,
})
}
useEffect(() => {
if (visible) {
setSelectedSchema('public')
setSelectedEntity('---')
setSelectedColumns([])
setSelectedIndexType(INDEX_TYPES[0].value)
}
}, [visible])
useEffect(() => {
setSelectedEntity('---')
setSelectedColumns([])
setSelectedIndexType(INDEX_TYPES[0].value)
}, [selectedSchema])
useEffect(() => {
setSelectedColumns([])
setSelectedIndexType(INDEX_TYPES[0].value)
}, [selectedEntity])
return (
<SidePanel
size="large"
header="Create new index"
visible={visible}
onCancel={onClose}
onConfirm={() => onSaveIndex()}
loading={isExecuting}
confirmText="Create index"
>
<div className="py-6 space-y-6">
<SidePanel.Content className="space-y-6">
<Listbox
size="small"
label="Select a schema"
value={selectedSchema}
onChange={setSelectedSchema}
>
{(schemas ?? []).map((schema) => (
<Listbox.Option key={schema.name} value={schema.name} label={schema.name}>
{schema.name}
</Listbox.Option>
))}
</Listbox>
{entityTypes.length === 0 ? (
<div className="space-y-2">
<p className="text-sm text-foreground-light leading-4">Select a table</p>
<Input
disabled
placeholder="No tables available in schema"
descriptionText="Create a table in this schema via the Table or SQL editor first"
/>
</div>
) : (
<Listbox
size="small"
label="Select a table"
value={selectedEntity}
onChange={setSelectedEntity}
>
<Listbox.Option key="default-option-table" value="---" label="---">
---
</Listbox.Option>
{(entityTypes ?? []).map((entity) => (
<Listbox.Option key={entity.name} value={entity.name} label={entity.name}>
{entity.name}
</Listbox.Option>
))}
</Listbox>
)}
{selectedEntity !== '---' && (
<div>
<p className="text-sm text-foreground-light mb-2">Select up to 32 columns</p>
{isLoadingTableColumns && <ShimmeringLoader className="py-4" />}
{isSuccessTableColumns && (
<MultiSelect
options={columnOptions}
placeholder=""
searchPlaceholder="Search for a column"
value={selectedColumns}
onChange={setSelectedColumns}
/>
)}
</div>
)}
</SidePanel.Content>
{selectedColumns.length > 0 && (
<>
<SidePanel.Separator />
<SidePanel.Content className="space-y-6">
<Listbox
size="small"
label="Select an index type"
value={selectedIndexType}
onChange={setSelectedIndexType}
>
{INDEX_TYPES.map((index) => (
<Listbox.Option key={index.name} value={index.value} label={index.name}>
<p>{index.name}</p>
{index.description.split('\n').map((x, idx) => (
<p key={`${index.value}-description-${idx}`}>{x}</p>
))}
</Listbox.Option>
))}
</Listbox>
</SidePanel.Content>
<SidePanel.Separator />
<SidePanel.Content>
<div className="flex items-center justify-between">
<p className="text-sm">Preview of SQL statement</p>
<Button asChild type="default">
<Link
href={
project !== undefined
? `/project/${project.ref}/sql/new?content=${generatedSQL}`
: '/'
}
>
Open in SQL Editor
</Link>
</Button>
</div>
</SidePanel.Content>
<div className="h-[200px] !mt-2">
<div className="relative h-full">
<CodeEditor
isReadOnly
autofocus={false}
id={`${selectedSchema}-${selectedEntity}-${selectedColumns.join(
','
)}-${selectedIndexType}`}
language="pgsql"
defaultValue={generatedSQL}
/>
</div>
</div>
</>
)}
</div>
</SidePanel>
)
}
export default CreateIndexSidePanel