diff --git a/lerna.json b/lerna.json index db0a1d59fa..9488ebe3a0 100644 --- a/lerna.json +++ b/lerna.json @@ -22,4 +22,4 @@ "loadEnvFiles": false } } -} \ No newline at end of file +} diff --git a/packages/backend-core/src/sql/sql.ts b/packages/backend-core/src/sql/sql.ts index dd08730f7c..4f1a9fc05e 100644 --- a/packages/backend-core/src/sql/sql.ts +++ b/packages/backend-core/src/sql/sql.ts @@ -231,8 +231,7 @@ class InternalBuilder { } const contains = (mode: object, any: boolean = false) => { - const fnc = allOr ? "orWhere" : "where" - const rawFnc = `${fnc}Raw` + const rawFnc = allOr ? "orWhereRaw" : "whereRaw" const not = mode === filters?.notContains ? "NOT " : "" function stringifyArray(value: Array, quoteStyle = '"'): string { for (let i in value) { @@ -245,24 +244,24 @@ class InternalBuilder { if (this.client === SqlClient.POSTGRES) { iterate(mode, (key: string, value: Array) => { const wrap = any ? "" : "'" - const containsOp = any ? "\\?| array" : "@>" + const op = any ? "\\?| array" : "@>" const fieldNames = key.split(/\./g) - const tableName = fieldNames[0] - const columnName = fieldNames[1] - // @ts-ignore + const table = fieldNames[0] + const col = fieldNames[1] query = query[rawFnc]( - `${not}"${tableName}"."${columnName}"::jsonb ${containsOp} ${wrap}${stringifyArray( + `${not}COALESCE("${table}"."${col}"::jsonb ${op} ${wrap}${stringifyArray( value, any ? "'" : '"' - )}${wrap}` + )}${wrap}, FALSE)` ) }) } else if (this.client === SqlClient.MY_SQL) { const jsonFnc = any ? "JSON_OVERLAPS" : "JSON_CONTAINS" iterate(mode, (key: string, value: Array) => { - // @ts-ignore query = query[rawFnc]( - `${not}${jsonFnc}(${key}, '${stringifyArray(value)}')` + `${not}COALESCE(${jsonFnc}(${key}, '${stringifyArray( + value + )}'), FALSE)` ) }) } else { @@ -277,7 +276,7 @@ class InternalBuilder { } statement += (statement ? andOr : "") + - `LOWER(${likeKey(this.client, key)}) LIKE ?` + `COALESCE(LOWER(${likeKey(this.client, key)}), '') LIKE ?` } if (statement === "") { @@ -342,14 +341,34 @@ class InternalBuilder { } if (filters.equal) { iterate(filters.equal, (key, value) => { - const fnc = allOr ? "orWhere" : "where" - query = query[fnc]({ [key]: value }) + const fnc = allOr ? "orWhereRaw" : "whereRaw" + if (this.client === SqlClient.MS_SQL) { + query = query[fnc]( + `CASE WHEN ${likeKey(this.client, key)} = ? THEN 1 ELSE 0 END = 1`, + [value] + ) + } else { + query = query[fnc]( + `COALESCE(${likeKey(this.client, key)} = ?, FALSE)`, + [value] + ) + } }) } if (filters.notEqual) { iterate(filters.notEqual, (key, value) => { - const fnc = allOr ? "orWhereNot" : "whereNot" - query = query[fnc]({ [key]: value }) + const fnc = allOr ? "orWhereRaw" : "whereRaw" + if (this.client === SqlClient.MS_SQL) { + query = query[fnc]( + `CASE WHEN ${likeKey(this.client, key)} = ? THEN 1 ELSE 0 END = 0`, + [value] + ) + } else { + query = query[fnc]( + `COALESCE(${likeKey(this.client, key)} != ?, TRUE)`, + [value] + ) + } }) } if (filters.empty) { diff --git a/packages/bbui/src/Form/Core/Picker.svelte b/packages/bbui/src/Form/Core/Picker.svelte index c6d4ed3353..88866196a7 100644 --- a/packages/bbui/src/Form/Core/Picker.svelte +++ b/packages/bbui/src/Form/Core/Picker.svelte @@ -157,7 +157,7 @@ useAnchorWidth={!autoWidth} maxWidth={autoWidth ? 400 : null} customHeight={customPopoverHeight} - maxHeight={240} + maxHeight={360} >
- import { flip } from "svelte/animate" - import { dndzone } from "svelte-dnd-action" - import Icon from "../Icon/Icon.svelte" - import Popover from "../Popover/Popover.svelte" - import { onMount } from "svelte" - - const flipDurationMs = 150 - - export let constraints - export let optionColors = {} - let options = [] - - let colorPopovers = [] - let anchors = [] - - let colorsArray = [ - "hsla(0, 90%, 75%, 0.3)", - "hsla(50, 80%, 75%, 0.3)", - "hsla(120, 90%, 75%, 0.3)", - "hsla(200, 90%, 75%, 0.3)", - "hsla(240, 90%, 75%, 0.3)", - "hsla(320, 90%, 75%, 0.3)", - ] - const removeInput = idx => { - delete optionColors[options[idx].name] - constraints.inclusion = constraints.inclusion.filter((e, i) => i !== idx) - options = options.filter((e, i) => i !== idx) - colorPopovers.pop(undefined) - anchors.pop(undefined) - } - - const addNewInput = () => { - options = [ - ...options, - { name: `Option ${constraints.inclusion.length + 1}`, id: Math.random() }, - ] - constraints.inclusion = [ - ...constraints.inclusion, - `Option ${constraints.inclusion.length + 1}`, - ] - - colorPopovers.push(undefined) - anchors.push(undefined) - } - - const handleDndConsider = e => { - options = e.detail.items - } - const handleDndFinalize = e => { - options = e.detail.items - constraints.inclusion = options.map(option => option.name) - } - - const handleColorChange = (optionName, color, idx) => { - optionColors[optionName] = color - colorPopovers[idx].hide() - } - - const handleNameChange = (optionName, idx, value) => { - constraints.inclusion[idx] = value - options[idx].name = value - optionColors[value] = optionColors[optionName] - delete optionColors[optionName] - } - - const openColorPickerPopover = (optionIdx, target) => { - colorPopovers[optionIdx].show() - anchors[optionIdx] = target - } - - onMount(() => { - // Initialize anchor arrays on mount, assuming 'options' is already populated - colorPopovers = constraints.inclusion.map(() => undefined) - anchors = constraints.inclusion.map(() => undefined) - - options = constraints.inclusion.map(value => ({ - name: value, - id: Math.random(), - })) - }) - - - - -
-
- {#each options as option, idx (option.id)} -
-
- -
-
-
openColorPickerPopover(idx, e.target)} - > - -
- {#each colorsArray as color} -
handleColorChange(option.name, color, idx)} - style="--color:{color};" - class="circle circle-hover" - /> - {/each} -
- -
-
-
- handleNameChange(option.name, idx, e.target.value)} - value={option.name} - placeholder="Option name" - /> -
-
- -
-
- {/each} -
-
- -
Add option
-
-
- - diff --git a/packages/bbui/src/index.js b/packages/bbui/src/index.js index e746e3a72b..7a4ff3f081 100644 --- a/packages/bbui/src/index.js +++ b/packages/bbui/src/index.js @@ -89,7 +89,6 @@ export { default as ListItem } from "./List/ListItem.svelte" export { default as IconSideNav } from "./IconSideNav/IconSideNav.svelte" export { default as IconSideNavItem } from "./IconSideNav/IconSideNavItem.svelte" export { default as Accordion } from "./Accordion/Accordion.svelte" -export { default as OptionSelectDnD } from "./OptionSelectDnD/OptionSelectDnD.svelte" export { default as AbsTooltip } from "./Tooltip/AbsTooltip.svelte" export { TooltipPosition, TooltipType } from "./Tooltip/AbsTooltip.svelte" diff --git a/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte b/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte index 087d0b94fc..0a27360347 100644 --- a/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte +++ b/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte @@ -99,7 +99,7 @@ on:change={e => onChange(e, field)} useLabel={false} /> -{:else if schema.type === "bb_reference"} +{:else if schema.type === "bb_reference" || schema.type === "bb_reference_single"} {:else if type === "attachment"} {:else if type === "attachment_single"} source._id === table?.sourceId @@ -559,9 +561,10 @@ bind:value={editableColumn.constraints.length.maximum} /> {:else if editableColumn.type === FieldType.OPTIONS} - {:else if editableColumn.type === FieldType.LONGFORM}
@@ -582,9 +585,10 @@ />
{:else if editableColumn.type === FieldType.ARRAY} - {:else if editableColumn.type === DATE_TYPE && !editableColumn.autocolumn}
diff --git a/packages/builder/src/components/backend/DataTable/modals/OptionsEditor.svelte b/packages/builder/src/components/backend/DataTable/modals/OptionsEditor.svelte new file mode 100644 index 0000000000..8e35e38ae9 --- /dev/null +++ b/packages/builder/src/components/backend/DataTable/modals/OptionsEditor.svelte @@ -0,0 +1,252 @@ + + + + +
+
options.set(e.detail.items)} + on:finalize={e => options.set(e.detail.items)} + > + {#each $enrichedOptions as option (option.id)} +
+
+ +
+
openColorPicker(option.id)} + > +
+ +
+ {#each OptionColours as colorOption} +
handleColorChange(option.id, colorOption)} + style="--color:{colorOption};" + class="circle" + class:selected={colorOption === option.color} + /> + {/each} +
+ +
+
+ handleNameChange(option.id, e.target.value)} + /> + removeInput(option.id)} + /> +
+ {/each} +
+
+ +
Add option
+
+
+ + diff --git a/packages/builder/src/components/common/LinkedRowSelector.svelte b/packages/builder/src/components/common/LinkedRowSelector.svelte index b042d3a9ae..8db4a7dc03 100644 --- a/packages/builder/src/components/common/LinkedRowSelector.svelte +++ b/packages/builder/src/components/common/LinkedRowSelector.svelte @@ -43,7 +43,7 @@ {linkedTable.name} table. -{:else if schema.relationshipType === "one-to-many"} +{:else if schema.relationshipType === "one-to-many" || schema.type === "bb_reference_single"}