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