From 6cb40c116b18c0e90f307a8b4d128dc52351f0ac Mon Sep 17 00:00:00 2001 From: Dean Date: Fri, 4 Apr 2025 11:26:56 +0100 Subject: [PATCH 01/48] Merge commit --- .../design/settings/componentSettings.js | 4 + .../new/_components/componentStructure.json | 1 + packages/client/manifest.json | 89 ++++++++++++++++--- packages/client/src/components/app/index.js | 1 + packages/client/src/index.ts | 2 + 5 files changed, 86 insertions(+), 11 deletions(-) diff --git a/packages/builder/src/components/design/settings/componentSettings.js b/packages/builder/src/components/design/settings/componentSettings.js index 1cf4b0211c..027c280704 100644 --- a/packages/builder/src/components/design/settings/componentSettings.js +++ b/packages/builder/src/components/design/settings/componentSettings.js @@ -25,6 +25,7 @@ import BasicColumnEditor from "./controls/ColumnEditor/BasicColumnEditor.svelte" import GridColumnEditor from "./controls/GridColumnConfiguration/GridColumnConfiguration.svelte" import BarButtonList from "./controls/BarButtonList.svelte" import FieldConfiguration from "./controls/FieldConfiguration/FieldConfiguration.svelte" +import FilterConfiguration from "./controls/FilterConfiguration/FilterConfiguration.svelte" import ButtonConfiguration from "./controls/ButtonConfiguration/ButtonConfiguration.svelte" import RelationshipFilterEditor from "./controls/RelationshipFilterEditor.svelte" import FormStepConfiguration from "./controls/FormStepConfiguration.svelte" @@ -32,6 +33,7 @@ import FormStepControls from "./controls/FormStepControls.svelte" import PaywalledSetting from "./controls/PaywalledSetting.svelte" import TableConditionEditor from "./controls/TableConditionEditor.svelte" import MultilineDrawerBindableInput from "@/components/common/MultilineDrawerBindableInput.svelte" +import EmbeddedDatasourceSelect from "./controls/EmbeddedDatasourceSelect.svelte" const componentMap = { text: DrawerBindableInput, @@ -41,6 +43,7 @@ const componentMap = { radio: RadioGroup, dataSource: DataSourceSelect, "dataSource/s3": S3DataSourceSelect, + "dataSource/embedded": EmbeddedDatasourceSelect, dataProvider: DataProviderSelect, boolean: Checkbox, number: Stepper, @@ -58,6 +61,7 @@ const componentMap = { "filter/relationship": RelationshipFilterEditor, url: URLSelect, fieldConfiguration: FieldConfiguration, + filterConfiguration: FilterConfiguration, buttonConfiguration: ButtonConfiguration, stepConfiguration: FormStepConfiguration, formStepControls: FormStepControls, diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/componentStructure.json b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/componentStructure.json index d809095dc0..62adafff68 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/componentStructure.json +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/componentStructure.json @@ -25,6 +25,7 @@ "gridblock", "spreadsheet", "dynamicfilter", + "filter", "daterangepicker" ] }, diff --git a/packages/client/manifest.json b/packages/client/manifest.json index fc0d5a4614..d11406866e 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -5526,6 +5526,58 @@ } ] }, + + "filter": { + "name": "Filter", + "icon": "Filter", + "size": { + "width": 100, + "height": 35 + }, + "settings": [ + { + "type": "dataSource/embedded", + "label": "Target component", + "required": true, + "key": "targetComponent", + "wide": true + }, + { + "label": "", + "type": "filterConfiguration", + "key": "filterConfig", + "nested": true, + "dependsOn": "targetComponent", + "resetOn": "targetComponent" + }, + { + "type": "boolean", + "label": "Persist filter values when a user returns to the screen", + "key": "persistFilters", + "wide": true, + "defaultValue": false + }, + { + "type": "boolean", + "label": "Add a 'Clear filters' button so that user can clear multiple filters at once", + "key": "showClear", + "defaultValue": false, + "wide": true + } + ] + }, + "filterconfig": { + "name": "", + "icon": "", + "editable": true, + "settings": [ + { + "type": "plainText", + "label": "Label", + "key": "label" + } + ] + }, "dynamicfilter": { "name": "Dynamic Filter", "icon": "Filter", @@ -7584,15 +7636,18 @@ "name": "Table", "icon": "Table", "styles": ["size"], - "cssVariables": [{ - "label": "Header color", - "variable": "--custom-header-cell-background", - "type": "color" - }, { - "label": "Stripe color", - "variable": "--custom-stripe-cell-background", - "type": "color" - }], + "cssVariables": [ + { + "label": "Header color", + "variable": "--custom-header-cell-background", + "type": "color" + }, + { + "label": "Stripe color", + "variable": "--custom-stripe-cell-background", + "type": "color" + } + ], "size": { "width": 600, "height": 400 @@ -7616,7 +7671,8 @@ "setting": "table.type", "value": "custom", "invert": true - } + }, + "resetOn": "table" }, { "type": "field/sortable", @@ -7766,9 +7822,20 @@ "type": "array" } ] + }, + { + "type": "static", + "scope": "local", + "values": [ + { + "label": "Datasources", + "key": "datasources", + "type": "array" + } + ] } ], - "actions": ["RefreshDatasource"] + "actions": ["RefreshDatasource", "AddDataProviderQueryExtension"] }, "bbreferencefield": { "devComment": "As bb reference is only used for user subtype for now, we are using user for icon and labels", diff --git a/packages/client/src/components/app/index.js b/packages/client/src/components/app/index.js index 78a3657024..17b97eda5e 100644 --- a/packages/client/src/components/app/index.js +++ b/packages/client/src/components/app/index.js @@ -36,6 +36,7 @@ export { default as sidepanel } from "./SidePanel.svelte" export { default as modal } from "./Modal.svelte" export { default as gridblock } from "./GridBlock.svelte" export { default as textv2 } from "./Text.svelte" +export { default as filter } from "./filter/Filter.svelte" export * from "./charts" export * from "./forms" export * from "./blocks" diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 43c5d71d68..aa32c64778 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -83,7 +83,9 @@ export interface SDK { ActionTypes: typeof ActionTypes fetchDatasourceSchema: any fetchDatasourceDefinition: (datasource: DataFetchDatasource) => Promise + enrichButtonActions: any generateGoldenSample: any + createContextStore: any builderStore: typeof builderStore authStore: typeof authStore notificationStore: typeof notificationStore From 0277462a6939935ef317a3e208c201719e6b88ba Mon Sep 17 00:00:00 2001 From: Dean Date: Fri, 11 Apr 2025 09:02:08 +0100 Subject: [PATCH 02/48] Merge --- packages/types/src/ui/components/index.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/types/src/ui/components/index.ts b/packages/types/src/ui/components/index.ts index b6bbdd6ed7..d9da7e079c 100644 --- a/packages/types/src/ui/components/index.ts +++ b/packages/types/src/ui/components/index.ts @@ -1,3 +1,5 @@ +import { FieldType } from "@budibase/types" + export * from "./codeEditor" export * from "./errors" @@ -82,3 +84,11 @@ export const enum ComponentContextScopes { Local = "local", Global = "global", } + +export type FilterConfig = { + active: boolean + field: string + label?: string + _id?: string + columnType?: FieldType +} From 0bf033c8f04f92d832ea8d5a62077e0eee7eca6e Mon Sep 17 00:00:00 2001 From: Dean Date: Thu, 17 Apr 2025 09:03:17 +0100 Subject: [PATCH 03/48] packages/builder/src/dataBinding.js --- packages/builder/src/dataBinding.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/dataBinding.js b/packages/builder/src/dataBinding.js index ada1ee274d..e1b79feac4 100644 --- a/packages/builder/src/dataBinding.js +++ b/packages/builder/src/dataBinding.js @@ -1400,7 +1400,7 @@ const bindingReplacement = ( * Extracts a component ID from a handlebars expression setting of * {{ literal [componentId] }} */ -const extractLiteralHandlebarsID = value => { +export const extractLiteralHandlebarsID = value => { if (!value || typeof value !== "string") { return null } From 7884d7d749bfc4395da5e0f50e44091488e38cb0 Mon Sep 17 00:00:00 2001 From: Dean Date: Thu, 17 Apr 2025 17:19:01 +0100 Subject: [PATCH 04/48] Filter component and general filtering updates --- .../Form/Core/DatePicker/DatePicker.svelte | 2 +- .../bbui/src/Form/Core/DateRangePicker.svelte | 72 ++- packages/bbui/src/Form/DateRangePicker.svelte | 6 +- .../DraggableList/DraggableList.svelte | 2 +- .../controls/EmbeddedDatasourceSelect.svelte | 71 +++ .../EditFilterPopover.svelte | 127 +++++ .../FilterConfiguration.svelte | 168 +++++++ .../FilterConfiguration/FilterSetting.svelte | 152 ++++++ .../GridColumnConfiguration.svelte | 2 +- .../GridColumnConfiguration/getColumns.js | 12 +- .../getColumns.test.js | 2 +- .../_components/Component/InfoDisplay.svelte | 4 +- .../builder/src/stores/builder/components.ts | 7 +- .../src/components/app/DataProvider.svelte | 1 + .../src/components/app/GridBlock.svelte | 80 +++- .../src/components/app/filter/Filter.svelte | 437 ++++++++++++++++++ .../components/app/filter/FilterButton.svelte | 257 ++++++++++ .../app/filter/FilterPopover.svelte | 382 +++++++++++++++ .../app/forms/BBReferenceField.svelte | 7 +- .../app/forms/RelationshipField.svelte | 28 +- packages/client/src/constants.ts | 2 + packages/client/src/stores/components.js | 1 + packages/client/src/stores/index.ts | 1 + packages/client/src/stores/uiState.ts | 26 ++ packages/client/src/types/index.ts | 8 + packages/client/src/utils/filtering.ts | 10 + packages/shared-core/src/filters.ts | 12 +- 27 files changed, 1839 insertions(+), 40 deletions(-) create mode 100644 packages/builder/src/components/design/settings/controls/EmbeddedDatasourceSelect.svelte create mode 100644 packages/builder/src/components/design/settings/controls/FilterConfiguration/EditFilterPopover.svelte create mode 100644 packages/builder/src/components/design/settings/controls/FilterConfiguration/FilterConfiguration.svelte create mode 100644 packages/builder/src/components/design/settings/controls/FilterConfiguration/FilterSetting.svelte create mode 100644 packages/client/src/components/app/filter/Filter.svelte create mode 100644 packages/client/src/components/app/filter/FilterButton.svelte create mode 100644 packages/client/src/components/app/filter/FilterPopover.svelte create mode 100644 packages/client/src/stores/uiState.ts create mode 100644 packages/client/src/utils/filtering.ts diff --git a/packages/bbui/src/Form/Core/DatePicker/DatePicker.svelte b/packages/bbui/src/Form/Core/DatePicker/DatePicker.svelte index f5189c9edd..6c8c367b31 100644 --- a/packages/bbui/src/Form/Core/DatePicker/DatePicker.svelte +++ b/packages/bbui/src/Form/Core/DatePicker/DatePicker.svelte @@ -13,7 +13,7 @@ export let readonly = false export let error = null export let enableTime = true - export let value = null + export let value = undefined export let placeholder = null export let timeOnly = false export let ignoreTimezones = false diff --git a/packages/bbui/src/Form/Core/DateRangePicker.svelte b/packages/bbui/src/Form/Core/DateRangePicker.svelte index 72180a98d6..44518ba1af 100644 --- a/packages/bbui/src/Form/Core/DateRangePicker.svelte +++ b/packages/bbui/src/Form/Core/DateRangePicker.svelte @@ -1,24 +1,78 @@ -
(fromDate = e.detail)} - enableTime={false} + value={parsedFrom} + on:change={e => onChangeFrom(e.detail)} + {enableTime} + {timeOnly} + {ignoreTimezones} />
(toDate = e.detail)} - enableTime={false} + value={parsedTo} + on:change={e => onChangeTo(e.detail)} + {enableTime} + {timeOnly} + {ignoreTimezones} />
diff --git a/packages/bbui/src/Form/DateRangePicker.svelte b/packages/bbui/src/Form/DateRangePicker.svelte index 39c2acb96a..12037e0ee4 100644 --- a/packages/bbui/src/Form/DateRangePicker.svelte +++ b/packages/bbui/src/Form/DateRangePicker.svelte @@ -3,7 +3,7 @@ import DateRangePicker from "./Core/DateRangePicker.svelte" import { createEventDispatcher } from "svelte" - export let value = null + export let value = undefined export let label = null export let labelPosition = "above" export let disabled = false @@ -12,6 +12,8 @@ export let helpText = null export let appendTo = undefined export let ignoreTimezones = false + export let enableTime = false + export let timeOnly = false const dispatch = createEventDispatcher() @@ -29,6 +31,8 @@ {value} {appendTo} {ignoreTimezones} + {enableTime} + {timeOnly} on:change={onChange} /> diff --git a/packages/builder/src/components/design/settings/controls/DraggableList/DraggableList.svelte b/packages/builder/src/components/design/settings/controls/DraggableList/DraggableList.svelte index 0ba7de42c2..f1f7005800 100644 --- a/packages/builder/src/components/design/settings/controls/DraggableList/DraggableList.svelte +++ b/packages/builder/src/components/design/settings/controls/DraggableList/DraggableList.svelte @@ -11,7 +11,7 @@ export let listTypeProps = {} export let listItemKey export let draggable = true - export let focus + export let focus = undefined let zoneType = generate() diff --git a/packages/builder/src/components/design/settings/controls/EmbeddedDatasourceSelect.svelte b/packages/builder/src/components/design/settings/controls/EmbeddedDatasourceSelect.svelte new file mode 100644 index 0000000000..1521f99624 --- /dev/null +++ b/packages/builder/src/components/design/settings/controls/EmbeddedDatasourceSelect.svelte @@ -0,0 +1,71 @@ + + + { + if (!editableFilter) return + + const sanitized = sanitizeOperator({ + ...editableFilter, + operator: e.detail, + }) + + editableFilter = { ...(sanitized || editableFilter) } + }} + /> + + + {#if editableFilter?.type && [FieldType.STRING, FieldType.LONGFORM, FieldType.NUMBER, FieldType.BIGINT, FieldType.FORMULA, FieldType.AI].includes(editableFilter.type)} + { + if (!editableFilter) return + editableFilter = sanitizeOperator({ + ...editableFilter, + value: e.detail, + }) + }} + /> + {:else if (editableFilter?.type && editableFilter?.type === FieldType.ARRAY) || (editableFilter.type === FieldType.OPTIONS && editableFilter.operator === ArrayOperator.ONE_OF)} + {@const isMulti = isArrayOperator(editableFilter.operator)} + {@const type = isMulti ? CoreCheckboxGroup : CoreRadioGroup} + {#key type} + { + if (!editableFilter) return + editableFilter = sanitizeOperator({ + ...editableFilter, + value: e.detail, + }) + }} + /> + {/key} + {:else if editableFilter.type === FieldType.OPTIONS} + { + if (!editableFilter) return + editableFilter = sanitizeOperator({ + ...editableFilter, + value: e.detail, + }) + }} + /> + {:else if editableFilter.type === FieldType.DATETIME && editableFilter.operator === "range"} + { + const [from, to] = e.detail + // check both - hard coded for testing + const parsedFrom = Helpers.stringifyDate(from, { + enableTime, + timeOnly, + ignoreTimezones, + }) + + const parsedTo = Helpers.stringifyDate(to, { + enableTime, + timeOnly, + ignoreTimezones, + }) + + if (!editableFilter) return + editableFilter = sanitizeOperator({ + ...editableFilter, + value: { low: parsedFrom, high: parsedTo }, + }) + + // editableFilter = { ...(sanitized ?? editableFilter) } + }} + /> + {:else if editableFilter.type === FieldType.DATETIME} + { + if (!editableFilter) return + editableFilter = sanitizeOperator({ + ...editableFilter, + value: e.detail, + }) + }} + /> + {:else if editableFilter.type === FieldType.BOOLEAN} + + {/if} + + + {/if} + + + + + diff --git a/packages/client/src/components/app/forms/BBReferenceField.svelte b/packages/client/src/components/app/forms/BBReferenceField.svelte index 60932ecf56..7dc220e76a 100644 --- a/packages/client/src/components/app/forms/BBReferenceField.svelte +++ b/packages/client/src/components/app/forms/BBReferenceField.svelte @@ -1,10 +1,12 @@ - - diff --git a/packages/builder/src/stores/builder/components.ts b/packages/builder/src/stores/builder/components.ts index 0c22e99545..ed263b5bd6 100644 --- a/packages/builder/src/stores/builder/components.ts +++ b/packages/builder/src/stores/builder/components.ts @@ -212,10 +212,8 @@ export class ComponentStore extends BudiStore { } const def = this.getDefinition(enrichedComponent?._component) - const filterableTypes = def?.settings?.filter( - setting => - setting?.type?.startsWith("filter") && - setting?.type !== "filterConfiguration" + const filterableTypes = def?.settings?.filter(setting => + ["filter", "filter/relationship"].includes(setting?.type) ) for (let setting of filterableTypes || []) { const isLegacy = Array.isArray(enrichedComponent[setting.key]) diff --git a/packages/client/manifest.json b/packages/client/manifest.json index f8c098ec63..ae7692d266 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -5204,7 +5204,7 @@ "icon": "Data", "illegalChildren": ["section"], "hasChildren": true, - "actions": ["RefreshDatasource"], + "actions": ["RefreshDatasource", "AddDataProviderQueryExtension"], "size": { "width": 500, "height": 200 @@ -5534,9 +5534,10 @@ "width": 100, "height": 35 }, + "new": true, "settings": [ { - "type": "dataSource/embedded", + "type": "dataSource/filterable", "label": "Target component", "required": true, "key": "targetComponent", @@ -5552,17 +5553,15 @@ }, { "type": "boolean", - "label": "Persist filter values when a user returns to the screen", + "label": "Persist filters", "key": "persistFilters", - "wide": true, "defaultValue": false }, { "type": "boolean", - "label": "Add a 'Clear filters' button so that user can clear multiple filters at once", + "label": "Clear filters", "key": "showClear", - "defaultValue": false, - "wide": true + "defaultValue": false } ] }, @@ -7873,20 +7872,9 @@ "type": "array" } ] - }, - { - "type": "static", - "scope": "local", - "values": [ - { - "label": "Datasources", - "key": "datasources", - "type": "array" - } - ] } ], - "actions": ["RefreshDatasource", "AddDataProviderQueryExtension"] + "actions": ["RefreshDatasource", "AddDataProviderFilterExtension"] }, "bbreferencefield": { "devComment": "As bb reference is only used for user subtype for now, we are using user for icon and labels", diff --git a/packages/client/src/components/app/GridBlock.svelte b/packages/client/src/components/app/GridBlock.svelte index c2fa344a14..283bdd482c 100644 --- a/packages/client/src/components/app/GridBlock.svelte +++ b/packages/client/src/components/app/GridBlock.svelte @@ -109,27 +109,16 @@ } const extendFilter = (initialFilter, extensions) => { - if (!extensions || !Object.keys(extensions).length) { + if (!Object.keys(extensions || {}).length) { return initialFilter } - - // Base filter - let extended = { - groups: initialFilter ? [initialFilter] : [], + return { + groups: (initialFilter ? [initialFilter] : []).concat( + Object.values(extensions) + ), logicalOperator: UILogicalOperator.ALL, onEmptyFilter: EmptyFilterOption.RETURN_NONE, } - - // Process and aggregate all filters. - let groups = Object.entries(extensions).map(([_, entry]) => { - // Assuming there should only ever be 1 - return entry - }) - - // Combine all groups into the base - extended.groups = [...extended.groups, ...groups] - - return extended } // Provide additional data context for live binding eval diff --git a/packages/client/src/components/app/filter/FilterButton.svelte b/packages/client/src/components/app/filter/FilterButton.svelte index 5536b9f9d0..02789af923 100644 --- a/packages/client/src/components/app/filter/FilterButton.svelte +++ b/packages/client/src/components/app/filter/FilterButton.svelte @@ -229,7 +229,7 @@ } .spectrum-Button-label .display { - color: rgba(179, 222, 254, 1); + color: var(--spectrum-global-color-blue-600); } .spectrum-Button--secondary.new-styles { diff --git a/packages/client/src/stores/components.js b/packages/client/src/stores/components.js index 239336ff28..024e7b4edc 100644 --- a/packages/client/src/stores/components.js +++ b/packages/client/src/stores/components.js @@ -44,7 +44,6 @@ const createComponentStore = () => { selectedComponentPath: selectedPath?.map(component => component._id), mountedComponentCount: Object.keys($store.mountedComponents).length, screenslotInstance: $store.mountedComponents[ScreenslotID], - mounted: $store.mountedComponents, } } ) From 0868a4a683f2e0c524c33354254d68243fb0e0d3 Mon Sep 17 00:00:00 2001 From: Dean Date: Wed, 23 Apr 2025 09:56:19 +0100 Subject: [PATCH 08/48] Fix for isIn operator when processing number querys --- packages/client/src/components/app/filter/FilterPopover.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/components/app/filter/FilterPopover.svelte b/packages/client/src/components/app/filter/FilterPopover.svelte index d0d7eac8a6..056bf98c1f 100644 --- a/packages/client/src/components/app/filter/FilterPopover.svelte +++ b/packages/client/src/components/app/filter/FilterPopover.svelte @@ -94,7 +94,7 @@ if ( isOperatorArray && typeof filter.value === "string" && - filter.type !== FieldType.STRING + ![FieldType.STRING, FieldType.NUMBER].includes(filter.type!) ) { clone.value = [filter.value] } else if (isOperatorArray && !filter?.value?.length) { From 07111fd2fbd9bdd7f4c3d8c56d1374a3b0d2c1a3 Mon Sep 17 00:00:00 2001 From: Dean Date: Wed, 23 Apr 2025 10:59:19 +0100 Subject: [PATCH 09/48] Enable filtering by relationship field in the updated filter component --- .../FilterConfiguration.svelte | 58 +++++++++++-------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/packages/builder/src/components/design/settings/controls/FilterConfiguration/FilterConfiguration.svelte b/packages/builder/src/components/design/settings/controls/FilterConfiguration/FilterConfiguration.svelte index 2df059b527..df2a360a7c 100644 --- a/packages/builder/src/components/design/settings/controls/FilterConfiguration/FilterConfiguration.svelte +++ b/packages/builder/src/components/design/settings/controls/FilterConfiguration/FilterConfiguration.svelte @@ -1,5 +1,5 @@
From 03779660555c3ebaa8d834f85d849ec714745e3d Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 30 Apr 2025 15:58:01 +0200 Subject: [PATCH 10/48] Type screens --- packages/bbui/src/Layout/Layout.svelte | 2 +- .../[screenId]/_components/ScreenList/index.svelte | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/bbui/src/Layout/Layout.svelte b/packages/bbui/src/Layout/Layout.svelte index b3de7b49db..61d33e3ab2 100644 --- a/packages/bbui/src/Layout/Layout.svelte +++ b/packages/bbui/src/Layout/Layout.svelte @@ -1,7 +1,7 @@ @@ -62,7 +63,6 @@
+ getRelationshipSchemaAdditions: (schema: Record) => Promise enrichButtonActions: any generateGoldenSample: any createContextStore: any diff --git a/packages/client/src/sdk.js b/packages/client/src/sdk.js index 15b88d3123..e9ef6a8505 100644 --- a/packages/client/src/sdk.js +++ b/packages/client/src/sdk.js @@ -29,6 +29,7 @@ import { ActionTypes } from "./constants" import { fetchDatasourceSchema, fetchDatasourceDefinition, + getRelationshipSchemaAdditions, } from "./utils/schema" import { getAPIKey } from "./utils/api.js" import { enrichButtonActions } from "./utils/buttonActions.js" @@ -71,6 +72,7 @@ export default { getAction, fetchDatasourceSchema, fetchDatasourceDefinition, + getRelationshipSchemaAdditions, fetchData, QueryUtils, ContextScopes: Constants.ContextScopes, diff --git a/packages/client/src/utils/schema.ts b/packages/client/src/utils/schema.ts index 5d68ddc5cc..3e637b4579 100644 --- a/packages/client/src/utils/schema.ts +++ b/packages/client/src/utils/schema.ts @@ -121,6 +121,7 @@ export const getRelationshipSchemaAdditions = async ( relationshipAdditions[`${fieldKey}.${linkKey}`] = { type: linkSchema[linkKey].type, externalType: linkSchema[linkKey].externalType, + constraints: linkSchema[linkKey].constraints, } }) } diff --git a/packages/shared-core/src/filters.ts b/packages/shared-core/src/filters.ts index 3630ab90ce..e4ad413aed 100644 --- a/packages/shared-core/src/filters.ts +++ b/packages/shared-core/src/filters.ts @@ -103,8 +103,6 @@ export const getValidOperatorsForType = ( ops = [Op.Equals, Op.NotEquals, Op.Empty, Op.NotEmpty, Op.In] } else if (type === FieldType.BB_REFERENCE) { ops = [Op.Contains, Op.NotContains, Op.ContainsAny, Op.Empty, Op.NotEmpty] - } else if (type === FieldType.LINK) { - ops = [Op.Equals, Op.NotEquals, Op.Empty, Op.NotEmpty, Op.In] } // Only allow equal/not equal for _id in SQL tables From 0f119092217333c36ee880441c84af029dcd2aaa Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 2 May 2025 11:12:23 +0100 Subject: [PATCH 23/48] Ensure primary display column is always required, regardless of validation rules --- .../src/sdk/app/rows/tests/utils.spec.ts | 27 +++++++++++++++++++ packages/server/src/sdk/app/rows/utils.ts | 8 +++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/packages/server/src/sdk/app/rows/tests/utils.spec.ts b/packages/server/src/sdk/app/rows/tests/utils.spec.ts index 516334d31d..0ae9e0c079 100644 --- a/packages/server/src/sdk/app/rows/tests/utils.spec.ts +++ b/packages/server/src/sdk/app/rows/tests/utils.spec.ts @@ -375,4 +375,31 @@ describe("validate", () => { }) }) }) + + describe("primary display", () => { + const getTable = (): Table => ({ + type: "table", + _id: generateTableID(), + name: "table", + sourceId: INTERNAL_TABLE_SOURCE_ID, + sourceType: TableSourceType.INTERNAL, + primaryDisplay: "foo", + schema: { + foo: { + name: "foo", + type: FieldType.STRING, + }, + }, + }) + + it("should always require primary display column", async () => { + const row = {} + const table = getTable() + const output = await validate({ source: table, row }) + expect(output.valid).toBe(false) + expect(output.errors).toStrictEqual({ + foo: ["can't be blank"], + }) + }) + }) }) diff --git a/packages/server/src/sdk/app/rows/utils.ts b/packages/server/src/sdk/app/rows/utils.ts index c19654d817..cac44aaac9 100644 --- a/packages/server/src/sdk/app/rows/utils.ts +++ b/packages/server/src/sdk/app/rows/utils.ts @@ -206,8 +206,14 @@ export async function validate({ ] for (let fieldName of Object.keys(table.schema)) { const column = table.schema[fieldName] - const constraints = cloneDeep(column.constraints) const type = column.type + let constraints = cloneDeep(column.constraints) + + // Ensure display column is required + if (table.primaryDisplay === fieldName) { + constraints = { ...constraints, presence: true } + } + // foreign keys are likely to be enriched if (isForeignKey(fieldName, table)) { continue From 1e7456a31cb1cb38d3dc905bf911538ee810d6ce Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Fri, 2 May 2025 11:20:37 +0100 Subject: [PATCH 24/48] Fix issue where AI config screen would flicker briefly on load --- .../builder/portal/settings/ai/index.svelte | 130 +++++++++--------- 1 file changed, 67 insertions(+), 63 deletions(-) diff --git a/packages/builder/src/pages/builder/portal/settings/ai/index.svelte b/packages/builder/src/pages/builder/portal/settings/ai/index.svelte index 1b23c81ad9..cc881bb101 100644 --- a/packages/builder/src/pages/builder/portal/settings/ai/index.svelte +++ b/packages/builder/src/pages/builder/portal/settings/ai/index.svelte @@ -35,6 +35,7 @@ }, }) + let mounted = false let aiConfig: AIConfig let configModal: { show: () => void; hide: () => void } let portalModal: { show: () => void; hide: () => void } @@ -153,80 +154,83 @@ aiConfig = (await API.getConfig(ConfigType.AI)) as AIConfig const licenseKeyResponse = await API.getLicenseKey() hasLicenseKey = licenseKeyResponse?.licenseKey + mounted = true } catch { notifications.error("Error fetching AI settings") } }) - - -
- AI -
- - Connect an LLM to enable AI features. You can only enable one LLM at a - time. - -
- +{#if mounted} + + +
+ AI +
+ + Connect an LLM to enable AI features. You can only enable one LLM at a + time. + +
+ - {#if !activeProvider && !$bannerStore} -