1
0
Fork 0
mirror of synced 2024-06-01 18:20:18 +12:00
budibase/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte

332 lines
9.3 KiB
Svelte
Raw Normal View History

2020-08-08 03:13:57 +12:00
<script>
2021-02-10 07:49:12 +13:00
import {
Input,
Button,
Label,
Select,
Toggle,
RadioGroup,
DatePicker,
2021-04-17 04:12:22 +12:00
ModalContent,
Context,
2021-02-10 07:49:12 +13:00
} from "@budibase/bbui"
2021-04-30 06:10:02 +12:00
import { cloneDeep } from "lodash/fp"
import { tables } from "stores/backend"
import { TableNames, UNEDITABLE_USER_FIELDS } from "constants"
2021-03-02 07:03:33 +13:00
import {
FIELDS,
AUTO_COLUMN_SUB_TYPES,
RelationshipTypes,
} from "constants/backend"
2021-04-30 06:10:02 +12:00
import { getAutoColumnInformation, buildAutoColumn } from "builderStore/utils"
import { notifications } from "@budibase/bbui"
2020-08-08 03:13:57 +12:00
import ValuesList from "components/common/ValuesList.svelte"
2020-10-24 05:38:10 +13:00
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
2021-04-30 06:10:02 +12:00
import { truncate } from "lodash"
import ModalBindableInput from "components/common/bindings/ModalBindableInput.svelte"
2021-04-30 06:10:02 +12:00
import { getBindings } from "components/backend/DataTable/formula"
2021-04-17 04:12:22 +12:00
import { getContext } from "svelte"
2020-08-08 03:13:57 +12:00
const AUTO_TYPE = "auto"
const FORMULA_TYPE = FIELDS.FORMULA.type
const LINK_TYPE = FIELDS.LINK.type
let fieldDefinitions = cloneDeep(FIELDS)
2021-04-17 04:12:22 +12:00
const { hide } = getContext(Context.Modal)
export let field = {
type: "string",
constraints: fieldDefinitions.STRING.constraints,
// Initial value for column name in other table for linked records
2021-03-23 23:54:03 +13:00
fieldName: $tables.selected.name,
}
2020-08-08 03:13:57 +12:00
2020-08-11 02:34:37 +12:00
let originalName = field.name
let primaryDisplay =
2021-03-23 23:54:03 +13:00
$tables.selected.primaryDisplay == null ||
$tables.selected.primaryDisplay === field.name
2021-03-23 23:54:03 +13:00
let table = $tables.selected
let indexes = [...($tables.selected.indexes || [])]
2020-10-24 05:38:10 +13:00
let confirmDeleteDialog
2020-10-28 02:04:32 +13:00
let deletion
2020-10-24 05:38:10 +13:00
2021-03-23 23:54:03 +13:00
$: tableOptions = $tables.list.filter(
table => table._id !== $tables.draft._id
)
$: required = !!field?.constraints?.presence || primaryDisplay
$: uneditable =
2021-04-17 04:25:53 +12:00
($tables.selected?._id === TableNames.USERS &&
UNEDITABLE_USER_FIELDS.includes(field.name)) ||
(originalName && field.type === LINK_TYPE)
2021-03-16 09:38:55 +13:00
$: invalid =
!field.name ||
2021-03-16 09:38:55 +13:00
(field.type === LINK_TYPE && !field.tableId) ||
2021-04-17 04:12:22 +12:00
Object.keys($tables.draft?.schema ?? {}).some(
key => key !== originalName && key === field.name
2021-03-16 09:38:55 +13:00
)
2020-08-08 04:41:20 +12:00
// used to select what different options can be displayed for column type
2021-02-16 08:59:49 +13:00
$: canBeSearched =
field.type !== LINK_TYPE &&
2021-02-16 08:59:49 +13:00
field.subtype !== AUTO_COLUMN_SUB_TYPES.CREATED_BY &&
field.subtype !== AUTO_COLUMN_SUB_TYPES.UPDATED_BY &&
field.type !== FORMULA_TYPE
2021-04-30 06:10:02 +12:00
$: canBeDisplay =
field.type !== LINK_TYPE &&
field.type !== AUTO_TYPE &&
field.type !== FORMULA_TYPE
2021-02-16 08:59:49 +13:00
$: canBeRequired =
field.type !== LINK_TYPE && !uneditable && field.type !== AUTO_TYPE
$: relationshipOptions = getRelationshipOptions(field)
2020-08-08 03:13:57 +12:00
async function saveColumn() {
if (field.type === AUTO_TYPE) {
2021-04-01 22:29:47 +13:00
field = buildAutoColumn($tables.draft.name, field.name, field.subtype)
}
2021-03-23 23:54:03 +13:00
tables.saveField({
2021-04-01 22:29:47 +13:00
originalName,
field,
primaryDisplay,
indexes,
})
2020-08-08 03:13:57 +12:00
}
2020-10-24 05:38:10 +13:00
function deleteColumn() {
2021-03-23 23:54:03 +13:00
if (field.name === $tables.selected.primaryDisplay) {
notifications.error("You cannot delete the display column")
2020-10-24 05:38:10 +13:00
} else {
2021-03-23 23:54:03 +13:00
tables.deleteField(field)
notifications.success(`Column ${field.name} deleted.`)
2021-04-17 04:12:22 +12:00
hide()
2020-10-24 05:38:10 +13:00
}
}
function handleTypeChange(event) {
// remove any extra fields that may not be related to this type
delete field.autocolumn
delete field.subtype
delete field.tableId
delete field.relationshipType
// Add in defaults and initial definition
const definition = fieldDefinitions[event.detail?.toUpperCase()]
if (definition?.constraints) {
field.constraints = definition.constraints
}
// Default relationships many to many
if (field.type === LINK_TYPE) {
field.relationshipType = RelationshipTypes.MANY_TO_MANY
}
}
function onChangeRequired(e) {
2021-04-17 04:12:22 +12:00
const req = e.detail
2021-04-30 06:10:02 +12:00
field.constraints.presence = req ? { allowEmpty: false } : false
required = req
}
function onChangePrimaryDisplay(e) {
2021-04-17 04:12:22 +12:00
const isPrimary = e.detail
// primary display is always required
if (isPrimary) {
2021-04-30 06:10:02 +12:00
field.constraints.presence = { allowEmpty: false }
}
}
2020-10-24 05:38:10 +13:00
2021-02-10 07:49:12 +13:00
function onChangePrimaryIndex(e) {
2021-04-17 04:12:22 +12:00
indexes = e.detail ? [field.name] : []
2021-02-10 07:49:12 +13:00
}
2021-02-02 10:02:54 +13:00
2021-02-10 07:49:12 +13:00
function onChangeSecondaryIndex(e) {
2021-04-17 04:12:22 +12:00
if (e.detail) {
2021-02-10 07:49:12 +13:00
indexes[1] = field.name
} else {
2021-02-10 07:57:32 +13:00
indexes = indexes.slice(0, 1)
2021-02-10 07:49:12 +13:00
}
2021-02-02 10:02:54 +13:00
}
2020-10-24 05:38:10 +13:00
function confirmDelete() {
confirmDeleteDialog.show()
2020-10-28 02:04:32 +13:00
deletion = true
2020-10-24 11:55:51 +13:00
}
function hideDeleteDialog() {
confirmDeleteDialog.hide()
2020-10-28 02:04:32 +13:00
deletion = false
2020-10-24 05:38:10 +13:00
}
function getRelationshipOptions(field) {
if (!field || !field.tableId) {
return null
}
const linkTable = tableOptions.find(table => table._id === field.tableId)
if (!linkTable) {
return null
}
2021-04-30 06:10:02 +12:00
const thisName = truncate(table.name, { length: 14 }),
linkName = truncate(linkTable.name, { length: 14 })
return [
2021-03-02 07:03:33 +13:00
{
name: `Many ${thisName} rows → many ${linkName} rows`,
alt: `Many ${table.name} rows → many ${linkTable.name} rows`,
2021-03-02 07:03:33 +13:00
value: RelationshipTypes.MANY_TO_MANY,
},
{
name: `One ${linkName} row → many ${thisName} rows`,
alt: `One ${linkTable.name} rows → many ${table.name} rows`,
2021-03-02 07:03:33 +13:00
value: RelationshipTypes.ONE_TO_MANY,
},
{
name: `One ${thisName} row → many ${linkName} rows`,
alt: `One ${table.name} rows → many ${linkTable.name} rows`,
2021-03-02 07:03:33 +13:00
value: RelationshipTypes.MANY_TO_ONE,
},
]
}
2020-08-08 03:13:57 +12:00
</script>
2021-04-17 04:12:22 +12:00
<ModalContent
title={originalName ? "Edit Column" : "Create Column"}
2021-04-17 04:12:22 +12:00
confirmText="Save Column"
onConfirm={saveColumn}
disabled={invalid}
>
<Input label="Name" bind:value={field.name} disabled={uneditable} />
2020-08-08 03:13:57 +12:00
<Select
disabled={originalName}
label="Type"
bind:value={field.type}
on:change={handleTypeChange}
options={[
...Object.values(fieldDefinitions),
{ name: "Auto Column", type: AUTO_TYPE },
]}
getOptionLabel={field => field.name}
2021-05-04 22:32:22 +12:00
getOptionValue={field => field.type}
/>
2020-08-08 03:13:57 +12:00
2021-04-17 04:12:22 +12:00
{#if canBeRequired || canBeDisplay}
<div>
{#if canBeRequired}
<Toggle
value={required}
on:change={onChangeRequired}
disabled={primaryDisplay}
thin
text="Required"
/>
2021-04-17 04:12:22 +12:00
{/if}
{#if canBeDisplay}
<Toggle
bind:value={primaryDisplay}
on:change={onChangePrimaryDisplay}
thin
text="Use as table display column"
/>
2021-04-17 04:12:22 +12:00
{/if}
</div>
{/if}
2020-08-08 03:13:57 +12:00
{#if canBeSearched}
2021-04-17 04:12:22 +12:00
<div>
<Label grey small>Search Indexes</Label>
<Toggle
value={indexes[0] === field.name}
disabled={indexes[1] === field.name}
on:change={onChangePrimaryIndex}
text="Primary"
/>
2021-04-17 04:12:22 +12:00
<Toggle
value={indexes[1] === field.name}
disabled={!indexes[0] || indexes[0] === field.name}
on:change={onChangeSecondaryIndex}
text="Secondary"
/>
2021-04-17 04:12:22 +12:00
</div>
{/if}
{#if field.type === "string"}
<Input
type="number"
label="Max Length"
bind:value={field.constraints.length.maximum}
/>
{:else if field.type === "options"}
<ValuesList
label="Options (one per line)"
bind:values={field.constraints.inclusion}
/>
{:else if field.type === "datetime"}
<DatePicker
label="Earliest"
bind:value={field.constraints.datetime.earliest}
/>
<DatePicker label="Latest" bind:value={field.constraints.datetime.latest} />
{:else if field.type === "number"}
<Input
type="number"
label="Min Value"
bind:value={field.constraints.numericality.greaterThanOrEqualTo}
/>
<Input
type="number"
label="Max Value"
bind:value={field.constraints.numericality.lessThanOrEqualTo}
/>
{:else if field.type === "link"}
<Select
label="Table"
bind:value={field.tableId}
options={tableOptions}
getOptionLabel={table => table.name}
2021-05-04 22:32:22 +12:00
getOptionValue={table => table._id}
/>
{#if relationshipOptions && relationshipOptions.length > 0}
<RadioGroup
disabled={originalName}
label="Define the relationship"
bind:value={field.relationshipType}
options={relationshipOptions}
getOptionLabel={option => option.name}
2021-05-04 22:32:22 +12:00
getOptionValue={option => option.value}
/>
{/if}
<Input label={`Column name in other table`} bind:value={field.fieldName} />
{:else if field.type === FORMULA_TYPE}
<ModalBindableInput
title="Handlebars Formula"
label="Formula"
value={field.formula}
on:change={e => (field.formula = e.detail)}
bindings={getBindings({ table })}
serverSide="true"
/>
{:else if field.type === AUTO_TYPE}
<Select
label="Auto Column Type"
value={field.subtype}
on:change={e => (field.subtype = e.detail)}
options={Object.entries(getAutoColumnInformation())}
getOptionLabel={option => option[1].name}
2021-05-04 22:32:22 +12:00
getOptionValue={option => option[0]}
/>
{/if}
2021-04-17 04:12:22 +12:00
<div slot="footer">
{#if !uneditable && originalName != null}
2021-04-17 04:12:22 +12:00
<Button warning text on:click={confirmDelete}>Delete</Button>
2020-10-24 05:38:10 +13:00
{/if}
2021-04-17 04:12:22 +12:00
</div>
</ModalContent>
2020-10-24 05:38:10 +13:00
<ConfirmDialog
bind:this={confirmDeleteDialog}
body={`Are you sure you wish to delete this column? Your data will be deleted and this action cannot be undone.`}
okText="Delete Column"
onOk={deleteColumn}
2020-10-24 11:55:51 +13:00
onCancel={hideDeleteDialog}
title="Confirm Deletion"
/>