Merge pull request #15147 from Budibase/budi-8909-builder-crashes-when-incomplete-filters-are-saved

Disable filter value binding input when no column is selected in filter screen
This commit is contained in:
Andrew Thompson 2024-12-10 15:44:08 +00:00 committed by GitHub
commit adb71a9a8f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 43 additions and 15 deletions

View File

@ -19,7 +19,7 @@
import FlowItemHeader from "./FlowItemHeader.svelte" import FlowItemHeader from "./FlowItemHeader.svelte"
import FlowItemActions from "./FlowItemActions.svelte" import FlowItemActions from "./FlowItemActions.svelte"
import { automationStore, selectedAutomation } from "stores/builder" import { automationStore, selectedAutomation } from "stores/builder"
import { QueryUtils } from "@budibase/frontend-core" import { QueryUtils, Utils } from "@budibase/frontend-core"
import { cloneDeep } from "lodash/fp" import { cloneDeep } from "lodash/fp"
import { createEventDispatcher, getContext } from "svelte" import { createEventDispatcher, getContext } from "svelte"
import DragZone from "./DragZone.svelte" import DragZone from "./DragZone.svelte"
@ -36,13 +36,11 @@
const view = getContext("draggableView") const view = getContext("draggableView")
let drawer let drawer
let condition
let open = true let open = true
let confirmDeleteModal let confirmDeleteModal
$: branch = step.inputs?.branches?.[branchIdx] $: branch = step.inputs?.branches?.[branchIdx]
$: editableConditionUI = cloneDeep(branch.conditionUI || {}) $: editableConditionUI = branch.conditionUI || {}
$: condition = QueryUtils.buildQuery(editableConditionUI)
// Parse all the bindings into fields for the condition builder // Parse all the bindings into fields for the condition builder
$: schemaFields = bindings.map(binding => { $: schemaFields = bindings.map(binding => {
@ -80,9 +78,10 @@
slot="buttons" slot="buttons"
on:click={() => { on:click={() => {
drawer.hide() drawer.hide()
const updatedConditionsUI = Utils.parseFilter(editableConditionUI)
dispatch("change", { dispatch("change", {
conditionUI: editableConditionUI, conditionUI: updatedConditionsUI,
condition, condition: QueryUtils.buildQuery(updatedConditionsUI),
}) })
}} }}
> >

View File

@ -594,10 +594,11 @@
} }
function saveFilters(key) { function saveFilters(key) {
const query = QueryUtils.buildQuery(tempFilters) const update = Utils.parseFilter(tempFilters)
const query = QueryUtils.buildQuery(update)
onChange({ onChange({
[key]: query, [key]: query,
[`${key}-def`]: tempFilters, // need to store the builder definition in the automation [`${key}-def`]: update, // need to store the builder definition in the automation
}) })
drawer.hide() drawer.hide()

View File

@ -4,7 +4,7 @@
import FilterBuilder from "components/design/settings/controls/FilterEditor/FilterBuilder.svelte" import FilterBuilder from "components/design/settings/controls/FilterEditor/FilterBuilder.svelte"
import { getUserBindings } from "dataBinding" import { getUserBindings } from "dataBinding"
import { makePropSafe } from "@budibase/string-templates" import { makePropSafe } from "@budibase/string-templates"
import { search } from "@budibase/frontend-core" import { search, Utils } from "@budibase/frontend-core"
import { tables } from "stores/builder" import { tables } from "stores/builder"
import DetailPopover from "components/common/DetailPopover.svelte" import DetailPopover from "components/common/DetailPopover.svelte"
@ -73,7 +73,7 @@
cta cta
slot="buttons" slot="buttons"
on:click={() => { on:click={() => {
dispatch("change", localFilters) dispatch("change", Utils.parseFilter(localFilters))
popover.hide() popover.hide()
}} }}
> >

View File

@ -10,7 +10,7 @@
import { getDatasourceForProvider, getSchemaForDatasource } from "dataBinding" import { getDatasourceForProvider, getSchemaForDatasource } from "dataBinding"
import FilterBuilder from "./FilterBuilder.svelte" import FilterBuilder from "./FilterBuilder.svelte"
import { tables, selectedScreen } from "stores/builder" import { tables, selectedScreen } from "stores/builder"
import { search } from "@budibase/frontend-core" import { search, Utils } from "@budibase/frontend-core"
import { utils } from "@budibase/shared-core" import { utils } from "@budibase/shared-core"
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
@ -33,7 +33,8 @@
$: text = getText(value) $: text = getText(value)
async function saveFilter() { async function saveFilter() {
dispatch("change", localFilters) const update = Utils.parseFilter(localFilters)
dispatch("change", update)
notifications.success("Filters saved") notifications.success("Filters saved")
drawer.hide() drawer.hide()
} }

View File

@ -6,6 +6,7 @@
QueryUtils, QueryUtils,
Constants, Constants,
CoreFilterBuilder, CoreFilterBuilder,
Utils,
} from "@budibase/frontend-core" } from "@budibase/frontend-core"
import Button from "../Button.svelte" import Button from "../Button.svelte"
@ -95,7 +96,7 @@
} }
const updateQuery = () => { const updateQuery = () => {
filters = editableFilters filters = Utils.parseFilter(editableFilters)
} }
onDestroy(() => { onDestroy(() => {

View File

@ -407,6 +407,7 @@
/> />
<FilterField <FilterField
placeholder="Value" placeholder="Value"
disabled={!filter.field && builderType === "filter"}
drawerTitle={builderType === "condition" drawerTitle={builderType === "condition"
? "Edit binding" ? "Edit binding"
: null} : null}

View File

@ -1,5 +1,6 @@
import { makePropSafe as safe } from "@budibase/string-templates" import { makePropSafe as safe } from "@budibase/string-templates"
import { Helpers } from "@budibase/bbui" import { Helpers } from "@budibase/bbui"
import { cloneDeep } from "lodash"
export const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)) export const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
@ -351,3 +352,27 @@ export const buildMultiStepFormBlockDefaultProps = props => {
title, title,
} }
} }
/**
* Parse out empty or invalid UI filters and clear empty groups
* @param {Object} filter UI filter
* @returns {Object} parsed filter
*/
export function parseFilter(filter) {
if (!filter?.groups) {
return filter
}
const update = cloneDeep(filter)
update.groups = update.groups
.map(group => {
group.filters = group.filters.filter(filter => {
return filter.field && filter.operator
})
return group.filters.length ? group : null
})
.filter(group => group)
return update
}

View File

@ -298,7 +298,8 @@ export class ColumnSplitter {
function buildCondition(filter: undefined): undefined function buildCondition(filter: undefined): undefined
function buildCondition(filter: SearchFilter): SearchFilters function buildCondition(filter: SearchFilter): SearchFilters
function buildCondition(filter?: SearchFilter): SearchFilters | undefined { function buildCondition(filter?: SearchFilter): SearchFilters | undefined {
if (!filter) { // Ignore empty or invalid filters
if (!filter || !filter?.operator || !filter?.field) {
return return
} }
@ -475,7 +476,6 @@ export function buildQuery(
if (group.logicalOperator) { if (group.logicalOperator) {
operator = logicalOperatorFromUI(group.logicalOperator) operator = logicalOperatorFromUI(group.logicalOperator)
} }
return { return {
[operator]: { conditions: filters.map(buildCondition).filter(f => f) }, [operator]: { conditions: filters.map(buildCondition).filter(f => f) },
} }