Merge pull request #9205 from Budibase/fix/block-duplicate-autocolumn-types
Create/Edit Column refactoring and validation updates
This commit is contained in:
commit
02b6890d58
2 changed files with 243 additions and 112 deletions
|
@ -12,7 +12,7 @@
|
||||||
Modal,
|
Modal,
|
||||||
notifications,
|
notifications,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { createEventDispatcher, onMount } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import { tables, datasources } from "stores/backend"
|
import { tables, datasources } from "stores/backend"
|
||||||
import { TableNames, UNEDITABLE_USER_FIELDS } from "constants"
|
import { TableNames, UNEDITABLE_USER_FIELDS } from "constants"
|
||||||
|
@ -48,7 +48,22 @@
|
||||||
const { hide } = getContext(Context.Modal)
|
const { hide } = getContext(Context.Modal)
|
||||||
let fieldDefinitions = cloneDeep(FIELDS)
|
let fieldDefinitions = cloneDeep(FIELDS)
|
||||||
|
|
||||||
export let field = {
|
export let field
|
||||||
|
|
||||||
|
let originalName
|
||||||
|
let linkEditDisabled
|
||||||
|
let primaryDisplay
|
||||||
|
let indexes = [...($tables.selected.indexes || [])]
|
||||||
|
let isCreating
|
||||||
|
|
||||||
|
let table = $tables.selected
|
||||||
|
let confirmDeleteDialog
|
||||||
|
let deletion
|
||||||
|
let savingColumn
|
||||||
|
let deleteColName
|
||||||
|
let jsonSchemaModal
|
||||||
|
|
||||||
|
let editableColumn = {
|
||||||
type: "string",
|
type: "string",
|
||||||
constraints: fieldDefinitions.STRING.constraints,
|
constraints: fieldDefinitions.STRING.constraints,
|
||||||
|
|
||||||
|
@ -56,48 +71,80 @@
|
||||||
fieldName: $tables.selected.name,
|
fieldName: $tables.selected.name,
|
||||||
}
|
}
|
||||||
|
|
||||||
let originalName = field.name
|
$: if (primaryDisplay) {
|
||||||
const linkEditDisabled = originalName != null
|
editableColumn.constraints.presence = { allowEmpty: false }
|
||||||
let primaryDisplay =
|
}
|
||||||
$tables.selected.primaryDisplay == null ||
|
|
||||||
$tables.selected.primaryDisplay === field.name
|
|
||||||
let isCreating = originalName == null
|
|
||||||
|
|
||||||
let table = $tables.selected
|
$: if (field && !savingColumn) {
|
||||||
let indexes = [...($tables.selected.indexes || [])]
|
editableColumn = cloneDeep(field)
|
||||||
let confirmDeleteDialog
|
originalName = editableColumn.name ? editableColumn.name + "" : null
|
||||||
let deletion
|
linkEditDisabled = originalName != null
|
||||||
let deleteColName
|
isCreating = originalName == null
|
||||||
let jsonSchemaModal
|
primaryDisplay =
|
||||||
|
$tables.selected.primaryDisplay == null ||
|
||||||
|
$tables.selected.primaryDisplay === editableColumn.name
|
||||||
|
}
|
||||||
|
|
||||||
$: checkConstraints(field)
|
$: checkConstraints(editableColumn)
|
||||||
$: required = !!field?.constraints?.presence || primaryDisplay
|
$: required = !!editableColumn?.constraints?.presence || primaryDisplay
|
||||||
$: uneditable =
|
$: uneditable =
|
||||||
$tables.selected?._id === TableNames.USERS &&
|
$tables.selected?._id === TableNames.USERS &&
|
||||||
UNEDITABLE_USER_FIELDS.includes(field.name)
|
UNEDITABLE_USER_FIELDS.includes(editableColumn.name)
|
||||||
$: invalid =
|
$: invalid =
|
||||||
!field.name ||
|
!editableColumn?.name ||
|
||||||
(field.type === LINK_TYPE && !field.tableId) ||
|
(editableColumn?.type === LINK_TYPE && !editableColumn?.tableId) ||
|
||||||
Object.keys(errors).length !== 0
|
Object.keys(errors).length !== 0
|
||||||
$: errors = checkErrors(field)
|
$: errors = checkErrors(editableColumn)
|
||||||
$: datasource = $datasources.list.find(
|
$: datasource = $datasources.list.find(
|
||||||
source => source._id === table?.sourceId
|
source => source._id === table?.sourceId
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const getTableAutoColumnTypes = table => {
|
||||||
|
return Object.keys(table?.schema).reduce((acc, key) => {
|
||||||
|
let fieldSchema = table?.schema[key]
|
||||||
|
if (fieldSchema.autocolumn) {
|
||||||
|
acc.push(fieldSchema.subtype)
|
||||||
|
}
|
||||||
|
return acc
|
||||||
|
}, [])
|
||||||
|
}
|
||||||
|
|
||||||
|
let autoColumnInfo = getAutoColumnInformation()
|
||||||
|
|
||||||
|
$: tableAutoColumnsTypes = getTableAutoColumnTypes($tables?.selected)
|
||||||
|
$: availableAutoColumns = Object.keys(autoColumnInfo).reduce((acc, key) => {
|
||||||
|
if (!tableAutoColumnsTypes.includes(key)) {
|
||||||
|
acc[key] = autoColumnInfo[key]
|
||||||
|
}
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
$: availableAutoColumnKeys = availableAutoColumns
|
||||||
|
? Object.keys(availableAutoColumns)
|
||||||
|
: []
|
||||||
|
|
||||||
|
$: autoColumnOptions = editableColumn.autocolumn
|
||||||
|
? autoColumnInfo
|
||||||
|
: availableAutoColumns
|
||||||
|
|
||||||
// used to select what different options can be displayed for column type
|
// used to select what different options can be displayed for column type
|
||||||
$: canBeSearched =
|
$: canBeSearched =
|
||||||
field.type !== LINK_TYPE &&
|
editableColumn?.type !== LINK_TYPE &&
|
||||||
field.type !== JSON_TYPE &&
|
editableColumn?.type !== JSON_TYPE &&
|
||||||
field.subtype !== AUTO_COLUMN_SUB_TYPES.CREATED_BY &&
|
editableColumn?.subtype !== AUTO_COLUMN_SUB_TYPES.CREATED_BY &&
|
||||||
field.subtype !== AUTO_COLUMN_SUB_TYPES.UPDATED_BY &&
|
editableColumn?.subtype !== AUTO_COLUMN_SUB_TYPES.UPDATED_BY &&
|
||||||
field.type !== FORMULA_TYPE
|
editableColumn?.type !== FORMULA_TYPE
|
||||||
$: canBeDisplay =
|
$: canBeDisplay =
|
||||||
field.type !== LINK_TYPE &&
|
editableColumn?.type !== LINK_TYPE &&
|
||||||
field.type !== AUTO_TYPE &&
|
editableColumn?.type !== AUTO_TYPE &&
|
||||||
field.type !== JSON_TYPE
|
editableColumn?.type !== JSON_TYPE &&
|
||||||
|
!editableColumn.autocolumn
|
||||||
$: canBeRequired =
|
$: canBeRequired =
|
||||||
field.type !== LINK_TYPE && !uneditable && field.type !== AUTO_TYPE
|
editableColumn?.type !== LINK_TYPE &&
|
||||||
$: relationshipOptions = getRelationshipOptions(field)
|
!uneditable &&
|
||||||
|
editableColumn?.type !== AUTO_TYPE &&
|
||||||
|
!editableColumn.autocolumn
|
||||||
|
$: relationshipOptions = getRelationshipOptions(editableColumn)
|
||||||
$: external = table.type === "external"
|
$: external = table.type === "external"
|
||||||
// in the case of internal tables the sourceId will just be undefined
|
// in the case of internal tables the sourceId will just be undefined
|
||||||
$: tableOptions = $tables.list.filter(
|
$: tableOptions = $tables.list.filter(
|
||||||
|
@ -108,76 +155,90 @@
|
||||||
)
|
)
|
||||||
$: typeEnabled =
|
$: typeEnabled =
|
||||||
!originalName ||
|
!originalName ||
|
||||||
(originalName && SWITCHABLE_TYPES.indexOf(field.type) !== -1)
|
(originalName &&
|
||||||
|
SWITCHABLE_TYPES.indexOf(editableColumn.type) !== -1 &&
|
||||||
|
!editableColumn?.autocolumn)
|
||||||
|
|
||||||
async function saveColumn() {
|
async function saveColumn() {
|
||||||
if (field.type === AUTO_TYPE) {
|
savingColumn = true
|
||||||
field = buildAutoColumn($tables.draft.name, field.name, field.subtype)
|
if (errors?.length) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if (field.type !== LINK_TYPE) {
|
|
||||||
delete field.fieldName
|
let saveColumn = cloneDeep(editableColumn)
|
||||||
|
|
||||||
|
if (saveColumn.type === AUTO_TYPE) {
|
||||||
|
saveColumn = buildAutoColumn(
|
||||||
|
$tables.draft.name,
|
||||||
|
saveColumn.name,
|
||||||
|
saveColumn.subtype
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (saveColumn.type !== LINK_TYPE) {
|
||||||
|
delete saveColumn.fieldName
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await tables.saveField({
|
await tables.saveField({
|
||||||
originalName,
|
originalName,
|
||||||
field,
|
field: saveColumn,
|
||||||
primaryDisplay,
|
primaryDisplay,
|
||||||
indexes,
|
indexes,
|
||||||
})
|
})
|
||||||
dispatch("updatecolumns")
|
dispatch("updatecolumns")
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
notifications.error("Error saving column")
|
console.log(err)
|
||||||
|
notifications.error(`Error saving column: ${err.message}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancelEdit() {
|
function cancelEdit() {
|
||||||
field.name = originalName
|
editableColumn.name = originalName
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteColumn() {
|
function deleteColumn() {
|
||||||
try {
|
try {
|
||||||
field.name = deleteColName
|
editableColumn.name = deleteColName
|
||||||
if (field.name === $tables.selected.primaryDisplay) {
|
if (editableColumn.name === $tables.selected.primaryDisplay) {
|
||||||
notifications.error("You cannot delete the display column")
|
notifications.error("You cannot delete the display column")
|
||||||
} else {
|
} else {
|
||||||
tables.deleteField(field)
|
tables.deleteField(editableColumn)
|
||||||
notifications.success(`Column ${field.name} deleted.`)
|
notifications.success(`Column ${editableColumn.name} deleted.`)
|
||||||
confirmDeleteDialog.hide()
|
confirmDeleteDialog.hide()
|
||||||
hide()
|
hide()
|
||||||
deletion = false
|
deletion = false
|
||||||
dispatch("updatecolumns")
|
dispatch("updatecolumns")
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error("Error deleting column")
|
notifications.error(`Error deleting column: ${error.message}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTypeChange(event) {
|
function handleTypeChange(event) {
|
||||||
// remove any extra fields that may not be related to this type
|
// remove any extra fields that may not be related to this type
|
||||||
delete field.autocolumn
|
delete editableColumn.autocolumn
|
||||||
delete field.subtype
|
delete editableColumn.subtype
|
||||||
delete field.tableId
|
delete editableColumn.tableId
|
||||||
delete field.relationshipType
|
delete editableColumn.relationshipType
|
||||||
delete field.formulaType
|
delete editableColumn.formulaType
|
||||||
|
|
||||||
// Add in defaults and initial definition
|
// Add in defaults and initial definition
|
||||||
const definition = fieldDefinitions[event.detail?.toUpperCase()]
|
const definition = fieldDefinitions[event.detail?.toUpperCase()]
|
||||||
if (definition?.constraints) {
|
if (definition?.constraints) {
|
||||||
field.constraints = definition.constraints
|
editableColumn.constraints = definition.constraints
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default relationships many to many
|
// Default relationships many to many
|
||||||
if (field.type === LINK_TYPE) {
|
if (editableColumn.type === LINK_TYPE) {
|
||||||
field.relationshipType = RelationshipTypes.MANY_TO_MANY
|
editableColumn.relationshipType = RelationshipTypes.MANY_TO_MANY
|
||||||
}
|
}
|
||||||
if (field.type === FORMULA_TYPE) {
|
if (editableColumn.type === FORMULA_TYPE) {
|
||||||
field.formulaType = "dynamic"
|
editableColumn.formulaType = "dynamic"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChangeRequired(e) {
|
function onChangeRequired(e) {
|
||||||
const req = e.detail
|
const req = e.detail
|
||||||
field.constraints.presence = req ? { allowEmpty: false } : false
|
editableColumn.constraints.presence = req ? { allowEmpty: false } : false
|
||||||
required = req
|
required = req
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,17 +246,17 @@
|
||||||
const isPrimary = e.detail
|
const isPrimary = e.detail
|
||||||
// primary display is always required
|
// primary display is always required
|
||||||
if (isPrimary) {
|
if (isPrimary) {
|
||||||
field.constraints.presence = { allowEmpty: false }
|
editableColumn.constraints.presence = { allowEmpty: false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChangePrimaryIndex(e) {
|
function onChangePrimaryIndex(e) {
|
||||||
indexes = e.detail ? [field.name] : []
|
indexes = e.detail ? [editableColumn.name] : []
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChangeSecondaryIndex(e) {
|
function onChangeSecondaryIndex(e) {
|
||||||
if (e.detail) {
|
if (e.detail) {
|
||||||
indexes[1] = field.name
|
indexes[1] = editableColumn.name
|
||||||
} else {
|
} else {
|
||||||
indexes = indexes.slice(0, 1)
|
indexes = indexes.slice(0, 1)
|
||||||
}
|
}
|
||||||
|
@ -246,11 +307,14 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAllowedTypes() {
|
function getAllowedTypes() {
|
||||||
if (originalName && ALLOWABLE_STRING_TYPES.indexOf(field.type) !== -1) {
|
if (
|
||||||
|
originalName &&
|
||||||
|
ALLOWABLE_STRING_TYPES.indexOf(editableColumn.type) !== -1
|
||||||
|
) {
|
||||||
return ALLOWABLE_STRING_OPTIONS
|
return ALLOWABLE_STRING_OPTIONS
|
||||||
} else if (
|
} else if (
|
||||||
originalName &&
|
originalName &&
|
||||||
ALLOWABLE_NUMBER_TYPES.indexOf(field.type) !== -1
|
ALLOWABLE_NUMBER_TYPES.indexOf(editableColumn.type) !== -1
|
||||||
) {
|
) {
|
||||||
return ALLOWABLE_NUMBER_OPTIONS
|
return ALLOWABLE_NUMBER_OPTIONS
|
||||||
} else if (!external) {
|
} else if (!external) {
|
||||||
|
@ -275,6 +339,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkConstraints(fieldToCheck) {
|
function checkConstraints(fieldToCheck) {
|
||||||
|
if (!fieldToCheck) {
|
||||||
|
return
|
||||||
|
}
|
||||||
// most types need this, just make sure its always present
|
// most types need this, just make sure its always present
|
||||||
if (fieldToCheck && !fieldToCheck.constraints) {
|
if (fieldToCheck && !fieldToCheck.constraints) {
|
||||||
fieldToCheck.constraints = {}
|
fieldToCheck.constraints = {}
|
||||||
|
@ -296,10 +363,16 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkErrors(fieldInfo) {
|
function checkErrors(fieldInfo) {
|
||||||
|
if (!editableColumn) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
function inUse(tbl, column, ogName = null) {
|
function inUse(tbl, column, ogName = null) {
|
||||||
return Object.keys(tbl?.schema || {}).some(
|
const parsedColumn = column ? column.toLowerCase().trim() : column
|
||||||
key => key !== ogName && key === column
|
|
||||||
)
|
return Object.keys(tbl?.schema || {}).some(key => {
|
||||||
|
let lowerKey = key.toLowerCase()
|
||||||
|
return lowerKey !== ogName?.toLowerCase() && lowerKey === parsedColumn
|
||||||
|
})
|
||||||
}
|
}
|
||||||
const newError = {}
|
const newError = {}
|
||||||
if (!external && fieldInfo.name?.startsWith("_")) {
|
if (!external && fieldInfo.name?.startsWith("_")) {
|
||||||
|
@ -313,6 +386,11 @@
|
||||||
} else if (inUse($tables.draft, fieldInfo.name, originalName)) {
|
} else if (inUse($tables.draft, fieldInfo.name, originalName)) {
|
||||||
newError.name = `Column name already in use.`
|
newError.name = `Column name already in use.`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fieldInfo.type == "auto" && !fieldInfo.subtype) {
|
||||||
|
newError.subtype = `Auto Column requires a type`
|
||||||
|
}
|
||||||
|
|
||||||
if (fieldInfo.fieldName && fieldInfo.tableId) {
|
if (fieldInfo.fieldName && fieldInfo.tableId) {
|
||||||
const relatedTable = $tables.list.find(
|
const relatedTable = $tables.list.find(
|
||||||
tbl => tbl._id === fieldInfo.tableId
|
tbl => tbl._id === fieldInfo.tableId
|
||||||
|
@ -323,12 +401,6 @@
|
||||||
}
|
}
|
||||||
return newError
|
return newError
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
if (primaryDisplay) {
|
|
||||||
field.constraints.presence = { allowEmpty: false }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ModalContent
|
<ModalContent
|
||||||
|
@ -340,19 +412,26 @@
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
label="Name"
|
label="Name"
|
||||||
bind:value={field.name}
|
bind:value={editableColumn.name}
|
||||||
disabled={uneditable || (linkEditDisabled && field.type === LINK_TYPE)}
|
disabled={uneditable ||
|
||||||
|
(linkEditDisabled && editableColumn.type === LINK_TYPE)}
|
||||||
error={errors?.name}
|
error={errors?.name}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
disabled={!typeEnabled}
|
disabled={!typeEnabled}
|
||||||
label="Type"
|
label="Type"
|
||||||
bind:value={field.type}
|
bind:value={editableColumn.type}
|
||||||
on:change={handleTypeChange}
|
on:change={handleTypeChange}
|
||||||
options={getAllowedTypes()}
|
options={getAllowedTypes()}
|
||||||
getOptionLabel={field => field.name}
|
getOptionLabel={field => field.name}
|
||||||
getOptionValue={field => field.type}
|
getOptionValue={field => field.type}
|
||||||
|
isOptionEnabled={option => {
|
||||||
|
if (option.type == AUTO_TYPE) {
|
||||||
|
return availableAutoColumnKeys?.length > 0
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{#if canBeRequired || canBeDisplay}
|
{#if canBeRequired || canBeDisplay}
|
||||||
|
@ -381,32 +460,32 @@
|
||||||
<div>
|
<div>
|
||||||
<Label>Search Indexes</Label>
|
<Label>Search Indexes</Label>
|
||||||
<Toggle
|
<Toggle
|
||||||
value={indexes[0] === field.name}
|
value={indexes[0] === editableColumn.name}
|
||||||
disabled={indexes[1] === field.name}
|
disabled={indexes[1] === editableColumn.name}
|
||||||
on:change={onChangePrimaryIndex}
|
on:change={onChangePrimaryIndex}
|
||||||
text="Primary"
|
text="Primary"
|
||||||
/>
|
/>
|
||||||
<Toggle
|
<Toggle
|
||||||
value={indexes[1] === field.name}
|
value={indexes[1] === editableColumn.name}
|
||||||
disabled={!indexes[0] || indexes[0] === field.name}
|
disabled={!indexes[0] || indexes[0] === editableColumn.name}
|
||||||
on:change={onChangeSecondaryIndex}
|
on:change={onChangeSecondaryIndex}
|
||||||
text="Secondary"
|
text="Secondary"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if field.type === "string"}
|
{#if editableColumn.type === "string"}
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
label="Max Length"
|
label="Max Length"
|
||||||
bind:value={field.constraints.length.maximum}
|
bind:value={editableColumn.constraints.length.maximum}
|
||||||
/>
|
/>
|
||||||
{:else if field.type === "options"}
|
{:else if editableColumn.type === "options"}
|
||||||
<ValuesList
|
<ValuesList
|
||||||
label="Options (one per line)"
|
label="Options (one per line)"
|
||||||
bind:values={field.constraints.inclusion}
|
bind:values={editableColumn.constraints.inclusion}
|
||||||
/>
|
/>
|
||||||
{:else if field.type === "longform"}
|
{:else if editableColumn.type === "longform"}
|
||||||
<div>
|
<div>
|
||||||
<Label
|
<Label
|
||||||
size="M"
|
size="M"
|
||||||
|
@ -415,21 +494,24 @@
|
||||||
Formatting
|
Formatting
|
||||||
</Label>
|
</Label>
|
||||||
<Toggle
|
<Toggle
|
||||||
bind:value={field.useRichText}
|
bind:value={editableColumn.useRichText}
|
||||||
text="Enable rich text support (markdown)"
|
text="Enable rich text support (markdown)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{:else if field.type === "array"}
|
{:else if editableColumn.type === "array"}
|
||||||
<ValuesList
|
<ValuesList
|
||||||
label="Options (one per line)"
|
label="Options (one per line)"
|
||||||
bind:values={field.constraints.inclusion}
|
bind:values={editableColumn.constraints.inclusion}
|
||||||
/>
|
/>
|
||||||
{:else if field.type === "datetime"}
|
{:else if editableColumn.type === "datetime" && !editableColumn.autocolumn}
|
||||||
<DatePicker
|
<DatePicker
|
||||||
label="Earliest"
|
label="Earliest"
|
||||||
bind:value={field.constraints.datetime.earliest}
|
bind:value={editableColumn.constraints.datetime.earliest}
|
||||||
|
/>
|
||||||
|
<DatePicker
|
||||||
|
label="Latest"
|
||||||
|
bind:value={editableColumn.constraints.datetime.latest}
|
||||||
/>
|
/>
|
||||||
<DatePicker label="Latest" bind:value={field.constraints.datetime.latest} />
|
|
||||||
{#if datasource?.source !== "ORACLE" && datasource?.source !== "SQL_SERVER"}
|
{#if datasource?.source !== "ORACLE" && datasource?.source !== "SQL_SERVER"}
|
||||||
<div>
|
<div>
|
||||||
<Label
|
<Label
|
||||||
|
@ -439,25 +521,28 @@
|
||||||
>
|
>
|
||||||
Time zones
|
Time zones
|
||||||
</Label>
|
</Label>
|
||||||
<Toggle bind:value={field.ignoreTimezones} text="Ignore time zones" />
|
<Toggle
|
||||||
|
bind:value={editableColumn.ignoreTimezones}
|
||||||
|
text="Ignore time zones"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{:else if field.type === "number"}
|
{:else if editableColumn.type === "number" && !editableColumn.autocolumn}
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
label="Min Value"
|
label="Min Value"
|
||||||
bind:value={field.constraints.numericality.greaterThanOrEqualTo}
|
bind:value={editableColumn.constraints.numericality.greaterThanOrEqualTo}
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
label="Max Value"
|
label="Max Value"
|
||||||
bind:value={field.constraints.numericality.lessThanOrEqualTo}
|
bind:value={editableColumn.constraints.numericality.lessThanOrEqualTo}
|
||||||
/>
|
/>
|
||||||
{:else if field.type === "link"}
|
{:else if editableColumn.type === "link"}
|
||||||
<Select
|
<Select
|
||||||
label="Table"
|
label="Table"
|
||||||
disabled={linkEditDisabled}
|
disabled={linkEditDisabled}
|
||||||
bind:value={field.tableId}
|
bind:value={editableColumn.tableId}
|
||||||
options={tableOptions}
|
options={tableOptions}
|
||||||
getOptionLabel={table => table.name}
|
getOptionLabel={table => table.name}
|
||||||
getOptionValue={table => table._id}
|
getOptionValue={table => table._id}
|
||||||
|
@ -466,7 +551,7 @@
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
disabled={linkEditDisabled}
|
disabled={linkEditDisabled}
|
||||||
label="Define the relationship"
|
label="Define the relationship"
|
||||||
bind:value={field.relationshipType}
|
bind:value={editableColumn.relationshipType}
|
||||||
options={relationshipOptions}
|
options={relationshipOptions}
|
||||||
getOptionLabel={option => option.name}
|
getOptionLabel={option => option.name}
|
||||||
getOptionValue={option => option.value}
|
getOptionValue={option => option.value}
|
||||||
|
@ -476,14 +561,14 @@
|
||||||
<Input
|
<Input
|
||||||
disabled={linkEditDisabled}
|
disabled={linkEditDisabled}
|
||||||
label={`Column name in other table`}
|
label={`Column name in other table`}
|
||||||
bind:value={field.fieldName}
|
bind:value={editableColumn.fieldName}
|
||||||
error={errors.relatedName}
|
error={errors.relatedName}
|
||||||
/>
|
/>
|
||||||
{:else if field.type === FORMULA_TYPE}
|
{:else if editableColumn.type === FORMULA_TYPE}
|
||||||
{#if !table.sql}
|
{#if !table.sql}
|
||||||
<Select
|
<Select
|
||||||
label="Formula type"
|
label="Formula type"
|
||||||
bind:value={field.formulaType}
|
bind:value={editableColumn.formulaType}
|
||||||
options={[
|
options={[
|
||||||
{ label: "Dynamic", value: "dynamic" },
|
{ label: "Dynamic", value: "dynamic" },
|
||||||
{ label: "Static", value: "static" },
|
{ label: "Static", value: "static" },
|
||||||
|
@ -497,25 +582,28 @@
|
||||||
<ModalBindableInput
|
<ModalBindableInput
|
||||||
title="Formula"
|
title="Formula"
|
||||||
label="Formula"
|
label="Formula"
|
||||||
value={field.formula}
|
value={editableColumn.formula}
|
||||||
on:change={e => (field.formula = e.detail)}
|
on:change={e => (editableColumn.formula = e.detail)}
|
||||||
bindings={getBindings({ table })}
|
bindings={getBindings({ table })}
|
||||||
allowJS
|
allowJS
|
||||||
/>
|
/>
|
||||||
{:else if field.type === AUTO_TYPE}
|
{:else if editableColumn.type === JSON_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}
|
|
||||||
getOptionValue={option => option[0]}
|
|
||||||
/>
|
|
||||||
{:else if field.type === JSON_TYPE}
|
|
||||||
<Button primary text on:click={openJsonSchemaEditor}
|
<Button primary text on:click={openJsonSchemaEditor}
|
||||||
>Open schema editor</Button
|
>Open schema editor</Button
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if editableColumn.type === AUTO_TYPE || editableColumn.autocolumn}
|
||||||
|
<Select
|
||||||
|
label="Auto column type"
|
||||||
|
value={editableColumn.subtype}
|
||||||
|
on:change={e => (editableColumn.subtype = e.detail)}
|
||||||
|
options={Object.entries(autoColumnOptions)}
|
||||||
|
getOptionLabel={option => option[1].name}
|
||||||
|
getOptionValue={option => option[0]}
|
||||||
|
disabled={!availableAutoColumnKeys?.length || editableColumn.autocolumn}
|
||||||
|
error={errors?.subtype}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<div slot="footer">
|
<div slot="footer">
|
||||||
{#if !uneditable && originalName != null}
|
{#if !uneditable && originalName != null}
|
||||||
|
@ -525,11 +613,11 @@
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
<Modal bind:this={jsonSchemaModal}>
|
<Modal bind:this={jsonSchemaModal}>
|
||||||
<JSONSchemaModal
|
<JSONSchemaModal
|
||||||
schema={field.schema}
|
schema={editableColumn.schema}
|
||||||
json={field.json}
|
json={editableColumn.json}
|
||||||
on:save={({ detail }) => {
|
on:save={({ detail }) => {
|
||||||
field.schema = detail.schema
|
editableColumn.schema = detail.schema
|
||||||
field.json = detail.json
|
editableColumn.json = detail.json
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
@ -1,9 +1,45 @@
|
||||||
<script>
|
<script>
|
||||||
import TableDataTable from "components/backend/DataTable/DataTable.svelte"
|
import TableDataTable from "components/backend/DataTable/DataTable.svelte"
|
||||||
import { tables, database } from "stores/backend"
|
import { tables, database } from "stores/backend"
|
||||||
|
import { Banner } from "@budibase/bbui"
|
||||||
|
|
||||||
|
const verifyAutocolumns = table => {
|
||||||
|
// Check for duplicates
|
||||||
|
return Object.values(table?.schema || {}).reduce((acc, fieldSchema) => {
|
||||||
|
if (!fieldSchema.autocolumn || !fieldSchema.subtype) {
|
||||||
|
return acc
|
||||||
|
}
|
||||||
|
let fieldKey = fieldSchema.tableId
|
||||||
|
? `${fieldSchema.tableId}-${fieldSchema.subtype}`
|
||||||
|
: fieldSchema.subtype
|
||||||
|
acc[fieldKey] = acc[fieldKey] || []
|
||||||
|
acc[fieldKey].push(fieldSchema)
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
$: autoColumnStatus = verifyAutocolumns($tables?.selected)
|
||||||
|
$: duplicates = Object.values(autoColumnStatus).reduce((acc, status) => {
|
||||||
|
if (status.length > 1) {
|
||||||
|
acc = [...acc, ...status]
|
||||||
|
}
|
||||||
|
return acc
|
||||||
|
}, [])
|
||||||
|
$: invalidColumnText = duplicates.map(entry => {
|
||||||
|
return `${entry.name} (${entry.subtype})`
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $database?._id && $tables?.selected?.name}
|
{#if $database?._id && $tables?.selected?.name}
|
||||||
|
{#if duplicates?.length}
|
||||||
|
<div class="alert-wrap">
|
||||||
|
<Banner type="warning" showCloseButton={false}>
|
||||||
|
{`Schema Invalid - There are duplicate auto column types defined in this schema.
|
||||||
|
Please delete the duplicate entries where appropriate: -
|
||||||
|
${invalidColumnText.join(", ")}`}
|
||||||
|
</Banner>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
<TableDataTable />
|
<TableDataTable />
|
||||||
{:else}<i>Create your first table to start building</i>{/if}
|
{:else}<i>Create your first table to start building</i>{/if}
|
||||||
|
|
||||||
|
@ -13,4 +49,11 @@
|
||||||
color: var(--grey-5);
|
color: var(--grey-5);
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
.alert-wrap {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.alert-wrap :global(> *) {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in a new issue