Files
supabase/studio/components/interfaces/Database/Wrappers/WrapperTableEditor.tsx
T
b91532f6c7 Migrate to styling tokens (#18314)
* migrate some www components to tokens

* consolidate InteractiveShimmerCard to Panel component

* update tokens in blog

* update tokens in careers page

* update tokens in customers section

* update tokens in open-source section

* update tokens in Realtime page

* update tokens in Storage and Vector

* update tokens in SplitCodeBlockCarousel

* update tokens in PGCharts

* remove unused css files

* update tokens in Card

* update tokens in Pricing page

* clean up priving page imports

* remove hardcoded theme vars

* migrate first half of defaultTheme.ts to tokens

* migrate second half of defaultTheme.ts to tokens

* improve inputs

* add foreground to text-light and text-lighter

* add foreground to text-light and text-lighter

* migrate docs components with styling tokens

* migrate docs components with styling tokens

* fix broken Repos component

* fix broken classes in blog

* update tokens on Button and other components

* update tokens on IconPanel

* update studio main layout base styling tokens

* update tokens across studio, docs and www

* update tokens across studio, docs and www

* update ui/Panel to styling tokens

* update ExampleProject and TableEditorMenu tokens

* www vector page tokens

* update studio UI tokens

* update other studio UI tokens

* update more studio UI tokens

* change tokens here, change tokens there

* finish updating colors with tokens variables

* add gui sandbox for theme experimentation

* use common package for www, docs and studio and fix Command K tokens

* provide light mode default tokens options

* fix conflict leftover

* update loading line

* fix className typo

* fix prettier

* update themeSandbox preset default values

* fix text-background0

* prettier

* update warningBanner with warning color

* switch all border-border with border-default

* improve border-secondary and foreground-muted in light mode

* force ring color on toggle

* fix button bg color and border-muted light token

* fix input bg color

* fix dark button hover

* fix homepage product card

* fix code-hike table header colors

* button dark border

* remove tabIndex leftover from homepage main ctas

---------

Co-authored-by: Kevin Grüneberg <k.grueneberg1994@gmail.com>
Co-authored-by: Jonathan Summers-Muir <MildTomato@users.noreply.github.com>
2023-11-07 03:40:53 +00:00

313 lines
9.3 KiB
TypeScript

import { useEffect, useState } from 'react'
import ActionBar from 'components/interfaces/TableGridEditor/SidePanelEditor/ActionBar'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
import { useSchemasQuery } from 'data/database/schemas-query'
import { Form, IconDatabase, IconPlus, Input, Listbox, Modal, SidePanel } from 'ui'
import WrapperDynamicColumns from './WrapperDynamicColumns'
import { Table, TableOption } from './Wrappers.types'
import { makeValidateRequired } from './Wrappers.utils'
export type WrapperTableEditorProps = {
visible: boolean
onCancel: () => void
onSave: (values: any) => void
tables: Table[]
initialData: any
}
type OnSubmitFn = (values: any, { resetForm }: { resetForm: () => void }) => void
const WrapperTableEditor = ({
visible,
onCancel,
onSave,
tables,
initialData,
}: WrapperTableEditorProps) => {
const [selectedTableIndex, setSelectedTableIndex] = useState<string>('')
useEffect(() => {
if (initialData) {
setSelectedTableIndex(String(initialData.index))
}
}, [initialData])
const selectedTable = selectedTableIndex === '' ? undefined : tables[parseInt(selectedTableIndex)]
const handleCancel = () => {
setSelectedTableIndex('')
onCancel()
}
const onSubmit: OnSubmitFn = (values, { resetForm }) => {
onSave({
...values,
index: parseInt(selectedTableIndex),
schema_name: values.schema === 'custom' ? values.schema_name : values.schema,
is_new_schema: values.schema === 'custom',
})
resetForm()
setSelectedTableIndex('')
}
return (
<SidePanel
key="WrapperTableEditor"
size="medium"
visible={visible}
onCancel={handleCancel}
header={<span>Edit foreign table</span>}
customFooter={
<ActionBar
backButtonLabel="Cancel"
applyButtonLabel="Save"
formId="wrapper-table-editor-form"
closePanel={handleCancel}
/>
}
>
<SidePanel.Content>
<div className="my-4 space-y-6">
<Listbox
size="small"
label="Select a target the table will point to"
value={selectedTableIndex}
onChange={(value) => setSelectedTableIndex(value)}
>
<Listbox.Option key="empty" value="" label="---">
---
</Listbox.Option>
{tables.map((table, i) => {
return (
<Listbox.Option
className="group"
key={String(i)}
value={String(i)}
label={table.label}
>
<div className="space-y-1">
<p>{table.label}</p>
<p className="opacity-50">{table.description}</p>
</div>
</Listbox.Option>
)
})}
</Listbox>
{selectedTable && (
<TableForm table={selectedTable} onSubmit={onSubmit} initialData={initialData} />
)}
</div>
</SidePanel.Content>
</SidePanel>
)
}
export default WrapperTableEditor
const Option = ({ option }: { option: TableOption }) => {
if (option.type === 'select') {
return (
<Listbox
key={option.name}
id={option.name}
name={option.name}
label={option.label}
defaultValue={option.defaultValue ?? ''}
>
{[
...(!option.required
? [
<Listbox.Option key="empty" value="" label="---">
---
</Listbox.Option>,
]
: []),
...option.options.map((subOption) => (
<Listbox.Option
key={subOption.value}
id={option.name + subOption.value}
value={subOption.value}
label={subOption.label}
>
{subOption.label}
</Listbox.Option>
)),
]}
</Listbox>
)
}
return (
<Input
key={option.name}
id={option.name}
name={option.name}
label={option.label}
placeholder={option.placeholder ?? ''}
defaultValue={option.defaultValue ?? ''}
/>
)
}
const TableForm = ({
table,
onSubmit,
initialData,
}: {
table: Table
onSubmit: OnSubmitFn
initialData: any
}) => {
const { project } = useProjectContext()
const {
data: schemas,
isLoading,
isSuccess,
} = useSchemasQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
})
const requiredOptions =
table.options.filter((option) => option.editable && option.required && !option.defaultValue) ??
[]
const optionalOptions =
table.options.filter(
(option) => option.editable && (!option.required || option.defaultValue)
) ?? []
const initialValues = initialData ?? {
table_name: '',
columns: table.availableColumns ?? [],
...Object.fromEntries(table.options.map((option) => [option.name, option.defaultValue ?? ''])),
schema: 'public',
schema_name: '',
}
const validate = makeValidateRequired([
...table.options,
{ name: 'table_name', required: true },
{ name: 'columns', required: true },
...(table.availableColumns ? [] : [{ name: 'columns.name', required: true }]),
])
return (
<Form
id="wrapper-table-editor-form"
initialValues={initialValues}
validate={validate}
onSubmit={onSubmit}
enableReinitialize={true}
>
{({ errors, values, setFieldValue }: any) => {
return (
<div className="space-y-4">
{isLoading && <ShimmeringLoader className="py-4" />}
{isSuccess && (
<Listbox size="small" name="schema" label="Select a schema for the foreign table">
<Listbox.Option
key="custom"
id="custom"
label={`Create a new schema`}
value="custom"
addOnBefore={() => <IconPlus size={16} strokeWidth={1.5} />}
>
Create a new schema
</Listbox.Option>
<Modal.Separator />
{(schemas ?? [])?.map((schema) => {
return (
<Listbox.Option
key={schema.id}
id={schema.name}
label={schema.name}
value={schema.name}
addOnBefore={() => <IconDatabase size={16} strokeWidth={1.5} />}
>
{schema.name}
</Listbox.Option>
)
})}
</Listbox>
)}
{values.schema === 'custom' && (
<Input id="schema_name" name="schema_name" label="Schema name" />
)}
<Input
id="table_name"
name="table_name"
label="Table name"
descriptionText="You can query from this table after the wrapper is enabled."
/>
{requiredOptions.map((option) => (
<Option key={option.name} option={option} />
))}
<div className="form-group">
<label className="!w-full">
{table.availableColumns
? 'Select the columns to be added to your table'
: 'Add columns to your table'}
</label>
<div className="flex flex-wrap gap-2 w-full">
{table.availableColumns ? (
table.availableColumns.map((column) => {
const isSelected = Boolean(
values.columns.find((col: any) => col.name === column.name)
)
return (
<div
key={column.name}
className={[
'px-2 py-1 rounded cursor-pointer transition',
`${isSelected ? 'bg-brand-300' : 'bg-surface-300 hover:bg-selection'}`,
].join(' ')}
onClick={() => {
if (isSelected) {
setFieldValue(
'columns',
values.columns.filter((col: any) => col.name !== column.name)
)
} else {
setFieldValue('columns', values.columns.concat([column]))
}
}}
>
<p className="text-sm">{column.name}</p>
</div>
)
})
) : (
<WrapperDynamicColumns
initialColumns={values.columns}
onChange={(columns) => {
setFieldValue('columns', columns)
}}
errors={errors}
/>
)}
</div>
{errors.columns && (
<span className="text-red-900 text-sm mt-2">{errors.columns}</span>
)}
</div>
{optionalOptions.map((option) => (
<Option key={option.name} option={option} />
))}
</div>
)
}}
</Form>
)
}