Updates and refactoring of Automation flows
This commit is contained in:
parent
27917c78d1
commit
e9985adcd4
|
@ -14,36 +14,10 @@
|
||||||
let failedParse = null
|
let failedParse = null
|
||||||
let trigger = {}
|
let trigger = {}
|
||||||
let schemaProperties = {}
|
let schemaProperties = {}
|
||||||
let baseData = {}
|
|
||||||
|
|
||||||
let rowEvents = [
|
|
||||||
AutomationEventType.ROW_DELETE,
|
|
||||||
AutomationEventType.ROW_SAVE,
|
|
||||||
AutomationEventType.ROW_UPDATE,
|
|
||||||
]
|
|
||||||
|
|
||||||
const memoTestData = memo($selectedAutomation.testData)
|
const memoTestData = memo($selectedAutomation.testData)
|
||||||
$: memoTestData.set($selectedAutomation.testData)
|
$: memoTestData.set($selectedAutomation.testData)
|
||||||
|
|
||||||
$: if (memoTestData) {
|
|
||||||
baseData = cloneDeep($selectedAutomation.testData)
|
|
||||||
// Reset the test data for row trigger data when the table is changed.
|
|
||||||
if (rowEvents.includes(trigger?.event)) {
|
|
||||||
if (
|
|
||||||
!baseData?.row?.tableId ||
|
|
||||||
baseData.row.tableId !== trigger.inputs?.tableId
|
|
||||||
) {
|
|
||||||
baseData = {
|
|
||||||
...baseData,
|
|
||||||
_tableId: trigger.inputs?.tableId,
|
|
||||||
row: { tableId: trigger.inputs?.tableId },
|
|
||||||
meta: {},
|
|
||||||
id: "",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
// clone the trigger so we're not mutating the reference
|
// clone the trigger so we're not mutating the reference
|
||||||
trigger = cloneDeep($selectedAutomation.definition.trigger)
|
trigger = cloneDeep($selectedAutomation.definition.trigger)
|
||||||
|
@ -59,7 +33,7 @@
|
||||||
|
|
||||||
// Check the schema to see if required fields have been entered
|
// Check the schema to see if required fields have been entered
|
||||||
$: isError = !trigger.schema.outputs.required.every(
|
$: isError = !trigger.schema.outputs.required.every(
|
||||||
required => baseData?.[required] || required !== "row"
|
required => $memoTestData?.[required] || required !== "row"
|
||||||
)
|
)
|
||||||
|
|
||||||
function parseTestJSON(e) {
|
function parseTestJSON(e) {
|
||||||
|
@ -74,7 +48,7 @@
|
||||||
|
|
||||||
const testAutomation = async () => {
|
const testAutomation = async () => {
|
||||||
try {
|
try {
|
||||||
await automationStore.actions.test($selectedAutomation, baseData)
|
await automationStore.actions.test($selectedAutomation, $memoTestData)
|
||||||
$automationStore.showTestPanel = true
|
$automationStore.showTestPanel = true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error(error)
|
notifications.error(error)
|
||||||
|
@ -112,7 +86,7 @@
|
||||||
{#if selectedValues}
|
{#if selectedValues}
|
||||||
<div class="tab-content-padding">
|
<div class="tab-content-padding">
|
||||||
<AutomationBlockSetup
|
<AutomationBlockSetup
|
||||||
testData={baseData}
|
testData={$memoTestData}
|
||||||
{schemaProperties}
|
{schemaProperties}
|
||||||
isTestModal
|
isTestModal
|
||||||
block={trigger}
|
block={trigger}
|
||||||
|
|
|
@ -52,7 +52,11 @@
|
||||||
import { TriggerStepID, ActionStepID } from "constants/backend/automations"
|
import { TriggerStepID, ActionStepID } from "constants/backend/automations"
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import { AutomationEventType } from "@budibase/types"
|
import {
|
||||||
|
AutomationEventType,
|
||||||
|
AutomationStepType,
|
||||||
|
AutomationActionStepId,
|
||||||
|
} from "@budibase/types"
|
||||||
import { FIELDS } from "constants/backend"
|
import { FIELDS } from "constants/backend"
|
||||||
import PropField from "./PropField.svelte"
|
import PropField from "./PropField.svelte"
|
||||||
|
|
||||||
|
@ -69,6 +73,13 @@
|
||||||
TriggerStepID.ROW_SAVED,
|
TriggerStepID.ROW_SAVED,
|
||||||
TriggerStepID.ROW_DELETED,
|
TriggerStepID.ROW_DELETED,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
let rowEvents = [
|
||||||
|
AutomationEventType.ROW_DELETE,
|
||||||
|
AutomationEventType.ROW_SAVE,
|
||||||
|
AutomationEventType.ROW_UPDATE,
|
||||||
|
]
|
||||||
|
|
||||||
const rowSteps = [ActionStepID.UPDATE_ROW, ActionStepID.CREATE_ROW]
|
const rowSteps = [ActionStepID.UPDATE_ROW, ActionStepID.CREATE_ROW]
|
||||||
|
|
||||||
let webhookModal
|
let webhookModal
|
||||||
|
@ -92,9 +103,11 @@
|
||||||
}).schema
|
}).schema
|
||||||
$: schemaFields = Object.values(schema || {})
|
$: schemaFields = Object.values(schema || {})
|
||||||
$: queryLimit = tableId?.includes("datasource") ? "∞" : "1000"
|
$: queryLimit = tableId?.includes("datasource") ? "∞" : "1000"
|
||||||
$: isTrigger = block?.type === "TRIGGER"
|
$: isTrigger = block?.type === AutomationStepType.TRIGGER
|
||||||
$: codeMode =
|
$: codeMode =
|
||||||
stepId === "EXECUTE_BASH" ? EditorModes.Handlebars : EditorModes.JS
|
stepId === AutomationActionStepId.EXECUTE_BASH
|
||||||
|
? EditorModes.Handlebars
|
||||||
|
: EditorModes.JS
|
||||||
$: bindingsHelpers = new BindingHelpers(getCaretPosition, insertAtPos, {
|
$: bindingsHelpers = new BindingHelpers(getCaretPosition, insertAtPos, {
|
||||||
disableWrapping: true,
|
disableWrapping: true,
|
||||||
})
|
})
|
||||||
|
@ -133,7 +146,6 @@
|
||||||
$: customStepLayouts($memoBlock, schemaProperties)
|
$: customStepLayouts($memoBlock, schemaProperties)
|
||||||
|
|
||||||
const customStepLayouts = block => {
|
const customStepLayouts = block => {
|
||||||
console.log("BUILDING", inputData["row"])
|
|
||||||
if (
|
if (
|
||||||
rowSteps.includes(block.stepId) ||
|
rowSteps.includes(block.stepId) ||
|
||||||
(rowTriggers.includes(block.stepId) && isTestModal)
|
(rowTriggers.includes(block.stepId) && isTestModal)
|
||||||
|
@ -157,14 +169,16 @@
|
||||||
{
|
{
|
||||||
type: DrawerBindableInput,
|
type: DrawerBindableInput,
|
||||||
title: rowRevlabel,
|
title: rowRevlabel,
|
||||||
panel: AutomationBindingPanel,
|
props: {
|
||||||
value: inputData["revision"],
|
panel: AutomationBindingPanel,
|
||||||
onChange: e => {
|
value: inputData["revision"],
|
||||||
onChange({ ["revision"]: e.detail })
|
onChange: e => {
|
||||||
|
onChange({ ["revision"]: e.detail })
|
||||||
|
},
|
||||||
|
bindings,
|
||||||
|
updateOnChange: false,
|
||||||
|
forceModal: true,
|
||||||
},
|
},
|
||||||
bindings,
|
|
||||||
updateOnChange: false,
|
|
||||||
forceModal: true,
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: []
|
: []
|
||||||
|
@ -248,12 +262,73 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for row trigger automation updates.
|
||||||
|
@param {object} update - An automation block.inputs update object
|
||||||
|
@example
|
||||||
|
onRowTriggerUpdate({
|
||||||
|
"tableId" : "ta_bb_employee"
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
const onRowTriggerUpdate = async update => {
|
||||||
|
if (
|
||||||
|
update.hasOwnProperty("tableId") &&
|
||||||
|
$selectedAutomation.testData.row.tableId !== update.tableId
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const reqSchema = getSchemaForDatasourcePlus(update.tableId, {
|
||||||
|
searchableSchema: true,
|
||||||
|
}).schema
|
||||||
|
|
||||||
|
// Parse the block inputs as usual
|
||||||
|
const updatedAutomation =
|
||||||
|
await automationStore.actions.processBlockInputs(block, {
|
||||||
|
schema: reqSchema,
|
||||||
|
...update,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Save the entire automation and reset the testData
|
||||||
|
await automationStore.actions.save({
|
||||||
|
...updatedAutomation,
|
||||||
|
testData: {
|
||||||
|
// Reset Core fields
|
||||||
|
row: { tableId: update.tableId },
|
||||||
|
meta: {},
|
||||||
|
id: "",
|
||||||
|
revision: "",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error saving automation", error)
|
||||||
|
notifications.error("Error saving automation")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for automation block input updates.
|
||||||
|
@param {object} update - An automation inputs update object
|
||||||
|
@example
|
||||||
|
onChange({
|
||||||
|
meta: { fields : { "Photo": { useAttachmentBinding: false }} }
|
||||||
|
row: { "Active": true, "Order Id" : 14, ... }
|
||||||
|
})
|
||||||
|
*/
|
||||||
const onChange = Utils.sequential(async update => {
|
const onChange = Utils.sequential(async update => {
|
||||||
|
const request = cloneDeep(update)
|
||||||
|
|
||||||
|
// Process row trigger updates
|
||||||
|
if (isTrigger && !isTestModal) {
|
||||||
|
await onRowTriggerUpdate(request)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// We need to cache the schema as part of the definition because it is
|
// We need to cache the schema as part of the definition because it is
|
||||||
// used in the server to detect relationships. It would be far better to
|
// used in the server to detect relationships. It would be far better to
|
||||||
// instead fetch the schema in the backend at runtime.
|
// instead fetch the schema in the backend at runtime.
|
||||||
const request = cloneDeep(update)
|
// If _tableId is explicitly included in the update request, the schema will be requested
|
||||||
|
|
||||||
let schema
|
let schema
|
||||||
if (request?._tableId) {
|
if (request?._tableId) {
|
||||||
schema = getSchemaForDatasourcePlus(request._tableId, {
|
schema = getSchemaForDatasourcePlus(request._tableId, {
|
||||||
|
@ -643,18 +718,16 @@
|
||||||
<div class="label-wrapper">
|
<div class="label-wrapper">
|
||||||
<Label>{label}</Label>
|
<Label>{label}</Label>
|
||||||
</div>
|
</div>
|
||||||
{JSON.stringify(inputData)}
|
|
||||||
<div class="toggle-container">
|
<div class="toggle-container">
|
||||||
<Toggle
|
<Toggle
|
||||||
value={inputData?.meta?.useAttachmentBinding}
|
value={inputData?.meta?.useAttachmentBinding}
|
||||||
text={"Use bindings"}
|
text={"Use bindings"}
|
||||||
size={"XS"}
|
size={"XS"}
|
||||||
on:change={e => {
|
on:change={e => {
|
||||||
// DEAN - review this
|
|
||||||
onChange({
|
onChange({
|
||||||
row: { [key]: "" }, //null
|
[key]: null,
|
||||||
meta: {
|
meta: {
|
||||||
[key]: e.detail,
|
useAttachmentBinding: e.detail,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
|
@ -662,24 +735,50 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="attachment-field-width">
|
<div class="attachment-field-width">
|
||||||
<KeyValueBuilder
|
{#if !inputData?.meta?.useAttachmentBinding}
|
||||||
on:change={e =>
|
<KeyValueBuilder
|
||||||
onChange({
|
on:change={e =>
|
||||||
[key]: e.detail.map(({ name, value }) => ({
|
onChange({
|
||||||
url: name,
|
[key]: e.detail.map(({ name, value }) => ({
|
||||||
filename: value,
|
url: name,
|
||||||
})),
|
filename: value,
|
||||||
})}
|
})),
|
||||||
object={handleAttachmentParams(inputData[key])}
|
})}
|
||||||
allowJS
|
object={handleAttachmentParams(inputData[key])}
|
||||||
{bindings}
|
allowJS
|
||||||
keyBindings
|
{bindings}
|
||||||
customButtonText={value.type === "attachment"
|
keyBindings
|
||||||
? "Add attachment"
|
customButtonText={value.type === "attachment"
|
||||||
: "Add signature"}
|
? "Add attachment"
|
||||||
keyPlaceholder={"URL"}
|
: "Add signature"}
|
||||||
valuePlaceholder={"Filename"}
|
keyPlaceholder={"URL"}
|
||||||
/>
|
valuePlaceholder={"Filename"}
|
||||||
|
/>
|
||||||
|
{:else if isTestModal}
|
||||||
|
<ModalBindableInput
|
||||||
|
title={value.title || label}
|
||||||
|
value={inputData[key]}
|
||||||
|
panel={AutomationBindingPanel}
|
||||||
|
type={value.customType}
|
||||||
|
on:change={e => onChange({ [key]: e.detail })}
|
||||||
|
{bindings}
|
||||||
|
updateOnChange={false}
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
<DrawerBindableInput
|
||||||
|
title={value.title ?? label}
|
||||||
|
panel={AutomationBindingPanel}
|
||||||
|
type={value.customType}
|
||||||
|
value={inputData[key]}
|
||||||
|
on:change={e => onChange({ [key]: e.detail })}
|
||||||
|
{bindings}
|
||||||
|
updateOnChange={false}
|
||||||
|
placeholder={value.customType === "queryLimit"
|
||||||
|
? queryLimit
|
||||||
|
: ""}
|
||||||
|
drawerLeft="260px"
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{:else if value.customType === "filters"}
|
{:else if value.customType === "filters"}
|
||||||
|
|
|
@ -1,20 +1,38 @@
|
||||||
<script>
|
<script>
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import RowSelectorTypes from "./RowSelectorTypes.svelte"
|
|
||||||
import PropField from "./PropField.svelte"
|
import PropField from "./PropField.svelte"
|
||||||
|
import DrawerBindableInput from "../../common/bindings/DrawerBindableInput.svelte"
|
||||||
|
import ModalBindableInput from "../../common/bindings/ModalBindableInput.svelte"
|
||||||
|
import AutomationBindingPanel from "../../common/bindings/ServerBindingPanel.svelte"
|
||||||
|
import { DatePicker, Select } from "@budibase/bbui"
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
export let value
|
export let value = {}
|
||||||
export let bindings
|
export let bindings
|
||||||
export let block
|
export let block
|
||||||
export let isTestModal
|
export let isTestModal
|
||||||
|
|
||||||
let schemaFields
|
let schemaFields
|
||||||
|
let editableValue
|
||||||
|
|
||||||
|
$: processValue(value)
|
||||||
|
|
||||||
|
const processValue = value => {
|
||||||
|
editableValue = { ...value }
|
||||||
|
// DEAN - review this
|
||||||
|
// const fieldKeys = Object.keys(block?.inputs?.fields)
|
||||||
|
// // Purge orphaned keys
|
||||||
|
// Object.keys(editableValue || {}).forEach(key => {
|
||||||
|
// if (!fieldKeys.includes(key)) {
|
||||||
|
// delete editableValue[key]
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
let fields = {}
|
let fields = {}
|
||||||
|
// DEAN - review this
|
||||||
for (const [key, type] of Object.entries(block?.inputs?.fields ?? {})) {
|
for (const [key, type] of Object.entries(block?.inputs?.fields ?? {})) {
|
||||||
fields = {
|
fields = {
|
||||||
...fields,
|
...fields,
|
||||||
|
@ -26,8 +44,8 @@
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value[key] === type) {
|
if (editableValue[key] === type) {
|
||||||
value[key] = INITIAL_VALUES[type.toUpperCase()]
|
editableValue[key] = INITIAL_VALUES[type.toUpperCase()]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,52 +57,14 @@
|
||||||
NUMBER: null,
|
NUMBER: null,
|
||||||
DATETIME: null,
|
DATETIME: null,
|
||||||
STRING: "",
|
STRING: "",
|
||||||
OPTIONS: [],
|
ARRAY: "",
|
||||||
ARRAY: [],
|
|
||||||
}
|
|
||||||
|
|
||||||
const coerce = (value, type) => {
|
|
||||||
const re = new RegExp(/{{([^{].*?)}}/g)
|
|
||||||
if (re.test(value)) {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === "boolean") {
|
|
||||||
if (typeof value === "boolean") {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
return value === "true"
|
|
||||||
}
|
|
||||||
if (type === "number") {
|
|
||||||
if (typeof value === "number") {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
return Number(value)
|
|
||||||
}
|
|
||||||
if (type === "options") {
|
|
||||||
return [value]
|
|
||||||
}
|
|
||||||
if (type === "array") {
|
|
||||||
if (Array.isArray(value)) {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
return value.split(",").map(x => x.trim())
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === "link") {
|
|
||||||
if (Array.isArray(value)) {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
return [value]
|
|
||||||
}
|
|
||||||
|
|
||||||
return value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const onChange = (e, field, type) => {
|
const onChange = (e, field, type) => {
|
||||||
value[field] = coerce(e.detail, type)
|
if (e.detail !== editableValue[field]) {
|
||||||
dispatch("change", value)
|
editableValue[field] = e.detail
|
||||||
|
dispatch("change", editableValue)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -92,14 +72,34 @@
|
||||||
<div class="fields">
|
<div class="fields">
|
||||||
{#each schemaFields as [field, schema]}
|
{#each schemaFields as [field, schema]}
|
||||||
<PropField label={field}>
|
<PropField label={field}>
|
||||||
<RowSelectorTypes
|
{#if ["string", "number", "array"].includes(schema.type)}
|
||||||
{isTestModal}
|
<svelte:component
|
||||||
{field}
|
this={isTestModal ? ModalBindableInput : DrawerBindableInput}
|
||||||
{schema}
|
panel={AutomationBindingPanel}
|
||||||
{bindings}
|
value={editableValue[field]}
|
||||||
{value}
|
on:change={e => onChange(e, field)}
|
||||||
{onChange}
|
type="string"
|
||||||
/>
|
{bindings}
|
||||||
|
allowJS={true}
|
||||||
|
updateOnChange={false}
|
||||||
|
title={schema.name}
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
{:else if schema.type === "boolean"}
|
||||||
|
<Select
|
||||||
|
on:change={e => onChange(e, field)}
|
||||||
|
value={editableValue[field]}
|
||||||
|
options={[
|
||||||
|
{ label: "True", value: "true" },
|
||||||
|
{ label: "False", value: "false" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
{:else if schema.type === "datetime"}
|
||||||
|
<DatePicker
|
||||||
|
value={editableValue[field]}
|
||||||
|
on:change={e => onChange(e, field)}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
</PropField>
|
</PropField>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,12 +2,18 @@
|
||||||
import { Label } from "@budibase/bbui"
|
import { Label } from "@budibase/bbui"
|
||||||
|
|
||||||
export let label
|
export let label
|
||||||
|
export let labelTooltip
|
||||||
export let fullWidth = false
|
export let fullWidth = false
|
||||||
|
export let componentWidth = 320
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="prop-field" class:fullWidth>
|
<div
|
||||||
<div class="prop-label">
|
class="prop-field"
|
||||||
<Label>{label}</Label>
|
class:fullWidth
|
||||||
|
style={`--comp-width: ${componentWidth}px;`}
|
||||||
|
>
|
||||||
|
<div class="prop-label" title={label}>
|
||||||
|
<Label tooltip={labelTooltip}>{label}</Label>
|
||||||
</div>
|
</div>
|
||||||
<div class="prop-control">
|
<div class="prop-control">
|
||||||
<slot />
|
<slot />
|
||||||
|
@ -17,15 +23,30 @@
|
||||||
<style>
|
<style>
|
||||||
.prop-field {
|
.prop-field {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 320px;
|
grid-template-columns: 1fr var(--comp-width);
|
||||||
}
|
}
|
||||||
|
|
||||||
.prop-field.fullWidth {
|
.prop-field.fullWidth {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.prop-field.fullWidth .prop-label {
|
||||||
|
margin-bottom: var(--spacing-s);
|
||||||
|
}
|
||||||
|
|
||||||
.prop-label {
|
.prop-label {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prop-label :global(> div) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prop-label :global(> div > label) {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -43,13 +43,9 @@
|
||||||
let customPopover
|
let customPopover
|
||||||
let popoverAnchor
|
let popoverAnchor
|
||||||
let editableRow = {}
|
let editableRow = {}
|
||||||
|
|
||||||
//??
|
|
||||||
let editableMeta = {}
|
|
||||||
let editableFields = {}
|
let editableFields = {}
|
||||||
// let columns = new Set()
|
|
||||||
|
|
||||||
// Avoid unnecessary updates - DEAN double check after refactor
|
// Avoid unnecessary updates
|
||||||
$: memoStore.set({
|
$: memoStore.set({
|
||||||
row,
|
row,
|
||||||
meta,
|
meta,
|
||||||
|
@ -61,11 +57,6 @@
|
||||||
editableFields = cloneDeep($memoStore?.meta?.fields)
|
editableFields = cloneDeep($memoStore?.meta?.fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Needs to go now... entirely
|
|
||||||
// $: if ($memoStore?.meta?.columns) {
|
|
||||||
// columns = new Set(meta?.columns)
|
|
||||||
// }
|
|
||||||
|
|
||||||
$: parsedBindings = bindings.map(binding => {
|
$: parsedBindings = bindings.map(binding => {
|
||||||
let clone = Object.assign({}, binding)
|
let clone = Object.assign({}, binding)
|
||||||
clone.icon = "ShareAndroid"
|
clone.icon = "ShareAndroid"
|
||||||
|
@ -74,6 +65,9 @@
|
||||||
|
|
||||||
$: tableId = $memoStore?.row?.tableId
|
$: tableId = $memoStore?.row?.tableId
|
||||||
$: if (tableId) {
|
$: if (tableId) {
|
||||||
|
// Refresh all the row data
|
||||||
|
editableRow = cloneDeep($memoStore?.row)
|
||||||
|
|
||||||
table = $tables.list.find(table => table._id === tableId)
|
table = $tables.list.find(table => table._id === tableId)
|
||||||
|
|
||||||
if (table) {
|
if (table) {
|
||||||
|
@ -97,12 +91,11 @@
|
||||||
}
|
}
|
||||||
editableFields = editableFields
|
editableFields = editableFields
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go through the table schema and build out the editable content
|
// Go through the table schema and build out the editable content
|
||||||
// schemaFields.forEach(entry => {
|
|
||||||
for (const entry of schemaFields) {
|
for (const entry of schemaFields) {
|
||||||
const [key, fieldSchema] = entry
|
const [key, fieldSchema] = entry
|
||||||
if ($memoStore?.row?.[key] && !editableRow?.[key]) {
|
if ($memoStore?.row?.[key]) {
|
||||||
|
// DEAN - review this
|
||||||
editableRow = {
|
editableRow = {
|
||||||
...editableRow,
|
...editableRow,
|
||||||
[key]: $memoStore?.row[key],
|
[key]: $memoStore?.row[key],
|
||||||
|
@ -110,7 +103,23 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Legacy
|
// Legacy
|
||||||
if (editableFields[key]?.clearRelationships) {
|
const emptyField =
|
||||||
|
!$memoStore?.row[key] || $memoStore?.row[key]?.length === 0
|
||||||
|
|
||||||
|
// Legacy
|
||||||
|
// Put non-empty elements into the update and add their key to the fields list.
|
||||||
|
if (!emptyField && !editableFields.hasOwnProperty(key)) {
|
||||||
|
//DEAN - review this - IF THEY ADDED A NEW ONE IT WOULD BE MISSING FROM editableFields + editableFields
|
||||||
|
console.log("EMPTY STATE DETECTED")
|
||||||
|
editableFields = {
|
||||||
|
...editableFields,
|
||||||
|
[key]: key,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy - clearRelationships
|
||||||
|
// Init the field and add it to the update.
|
||||||
|
if (emptyField && editableFields[key]?.clearRelationships === true) {
|
||||||
const emptyField = coerce(
|
const emptyField = coerce(
|
||||||
!$memoStore?.row.hasOwnProperty(key) ? "" : $memoStore?.row[key],
|
!$memoStore?.row.hasOwnProperty(key) ? "" : $memoStore?.row[key],
|
||||||
fieldSchema.type
|
fieldSchema.type
|
||||||
|
@ -124,8 +133,6 @@
|
||||||
...editableRow,
|
...editableRow,
|
||||||
[key]: emptyField,
|
[key]: emptyField,
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("DEAN EMPTY - clearRelationships", emptyField)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,6 +200,14 @@
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isFullWidth = type => {
|
||||||
|
return (
|
||||||
|
attachmentTypes.includes(type) ||
|
||||||
|
type === FieldType.JSON ||
|
||||||
|
type === FieldType.LONGFORM
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const onChange = update => {
|
const onChange = update => {
|
||||||
const customizer = (objValue, srcValue, key) => {
|
const customizer = (objValue, srcValue, key) => {
|
||||||
if (isPlainObject(objValue) && isPlainObject(srcValue)) {
|
if (isPlainObject(objValue) && isPlainObject(srcValue)) {
|
||||||
|
@ -201,7 +216,6 @@
|
||||||
if (result[key] !== null) {
|
if (result[key] !== null) {
|
||||||
acc[key] = result[key]
|
acc[key] = result[key]
|
||||||
} else {
|
} else {
|
||||||
console.log(key + " is null", objValue)
|
|
||||||
}
|
}
|
||||||
return acc
|
return acc
|
||||||
}, {})
|
}, {})
|
||||||
|
@ -228,7 +242,7 @@
|
||||||
|
|
||||||
{#each schemaFields || [] as [field, schema]}
|
{#each schemaFields || [] as [field, schema]}
|
||||||
{#if !schema.autocolumn && editableFields.hasOwnProperty(field)}
|
{#if !schema.autocolumn && editableFields.hasOwnProperty(field)}
|
||||||
<PropField label={field} fullWidth={attachmentTypes.includes(schema.type)}>
|
<PropField label={field} fullWidth={isFullWidth(schema.type)}>
|
||||||
<div class="prop-control-wrap">
|
<div class="prop-control-wrap">
|
||||||
{#if isTestModal}
|
{#if isTestModal}
|
||||||
<RowSelectorTypes
|
<RowSelectorTypes
|
||||||
|
@ -249,13 +263,12 @@
|
||||||
type={schema.type}
|
type={schema.type}
|
||||||
{schema}
|
{schema}
|
||||||
value={editableRow[field]}
|
value={editableRow[field]}
|
||||||
on:change={e => {
|
on:change={e =>
|
||||||
onChange({
|
onChange({
|
||||||
row: {
|
row: {
|
||||||
[field]: e.detail.row[field],
|
[field]: e.detail,
|
||||||
},
|
},
|
||||||
})
|
})}
|
||||||
}}
|
|
||||||
{bindings}
|
{bindings}
|
||||||
allowJS={true}
|
allowJS={true}
|
||||||
updateOnChange={false}
|
updateOnChange={false}
|
||||||
|
@ -280,24 +293,11 @@
|
||||||
<Icon
|
<Icon
|
||||||
hoverable
|
hoverable
|
||||||
name="Close"
|
name="Close"
|
||||||
on:click={() => {
|
on:click={() =>
|
||||||
// Clear row data
|
|
||||||
const update = { ...editableRow }
|
|
||||||
update[field] = null
|
|
||||||
// delete update[field]
|
|
||||||
|
|
||||||
// Clear any related metadata
|
|
||||||
// delete editableFields[field]
|
|
||||||
// editableFields[field] = null
|
|
||||||
console.log("REMOVE STATE", {
|
|
||||||
row: update,
|
|
||||||
meta: { fields: { ...editableFields, [field]: null } },
|
|
||||||
})
|
|
||||||
onChange({
|
onChange({
|
||||||
row: update,
|
row: { [field]: null },
|
||||||
meta: { fields: { ...editableFields, [field]: null } },
|
meta: { fields: { [field]: null } },
|
||||||
})
|
})}
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</PropField>
|
</PropField>
|
||||||
|
@ -327,6 +327,7 @@
|
||||||
bind:this={customPopover}
|
bind:this={customPopover}
|
||||||
anchor={popoverAnchor}
|
anchor={popoverAnchor}
|
||||||
minWidth={popoverAnchor?.getBoundingClientRect()?.width}
|
minWidth={popoverAnchor?.getBoundingClientRect()?.width}
|
||||||
|
maxWidth={popoverAnchor?.getBoundingClientRect()?.width}
|
||||||
maxHeight={300}
|
maxHeight={300}
|
||||||
resizable={false}
|
resizable={false}
|
||||||
offset={10}
|
offset={10}
|
||||||
|
@ -385,4 +386,9 @@
|
||||||
grid-template-columns: 1fr min-content;
|
grid-template-columns: 1fr min-content;
|
||||||
gap: var(--spacing-s);
|
gap: var(--spacing-s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Override for general json field override */
|
||||||
|
.prop-control-wrap :global(.icon.json-slot-icon) {
|
||||||
|
right: 1px !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
import DrawerBindableInput from "../../common/bindings/DrawerBindableInput.svelte"
|
import DrawerBindableInput from "../../common/bindings/DrawerBindableInput.svelte"
|
||||||
import ModalBindableInput from "../../common/bindings/ModalBindableInput.svelte"
|
import ModalBindableInput from "../../common/bindings/ModalBindableInput.svelte"
|
||||||
import AutomationBindingPanel from "../../common/bindings/ServerBindingPanel.svelte"
|
import AutomationBindingPanel from "../../common/bindings/ServerBindingPanel.svelte"
|
||||||
import Editor from "components/integration/QueryEditor.svelte"
|
import CodeEditor from "components/common/CodeEditor/CodeEditor.svelte"
|
||||||
import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte"
|
import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte"
|
||||||
|
|
||||||
export let onChange
|
export let onChange
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
export let bindings
|
export let bindings
|
||||||
export let isTestModal
|
export let isTestModal
|
||||||
|
|
||||||
$: console.log(field + "VALUE???", value[field])
|
$: fieldData = value[field]
|
||||||
|
|
||||||
$: parsedBindings = bindings.map(binding => {
|
$: parsedBindings = bindings.map(binding => {
|
||||||
let clone = Object.assign({}, binding)
|
let clone = Object.assign({}, binding)
|
||||||
|
@ -56,7 +56,6 @@
|
||||||
params[param.url || ""] = param.filename || ""
|
params[param.url || ""] = param.filename || ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log("handleAttachmentParams ", params)
|
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -69,12 +68,12 @@
|
||||||
[field]: e.detail,
|
[field]: e.detail,
|
||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
value={value[field]}
|
value={fieldData}
|
||||||
options={schema.constraints.inclusion}
|
options={schema.constraints.inclusion}
|
||||||
/>
|
/>
|
||||||
{:else if schema.type === "datetime"}
|
{:else if schema.type === "datetime"}
|
||||||
<DatePicker
|
<DatePicker
|
||||||
value={value[field]}
|
value={fieldData}
|
||||||
on:change={e =>
|
on:change={e =>
|
||||||
onChange({
|
onChange({
|
||||||
row: {
|
row: {
|
||||||
|
@ -90,7 +89,7 @@
|
||||||
[field]: e.detail,
|
[field]: e.detail,
|
||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
value={value[field]}
|
value={fieldData}
|
||||||
options={[
|
options={[
|
||||||
{ label: "True", value: "true" },
|
{ label: "True", value: "true" },
|
||||||
{ label: "False", value: "false" },
|
{ label: "False", value: "false" },
|
||||||
|
@ -98,7 +97,7 @@
|
||||||
/>
|
/>
|
||||||
{:else if schemaHasOptions(schema) && schema.type === "array"}
|
{:else if schemaHasOptions(schema) && schema.type === "array"}
|
||||||
<Multiselect
|
<Multiselect
|
||||||
value={value[field]}
|
value={fieldData}
|
||||||
options={schema.constraints.inclusion}
|
options={schema.constraints.inclusion}
|
||||||
on:change={e =>
|
on:change={e =>
|
||||||
onChange({
|
onChange({
|
||||||
|
@ -109,7 +108,7 @@
|
||||||
/>
|
/>
|
||||||
{:else if schema.type === "longform"}
|
{:else if schema.type === "longform"}
|
||||||
<TextArea
|
<TextArea
|
||||||
value={value[field]}
|
value={fieldData}
|
||||||
on:change={e =>
|
on:change={e =>
|
||||||
onChange({
|
onChange({
|
||||||
row: {
|
row: {
|
||||||
|
@ -119,24 +118,25 @@
|
||||||
/>
|
/>
|
||||||
{:else if schema.type === "json"}
|
{:else if schema.type === "json"}
|
||||||
<span>
|
<span>
|
||||||
<Editor
|
<div class="field-wrap">
|
||||||
editorHeight="150"
|
<CodeEditor
|
||||||
mode="json"
|
value={fieldData}
|
||||||
on:change={e => {
|
on:change={e => {
|
||||||
if (e.detail?.value !== value[field]) {
|
console.log("JSON change", e.detail?.value, fieldData)
|
||||||
onChange({
|
if (e.detail?.value !== fieldData) {
|
||||||
row: {
|
onChange({
|
||||||
[field]: e.detail,
|
row: {
|
||||||
},
|
[field]: e.detail,
|
||||||
})
|
},
|
||||||
}
|
})
|
||||||
}}
|
}
|
||||||
value={value[field]}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
</span>
|
</span>
|
||||||
{:else if schema.type === "link"}
|
{:else if schema.type === "link"}
|
||||||
<LinkedRowSelector
|
<LinkedRowSelector
|
||||||
linkedRows={value[field]}
|
linkedRows={fieldData}
|
||||||
{schema}
|
{schema}
|
||||||
on:change={e =>
|
on:change={e =>
|
||||||
onChange({
|
onChange({
|
||||||
|
@ -148,7 +148,7 @@
|
||||||
/>
|
/>
|
||||||
{:else if schema.type === "bb_reference" || schema.type === "bb_reference_single"}
|
{:else if schema.type === "bb_reference" || schema.type === "bb_reference_single"}
|
||||||
<LinkedRowSelector
|
<LinkedRowSelector
|
||||||
linkedRows={value[field]}
|
linkedRows={fieldData}
|
||||||
{schema}
|
{schema}
|
||||||
linkedTableId={"ta_users"}
|
linkedTableId={"ta_users"}
|
||||||
on:change={e =>
|
on:change={e =>
|
||||||
|
@ -167,16 +167,10 @@
|
||||||
text={"Use bindings"}
|
text={"Use bindings"}
|
||||||
size={"XS"}
|
size={"XS"}
|
||||||
on:change={e => {
|
on:change={e => {
|
||||||
const fromFalse =
|
|
||||||
!meta?.fields?.[field]?.useAttachmentBinding && e.detail === true
|
|
||||||
onChange({
|
onChange({
|
||||||
...(fromFalse
|
row: {
|
||||||
? {
|
[field]: null,
|
||||||
row: {
|
},
|
||||||
[field]: "", //clear the value if switching
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
meta: {
|
meta: {
|
||||||
fields: {
|
fields: {
|
||||||
[field]: {
|
[field]: {
|
||||||
|
@ -192,7 +186,7 @@
|
||||||
{#if !meta?.fields?.[field]?.useAttachmentBinding}
|
{#if !meta?.fields?.[field]?.useAttachmentBinding}
|
||||||
<div class="attachment-field-spacing">
|
<div class="attachment-field-spacing">
|
||||||
<KeyValueBuilder
|
<KeyValueBuilder
|
||||||
on:change={e =>
|
on:change={e => {
|
||||||
onChange({
|
onChange({
|
||||||
row: {
|
row: {
|
||||||
[field]:
|
[field]:
|
||||||
|
@ -203,14 +197,15 @@
|
||||||
url: e.detail[0].name,
|
url: e.detail[0].name,
|
||||||
filename: e.detail[0].value,
|
filename: e.detail[0].value,
|
||||||
}
|
}
|
||||||
: {}
|
: null
|
||||||
: e.detail.map(({ name, value }) => ({
|
: e.detail.map(({ name, value }) => ({
|
||||||
url: name,
|
url: name,
|
||||||
filename: value,
|
filename: value,
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
})}
|
})
|
||||||
object={handleAttachmentParams(value[field], false)}
|
}}
|
||||||
|
object={handleAttachmentParams(fieldData)}
|
||||||
allowJS
|
allowJS
|
||||||
{bindings}
|
{bindings}
|
||||||
keyBindings
|
keyBindings
|
||||||
|
@ -221,16 +216,15 @@
|
||||||
valuePlaceholder={"Filename"}
|
valuePlaceholder={"Filename"}
|
||||||
actionButtonDisabled={(schema.type === FieldType.ATTACHMENT_SINGLE ||
|
actionButtonDisabled={(schema.type === FieldType.ATTACHMENT_SINGLE ||
|
||||||
schema.type === FieldType.SIGNATURE_SINGLE) &&
|
schema.type === FieldType.SIGNATURE_SINGLE) &&
|
||||||
Object.keys(value[field] || {}).length >= 1}
|
Object.keys(fieldData || {}).length >= 1}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="json-input-spacing">
|
<div class="json-input-spacing">
|
||||||
{JSON.stringify(value[field])}
|
|
||||||
<svelte:component
|
<svelte:component
|
||||||
this={isTestModal ? ModalBindableInput : DrawerBindableInput}
|
this={isTestModal ? ModalBindableInput : DrawerBindableInput}
|
||||||
panel={AutomationBindingPanel}
|
panel={AutomationBindingPanel}
|
||||||
value={value[field]}
|
value={fieldData}
|
||||||
on:change={e =>
|
on:change={e =>
|
||||||
onChange({
|
onChange({
|
||||||
row: {
|
row: {
|
||||||
|
@ -247,11 +241,10 @@
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{:else if ["string", "number", "bigint", "barcodeqr", "array"].includes(schema.type)}
|
{:else if ["string", "number", "bigint", "barcodeqr", "array"].includes(schema.type)}
|
||||||
{JSON.stringify(value[field])}
|
|
||||||
<svelte:component
|
<svelte:component
|
||||||
this={isTestModal ? ModalBindableInput : DrawerBindableInput}
|
this={isTestModal ? ModalBindableInput : DrawerBindableInput}
|
||||||
panel={AutomationBindingPanel}
|
panel={AutomationBindingPanel}
|
||||||
value={value[field]}
|
value={fieldData}
|
||||||
on:change={e =>
|
on:change={e =>
|
||||||
onChange({
|
onChange({
|
||||||
row: {
|
row: {
|
||||||
|
@ -268,11 +261,20 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.attachment-field-spacing,
|
.attachment-field-spacing {
|
||||||
.json-input-spacing {
|
|
||||||
margin-top: var(--spacing-s);
|
|
||||||
border: 1px solid var(--spectrum-global-color-gray-400);
|
border: 1px solid var(--spectrum-global-color-gray-400);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: var(--spacing-s);
|
padding: var(--spacing-s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.field-wrap {
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px solid var(--spectrum-global-color-gray-400);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-wrap :global(.cm-editor),
|
||||||
|
.field-wrap :global(.cm-scroller) {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -157,7 +157,8 @@ const automationActions = store => ({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateBlockInputs: async (block, data) => {
|
|
||||||
|
processBlockInputs: async (block, data) => {
|
||||||
// Create new modified block
|
// Create new modified block
|
||||||
let newBlock = {
|
let newBlock = {
|
||||||
...block,
|
...block,
|
||||||
|
@ -184,6 +185,14 @@ const automationActions = store => ({
|
||||||
|
|
||||||
// Don't save if no changes were made
|
// Don't save if no changes were made
|
||||||
if (JSON.stringify(newAutomation) === JSON.stringify(automation)) {
|
if (JSON.stringify(newAutomation) === JSON.stringify(automation)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return newAutomation
|
||||||
|
},
|
||||||
|
updateBlockInputs: async (block, data) => {
|
||||||
|
const newAutomation = await store.actions.processBlockInputs(block, data)
|
||||||
|
if (newAutomation === false) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await store.actions.save(newAutomation)
|
await store.actions.save(newAutomation)
|
||||||
|
|
|
@ -100,7 +100,10 @@ export function getError(err: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function guardAttachment(attachmentObject: any) {
|
export function guardAttachment(attachmentObject: any) {
|
||||||
if (!("url" in attachmentObject) || !("filename" in attachmentObject)) {
|
if (
|
||||||
|
attachmentObject &&
|
||||||
|
(!("url" in attachmentObject) || !("filename" in attachmentObject))
|
||||||
|
) {
|
||||||
const providedKeys = Object.keys(attachmentObject).join(", ")
|
const providedKeys = Object.keys(attachmentObject).join(", ")
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Attachments must have both "url" and "filename" keys. You have provided: ${providedKeys}`
|
`Attachments must have both "url" and "filename" keys. You have provided: ${providedKeys}`
|
||||||
|
|
|
@ -29,6 +29,17 @@ export const definition: AutomationStepSchema = {
|
||||||
meta: {
|
meta: {
|
||||||
type: AutomationIOType.OBJECT,
|
type: AutomationIOType.OBJECT,
|
||||||
title: "Field settings",
|
title: "Field settings",
|
||||||
|
// DEAN - REVIEW THIS - add in some record of these types
|
||||||
|
|
||||||
|
// properties: {
|
||||||
|
// fields: {
|
||||||
|
// properties: {
|
||||||
|
// useAttachmentBinding: {
|
||||||
|
// type: AutomationIOType.BOOLEAN,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
},
|
},
|
||||||
row: {
|
row: {
|
||||||
type: AutomationIOType.OBJECT,
|
type: AutomationIOType.OBJECT,
|
||||||
|
@ -83,37 +94,53 @@ export async function run({ inputs, appId, emitter }: AutomationStepInput) {
|
||||||
const tableId = inputs.row.tableId
|
const tableId = inputs.row.tableId
|
||||||
|
|
||||||
// Base update
|
// Base update
|
||||||
let rowUpdate: Record<string, any> = {
|
let rowUpdate: Record<string, any>
|
||||||
tableId,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Column checking - explicit clearing of empty fields
|
// Legacy - find any empty values in the row that need to be cleared
|
||||||
if (inputs?.meta?.columns) {
|
const legacyUpdated = Object.keys(inputs.row || {}).reduce(
|
||||||
rowUpdate = inputs?.meta?.columns.reduce(
|
(acc: Record<string, any>, key: string) => {
|
||||||
(acc: Record<string, any>, key: string) => {
|
const isEmpty = inputs.row[key] == null || inputs.row[key]?.length === 0
|
||||||
acc[key] =
|
const fieldConfig = inputs.meta?.fields?.[key]
|
||||||
!inputs.row[key] || inputs.row[key]?.length === 0
|
|
||||||
? null
|
if (isEmpty) {
|
||||||
: inputs.row[key]
|
if (
|
||||||
return acc
|
inputs.meta?.fields.hasOwnProperty(key) &&
|
||||||
},
|
fieldConfig?.clearRelationships === true
|
||||||
{}
|
) {
|
||||||
)
|
// Explicitly clear the field on update
|
||||||
} else {
|
acc[key] = []
|
||||||
// Legacy - clear any empty string column values so that they aren't updated
|
}
|
||||||
rowUpdate = {
|
} else {
|
||||||
...inputs.row,
|
// Keep non-empty values
|
||||||
}
|
acc[key] = inputs.row[key]
|
||||||
for (let propKey of Object.keys(rowUpdate)) {
|
|
||||||
const clearRelationships =
|
|
||||||
inputs.meta?.fields?.[propKey]?.clearRelationships
|
|
||||||
if (
|
|
||||||
(rowUpdate[propKey] == null || rowUpdate[propKey]?.length === 0) &&
|
|
||||||
!clearRelationships
|
|
||||||
) {
|
|
||||||
delete rowUpdate[propKey]
|
|
||||||
}
|
}
|
||||||
}
|
return acc
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// The source of truth for inclusion in the update is: inputs.meta?.fields
|
||||||
|
const parsedUpdate = Object.keys(inputs.meta?.fields || {}).reduce(
|
||||||
|
(acc: Record<string, any>, key: string) => {
|
||||||
|
const fieldConfig = inputs.meta?.fields?.[key]
|
||||||
|
// Ignore legacy config.
|
||||||
|
if (fieldConfig.hasOwnProperty("clearRelationships")) {
|
||||||
|
return acc
|
||||||
|
}
|
||||||
|
acc[key] =
|
||||||
|
inputs.row.hasOwnProperty(key) &&
|
||||||
|
(inputs.row[key] == null || inputs.row[key]?.length === 0)
|
||||||
|
? undefined
|
||||||
|
: inputs.row[key]
|
||||||
|
return acc
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
|
||||||
|
rowUpdate = {
|
||||||
|
tableId,
|
||||||
|
...parsedUpdate,
|
||||||
|
...legacyUpdated,
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
Loading…
Reference in New Issue