From bcf431a476bffe9691824f1561aed5843bff4c04 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 8 Nov 2023 14:43:28 +0000 Subject: [PATCH 01/77] Fix a few svelte bugs with the LinkedRowSelector component to fix adding/editing relationships via a model from the data UI --- .../automation/SetupPanel/RowSelectorTypes.svelte | 2 +- .../components/backend/DataTable/RowFieldControl.svelte | 7 ++++++- .../builder/src/components/common/LinkedRowSelector.svelte | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte b/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte index 373d174541..f6cce6182c 100644 --- a/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte +++ b/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte @@ -64,7 +64,7 @@ {:else if schema.type === "link"} onChange(e, field)} /> diff --git a/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte b/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte index 61b706e28e..1ec32cb3fd 100644 --- a/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte +++ b/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte @@ -70,7 +70,12 @@ options={meta.constraints.inclusion} /> {:else if type === "link"} - + (value = e.detail)} + /> {:else if type === "longform"} {#if meta.useRichText} diff --git a/packages/builder/src/components/common/LinkedRowSelector.svelte b/packages/builder/src/components/common/LinkedRowSelector.svelte index d357a0a54d..c45b7be195 100644 --- a/packages/builder/src/components/common/LinkedRowSelector.svelte +++ b/packages/builder/src/components/common/LinkedRowSelector.svelte @@ -56,12 +56,12 @@ /> {:else} row._id} sort - on:change={() => dispatch("change", linkedIds)} + on:change /> {/if} From 217ac49628c520f522cd3f503e577971f6232496 Mon Sep 17 00:00:00 2001 From: Gerard Burns Date: Tue, 21 Nov 2023 13:02:06 +0000 Subject: [PATCH 02/77] wip --- packages/bbui/src/Form/Core/TextField.svelte | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/bbui/src/Form/Core/TextField.svelte b/packages/bbui/src/Form/Core/TextField.svelte index 7afd8f86c3..cbc062d99a 100644 --- a/packages/bbui/src/Form/Core/TextField.svelte +++ b/packages/bbui/src/Form/Core/TextField.svelte @@ -21,7 +21,7 @@ let focus = false const updateValue = newValue => { - if (readonly) { + if (readonly || disabled) { return } if (type === "number") { @@ -32,14 +32,14 @@ } const onFocus = () => { - if (readonly) { + if (readonly || disabled) { return } focus = true } const onBlur = event => { - if (readonly) { + if (readonly || disabled) { return } focus = false @@ -47,14 +47,14 @@ } const onInput = event => { - if (readonly || !updateOnChange) { + if (readonly || !updateOnChange || disabled) { return } updateValue(event.target.value) } const updateValueOnEnter = event => { - if (readonly) { + if (readonly || disabled) { return } if (event.key === "Enter") { @@ -66,10 +66,12 @@ if (type === "bigint") { return "numeric" } - return type === "number" ? "decimal" : "text" + return type === "number" ? +"decimal" : "text" } onMount(() => { + if (disabled) return; focus = autofocus if (focus) field.focus() }) @@ -119,4 +121,8 @@ .spectrum-Textfield { width: 100%; } + + input:focus:hover::placeholder { + color: var(--grey-8) !important; + } From 96046dab3dc56246056fadfc490030ca22517d42 Mon Sep 17 00:00:00 2001 From: Gerard Burns Date: Tue, 21 Nov 2023 14:51:01 +0000 Subject: [PATCH 03/77] component name tooltips --- .../builder/src/builderStore/componentUtils.js | 17 +++++++++++++++++ .../src/components/common/NavItem.svelte | 5 ++++- .../builder/src/components/design/Panel.svelte | 7 +++++-- .../Component/ComponentSettingsPanel.svelte | 8 +++++--- .../ComponentList/ComponentTree.svelte | 2 ++ 5 files changed, 33 insertions(+), 6 deletions(-) diff --git a/packages/builder/src/builderStore/componentUtils.js b/packages/builder/src/builderStore/componentUtils.js index 522dbae416..85171ece66 100644 --- a/packages/builder/src/builderStore/componentUtils.js +++ b/packages/builder/src/builderStore/componentUtils.js @@ -1,4 +1,5 @@ import { store } from "./index" +import { get } from "svelte/store" import { Helpers } from "@budibase/bbui" import { decodeJSBinding, @@ -238,6 +239,10 @@ export const makeComponentUnique = component => { } export const getComponentText = component => { + if (component == null) { + return "" + } + if (component?._instanceName) { return component._instanceName } @@ -246,3 +251,15 @@ export const getComponentText = component => { "component" return capitalise(type) } + +export const getComponentName = component => { + if (component == null) { + return "" + } + + const components = get(store)?.components || {}; + const componentDefinition = components[component._component] || {}; + const name = componentDefinition.friendlyName || componentDefinition.name || ""; + + return name; +} diff --git a/packages/builder/src/components/common/NavItem.svelte b/packages/builder/src/components/common/NavItem.svelte index 2c8a862535..5ec399a7ea 100644 --- a/packages/builder/src/components/common/NavItem.svelte +++ b/packages/builder/src/components/common/NavItem.svelte @@ -1,10 +1,11 @@ {#if $selectedComponent} {#key $selectedComponent._id} - + { if (e.key.toLowerCase() === "enter") { e.target.blur() diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ComponentList/ComponentTree.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ComponentList/ComponentTree.svelte index 8adc6cb5d4..6b5d356deb 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ComponentList/ComponentTree.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ComponentList/ComponentTree.svelte @@ -11,6 +11,7 @@ import { findComponentPath, getComponentText, + getComponentName, } from "builderStore/componentUtils" import { get } from "svelte/store" import { dndStore } from "./dndStore" @@ -109,6 +110,7 @@ on:drop={onDrop} text={getComponentText(component)} icon={getComponentIcon(component)} + iconTooltip={getComponentName(component)} withArrow={componentHasChildren(component)} indentLevel={level} selected={$store.selectedComponentId === component._id} From f1c3d50930c288b2d4d0aa33a373aecefce8b7c5 Mon Sep 17 00:00:00 2001 From: Gerard Burns Date: Wed, 22 Nov 2023 20:50:55 +0000 Subject: [PATCH 04/77] functioning resize --- .../_components/ScreenList/index.svelte | 90 +++++-------- .../_components/ScreenList/resizable.js | 121 ++++++++++++++++++ 2 files changed, 150 insertions(+), 61 deletions(-) create mode 100644 packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/resizable.js diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/index.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/index.svelte index ef5911c0f8..a7d56e047a 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/index.svelte @@ -9,51 +9,42 @@ import NavItem from "components/common/NavItem.svelte" import RoleIndicator from "./RoleIndicator.svelte" import DropdownMenu from "./DropdownMenu.svelte" - import { onMount, tick } from "svelte" import { goto } from "@roxi/routify" + import { getVerticalResizeActions } from './resizable'; + import { tick } from "svelte" - let search = false + const [resizable, resizableHandle] = getVerticalResizeActions(); + + let searching = false let resizing = false let searchValue = "" let searchInput let container let screensContainer let scrolling = false - let previousHeight = null - let dragOffset $: filteredScreens = getFilteredScreens($sortedScreens, searchValue) - const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)) - const openSearch = async () => { - search = true + searching = true await tick() searchInput.focus() screensContainer.scroll({ top: 0, behavior: "smooth" }) - previousHeight = $screensHeight - $screensHeight = "calc(100% + 1px)" } const closeSearch = async () => { - if (previousHeight) { - // Restore previous height and wait for animation - $screensHeight = previousHeight - previousHeight = null - await sleep(300) - } - search = false + searching = false searchValue = "" } - const getFilteredScreens = (screens, search) => { + const getFilteredScreens = (screens, searchValue) => { return screens.filter(screen => { - return !search || screen.routing.route.includes(search) + return !searchValue || screen.routing.route.includes(searchValue) }) } const handleAddButton = () => { - if (search) { + if (searching) { closeSearch() } else { $goto("../new") @@ -70,67 +61,35 @@ scrolling = e.target.scrollTop !== 0 } - const startResizing = e => { - // Reset the height store to match the true height - $screensHeight = `${container.getBoundingClientRect().height}px` - - // Store an offset to easily compute new height when moving the mouse - dragOffset = parseInt($screensHeight) - e.clientY - - // Add event listeners - resizing = true - document.addEventListener("mousemove", resize) - document.addEventListener("mouseup", stopResizing) - } - - const resize = e => { - // Prevent negative heights as this screws with layout - const newHeight = Math.max(0, e.clientY + dragOffset) - if (newHeight == null || isNaN(newHeight)) { - return - } - $screensHeight = `${newHeight}px` - } - - const stopResizing = () => { - resizing = false - document.removeEventListener("mousemove", resize) - } - - onMount(() => { - // Ensure we aren't stuck at 100% height from leaving while searching - if ($screensHeight == null || isNaN(parseInt($screensHeight))) { - $screensHeight = "210px" - } - })
-
+
Screens
-
+
@@ -164,8 +123,10 @@
screensHeight.set("210px")} />
@@ -177,10 +138,11 @@ min-height: 147px; max-height: calc(100% - 147px); position: relative; - } - .screens.search { transition: height 300ms ease-out; + } + .screens.searching { max-height: none; + height: 100% !important; } .screens.resizing { user-select: none; @@ -219,7 +181,7 @@ .input::placeholder { color: var(--spectrum-global-color-gray-600); } - .screens.search input { + .screens.searching input { display: block; } @@ -305,4 +267,10 @@ .divider:hover:after { background: var(--spectrum-global-color-gray-300); } + .divider.disabled { + cursor: auto; + } + .divider.disabled:after { + background: var(--spectrum-global-color-gray-200); + } diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/resizable.js b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/resizable.js new file mode 100644 index 0000000000..c3bccf024d --- /dev/null +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/resizable.js @@ -0,0 +1,121 @@ +export const getHorizontalResizeActions = (initialValue, setValue = () => {}) => { + let element = null; + + const elementAction = (node) => { + element = node; + + if (initialValue != null) { + element.style.height = `${initialValue}px` + } + + return { + destroy() { + element = null; + } + } + } + + const dragHandleAction = (node) => { + let startWidth = null; + let startPosition = null; + + const handleMouseMove = (e) => { + const change = e.pageX - startPosition; + element.style.width = `${startWidth + change}px` + } + + const handleMouseUp = () => { + window.removeEventListener("mousemove", handleMouseMove); + window.removeEventListener("mouseup", handleMouseUp); + element.style.removeProperty('transition'); // remove temporary transition override + setValue(element.clientHeight); + } + + const handleMouseDown = (e) => { + if (e.target.hasAttribute("disabled") && e.target.getAttribute("disabled") !== "false") { + return; + } + + element.style.transition = "width 0ms"; // temporarily override any width transitions + startWidth = element.clientWidth; + startPosition = e.pageX; + + window.addEventListener("mousemove", handleMouseMove); + window.addEventListener("mouseup", handleMouseUp); + }; + + node.addEventListener("mousedown", handleMouseDown); + + return { + destroy() { + node.removeEventListener("mousedown", handleMouseDown); + } + } + } + + return [ + elementAction, + dragHandleAction + ] +}; + +export const getVerticalResizeActions = (initialValue, setValue = () => {}) => { + let element = null; + + const elementAction = (node) => { + element = node; + + if (initialValue != null) { + element.style.height = `${initialValue}px` + } + + return { + destroy() { + element = null; + } + } + } + + const dragHandleAction = (node) => { + let startHeight = null; + let startPosition = null; + + const handleMouseMove = (e) => { + const change = e.pageY - startPosition; + element.style.height = `${startHeight + change}px` + } + + const handleMouseUp = () => { + window.removeEventListener("mousemove", handleMouseMove); + window.removeEventListener("mouseup", handleMouseUp); + element.style.removeProperty('transition'); // remove temporary transition override + setValue(element.clientHeight); + } + + const handleMouseDown = (e) => { + if (e.target.hasAttribute("disabled") && e.target.getAttribute("disabled") !== "false") { + return; + } + + element.style.transition = "height 0ms"; // temporarily override any height transitions + startHeight = element.clientHeight; + startPosition = e.pageY; + + window.addEventListener("mousemove", handleMouseMove); + window.addEventListener("mouseup", handleMouseUp); + }; + + node.addEventListener("mousedown", handleMouseDown); + + return { + destroy() { + node.removeEventListener("mousedown", handleMouseDown); + } + } + } + + return [ + elementAction, + dragHandleAction + ] +}; From 3d39a2f77b85b56fd0c563e7ddf59ba2d91f44aa Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 23 Nov 2023 09:47:07 +0000 Subject: [PATCH 05/77] Fix relationship filter setting being ignored, and debounce searches --- .../app/forms/RelationshipField.svelte | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/client/src/components/app/forms/RelationshipField.svelte b/packages/client/src/components/app/forms/RelationshipField.svelte index 41a071686e..f047b53a94 100644 --- a/packages/client/src/components/app/forms/RelationshipField.svelte +++ b/packages/client/src/components/app/forms/RelationshipField.svelte @@ -1,6 +1,6 @@
-
-
- -
-
- {#if $store.clientFeatures.devicePreview} - - {/if} -
-
- {#key $store.version} - - {/key} +
+
+ +
+
+ {#if $store.clientFeatures.devicePreview} + + {/if} +
+
+
+ {#key $store.version} + + {/key} +
+ From e0e27aa3c96a09c1fedc2060c9bc0e6c307266b5 Mon Sep 17 00:00:00 2001 From: Gerard Burns Date: Thu, 23 Nov 2023 14:09:14 +0000 Subject: [PATCH 09/77] wip --- .../[screenId]/_components/AppPanel.svelte | 42 +++++------------ .../[screenId]/_components/LeftPanel.svelte | 46 +++++++++++++++++-- .../_components/ScreenList/resizable.js | 1 + 3 files changed, 55 insertions(+), 34 deletions(-) diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPanel.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPanel.svelte index 7cd3261bce..09f97302fd 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPanel.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPanel.svelte @@ -3,42 +3,28 @@ import AppPreview from "./AppPreview.svelte" import { store, screenHistoryStore } from "builderStore" import UndoRedoControl from "components/common/UndoRedoControl.svelte" - import { getHorizontalResizeActions } from './resizable'; - - const [resizable, resizableHandle] = getHorizontalResizeActions();
-
-
-
- -
-
- {#if $store.clientFeatures.devicePreview} - - {/if} -
+
+
+
-
- {#key $store.version} - - {/key} +
+ {#if $store.clientFeatures.devicePreview} + + {/if}
- diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/LeftPanel.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/LeftPanel.svelte index 96d4579eeb..4f77588e9b 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/LeftPanel.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/LeftPanel.svelte @@ -1,21 +1,59 @@ -
- - +
+
+ + +
+
+
diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/resizable.js b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/resizable.js index e74b72c6e5..4b9f365018 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/resizable.js +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/resizable.js @@ -20,6 +20,7 @@ const getResizeActions = (cssProperty, mouseMoveEventProperty, elementProperty, let startPosition = null; const handleMouseMove = (e) => { + e.preventDefault(); // Prevent highlighting while dragging const change = e[mouseMoveEventProperty] - startPosition; element.style[cssProperty] = `${startProperty + change}px` } From 4175ccbffb2aa843cda75d5ab7a8db72b003d559 Mon Sep 17 00:00:00 2001 From: Gerard Burns Date: Thu, 23 Nov 2023 14:12:54 +0000 Subject: [PATCH 10/77] wip --- .../[application]/design/[screenId]/_components/LeftPanel.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/LeftPanel.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/LeftPanel.svelte index 4f77588e9b..e6248b1d0a 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/LeftPanel.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/LeftPanel.svelte @@ -30,6 +30,7 @@ } .content { + overflow: scroll; flex-grow: 1; height: 100%; display: flex; From 23f5941bf52210eb42b2cc0dc5031717055bacb4 Mon Sep 17 00:00:00 2001 From: Gerard Burns Date: Thu, 23 Nov 2023 17:05:49 +0000 Subject: [PATCH 11/77] move position of screen nav item menu --- .../builder/src/components/common/NavItem.svelte | 4 +++- .../builder/src/components/design/Panel.svelte | 2 ++ .../design/[screenId]/_components/AppPanel.svelte | 1 + .../[screenId]/_components/LeftPanel.svelte | 4 ++-- .../_components/ScreenList/index.svelte | 3 ++- .../_components/ScreenList/resizable.js | 15 ++++++++++++++- .../design/[screenId]/_layout.svelte | 1 + 7 files changed, 25 insertions(+), 5 deletions(-) diff --git a/packages/builder/src/components/common/NavItem.svelte b/packages/builder/src/components/common/NavItem.svelte index 5ec399a7ea..1a18d92eca 100644 --- a/packages/builder/src/components/common/NavItem.svelte +++ b/packages/builder/src/components/common/NavItem.svelte @@ -166,9 +166,10 @@ justify-content: flex-start; align-items: center; gap: var(--spacing-xs); - width: max-content; + width: 100%; position: relative; padding-left: var(--spacing-l); + box-sizing: border-box; } /* Needed to fully display the actions icon */ @@ -266,6 +267,7 @@ } .right { + margin-left: auto; order: 10; } diff --git a/packages/builder/src/components/design/Panel.svelte b/packages/builder/src/components/design/Panel.svelte index 6cfc65a1a4..f9190bea60 100644 --- a/packages/builder/src/components/design/Panel.svelte +++ b/packages/builder/src/components/design/Panel.svelte @@ -66,6 +66,7 @@ diff --git a/packages/builder/src/components/design/settings/controls/FieldConfiguration/FieldSetting.svelte b/packages/builder/src/components/design/settings/controls/FieldConfiguration/FieldSetting.svelte index 1d9ce733b8..8c40c455c8 100644 --- a/packages/builder/src/components/design/settings/controls/FieldConfiguration/FieldSetting.svelte +++ b/packages/builder/src/components/design/settings/controls/FieldConfiguration/FieldSetting.svelte @@ -58,7 +58,15 @@
{readableText}
- + { + e.stopPropagation() + }} + text="" + value={item.active} + thin + />
diff --git a/packages/client/manifest.json b/packages/client/manifest.json index 07645d874a..8573d0a376 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -6040,18 +6040,6 @@ "options": ["Create", "Update", "View"], "defaultValue": "Create" }, - { - "type": "text", - "label": "Title", - "key": "title", - "nested": true - }, - { - "type": "text", - "label": "Description", - "key": "description", - "nested": true - }, { "section": true, "dependsOn": { @@ -6059,7 +6047,7 @@ "value": "Create", "invert": true }, - "name": "Row details", + "name": "Row ID", "info": "How to pass a row ID using bindings", "settings": [ { @@ -6079,8 +6067,20 @@ }, { "section": true, - "name": "Fields", + "name": "Details", "settings": [ + { + "type": "text", + "label": "Title", + "key": "title", + "nested": true + }, + { + "type": "text", + "label": "Description", + "key": "description", + "nested": true + }, { "type": "fieldConfiguration", "key": "fields", From 22dc6a682b870691fb05a9fa9cc4dead31a7db28 Mon Sep 17 00:00:00 2001 From: Dean Date: Thu, 23 Nov 2023 18:15:33 +0000 Subject: [PATCH 14/77] Lint --- .../design/settings/controls/DraggableList/DraggableList.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 18b6700040..cce11e4b17 100644 --- a/packages/builder/src/components/design/settings/controls/DraggableList/DraggableList.svelte +++ b/packages/builder/src/components/design/settings/controls/DraggableList/DraggableList.svelte @@ -92,7 +92,7 @@ on:click={() => { get(store).actions.select(draggableItem.id) }} - on:mousedown={e => { + on:mousedown={() => { get(store).actions.select() }} bind:this={anchors[draggableItem.id]} From 1fb61e6ed9969eb229f0407b6c2a0f2d7e76b7a9 Mon Sep 17 00:00:00 2001 From: Gerard Burns Date: Fri, 24 Nov 2023 10:18:49 +0000 Subject: [PATCH 15/77] wip --- packages/bbui/src/Form/Core/TextField.svelte | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/bbui/src/Form/Core/TextField.svelte b/packages/bbui/src/Form/Core/TextField.svelte index f29cb379a2..008007256a 100644 --- a/packages/bbui/src/Form/Core/TextField.svelte +++ b/packages/bbui/src/Form/Core/TextField.svelte @@ -121,7 +121,15 @@ width: 100%; } - input:focus:hover::placeholder { - color: var(--grey-8) !important; + input::placeholder { + color: var(--grey-7); + } + + input:hover::placeholder { + color: var(--grey-7) !important; + } + + input:focus::placeholder { + color: var(--grey-7) !important; } From a65b29eb880f264fbc4fc78d3468f6109bc7044b Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 27 Nov 2023 18:50:44 +0000 Subject: [PATCH 16/77] banner changes for new pricing, fix for onboarding to prevent flash of UI before onboarding tutorial --- packages/backend-core/src/objectStore/objectStore.ts | 4 ++-- .../src/pages/builder/portal/apps/_layout.svelte | 2 +- .../src/pages/builder/portal/apps/index.svelte | 12 ++++++++++++ packages/pro | 2 +- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/backend-core/src/objectStore/objectStore.ts b/packages/backend-core/src/objectStore/objectStore.ts index cdaf19fa55..cfd988f5e0 100644 --- a/packages/backend-core/src/objectStore/objectStore.ts +++ b/packages/backend-core/src/objectStore/objectStore.ts @@ -260,12 +260,12 @@ export async function listAllObjects(bucketName: string, path: string) { } /** - * Generate a presigned url with a default TTL of 1 hour + * Generate a presigned url with a default TTL of 1 day */ export function getPresignedUrl( bucketName: string, key: string, - durationSeconds: number = 3600 + durationSeconds: number = 86400 ) { const objectStore = ObjectStore(bucketName, { presigning: true }) const params = { diff --git a/packages/builder/src/pages/builder/portal/apps/_layout.svelte b/packages/builder/src/pages/builder/portal/apps/_layout.svelte index 38c0274eca..8810edca9c 100644 --- a/packages/builder/src/pages/builder/portal/apps/_layout.svelte +++ b/packages/builder/src/pages/builder/portal/apps/_layout.svelte @@ -14,7 +14,7 @@ import PortalSideBar from "./_components/PortalSideBar.svelte" // Don't block loading if we've already hydrated state - let loaded = $apps.length != null + let loaded = !!$apps?.length onMount(async () => { try { diff --git a/packages/builder/src/pages/builder/portal/apps/index.svelte b/packages/builder/src/pages/builder/portal/apps/index.svelte index ad0d3658ea..fb0ba8bc2e 100644 --- a/packages/builder/src/pages/builder/portal/apps/index.svelte +++ b/packages/builder/src/pages/builder/portal/apps/index.svelte @@ -1,5 +1,6 @@ @@ -48,7 +48,7 @@ .dividerClickExtender { position: absolute; - cursor: row-resize; + cursor: col-resize; height: 100%; width: 12px; } diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/index.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/index.svelte index f01533d6bd..e3e24d7b13 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ScreenList/index.svelte @@ -5,7 +5,7 @@ import RoleIndicator from "./RoleIndicator.svelte" import DropdownMenu from "./DropdownMenu.svelte" import { goto } from "@roxi/routify" - import { getVerticalResizeActions } from "./resizable" + import { getVerticalResizeActions } from "components/common/resizable" import NavHeader from "components/common/NavHeader.svelte" const [resizable, resizableHandle] = getVerticalResizeActions() From 9ac413a73fd0dcf858fdd92d21b96d381f79845b Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 28 Nov 2023 11:20:19 +0000 Subject: [PATCH 27/77] Remove healthcheck script for now. --- hosting/couchdb/Dockerfile | 4 ---- hosting/couchdb/healthcheck.sh | 13 ------------- 2 files changed, 17 deletions(-) delete mode 100644 hosting/couchdb/healthcheck.sh diff --git a/hosting/couchdb/Dockerfile b/hosting/couchdb/Dockerfile index 254a676f63..f83df7038b 100644 --- a/hosting/couchdb/Dockerfile +++ b/hosting/couchdb/Dockerfile @@ -29,10 +29,6 @@ WORKDIR /opt/couchdb ADD couch/vm.args couch/local.ini ./etc/ WORKDIR / -ADD healthcheck.sh ./healthcheck.sh -RUN chmod +x ./healthcheck.sh -HEALTHCHECK --interval=15s --timeout=15s --start-period=45s CMD "/healthcheck.sh" - ADD runner.sh ./bbcouch-runner.sh RUN chmod +x ./bbcouch-runner.sh /opt/clouseau/bin/clouseau CMD ["./bbcouch-runner.sh"] diff --git a/hosting/couchdb/healthcheck.sh b/hosting/couchdb/healthcheck.sh deleted file mode 100644 index 758ee5111a..0000000000 --- a/hosting/couchdb/healthcheck.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash -healthy=true - -if [[ $(curl -s -w "%{http_code}\n" http://localhost:5984/_up -o /dev/null) -ne 200 ]]; then - echo 'ERROR: CouchDB is not running'; - healthy=false -fi - -if [ $healthy == true ]; then - exit 0 -else - exit 1 -fi From 24357aa7de033002501b446eca22866500af2292 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 28 Nov 2023 11:42:20 +0000 Subject: [PATCH 28/77] update copy in banner --- packages/builder/src/pages/builder/portal/apps/index.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/pages/builder/portal/apps/index.svelte b/packages/builder/src/pages/builder/portal/apps/index.svelte index fac5c502b3..cf2c61b11d 100644 --- a/packages/builder/src/pages/builder/portal/apps/index.svelte +++ b/packages/builder/src/pages/builder/portal/apps/index.svelte @@ -205,7 +205,7 @@ messages: [ { message: - "We've updated our pricing - read the blog post to learn more.", + "We've updated our pricing - see our website to learn more.", type: BANNER_TYPES.NEUTRAL, extraButtonText: "Learn More", extraButtonAction: () => From e35e4b592c441a3f075a47c1fb03793e3eece354 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Tue, 28 Nov 2023 11:48:11 +0000 Subject: [PATCH 29/77] Bump version to 2.13.19 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 38872a31b1..e54b76c5e9 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.13.18", + "version": "2.13.19", "npmClient": "yarn", "packages": [ "packages/*" From 204769b6e99be4aeef026c1d65f052718bff7c04 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 29 Nov 2023 09:19:08 +0000 Subject: [PATCH 30/77] Add @budibase/backend as code owners to packages/{server,worker,backend-core} --- packages/backend-core/CODEOWNERS | 1 + packages/server/CODEOWNERS | 1 + packages/worker/CODEOWNERS | 1 + 3 files changed, 3 insertions(+) create mode 100644 packages/backend-core/CODEOWNERS create mode 100644 packages/server/CODEOWNERS create mode 100644 packages/worker/CODEOWNERS diff --git a/packages/backend-core/CODEOWNERS b/packages/backend-core/CODEOWNERS new file mode 100644 index 0000000000..291ffa073e --- /dev/null +++ b/packages/backend-core/CODEOWNERS @@ -0,0 +1 @@ +* @budibase/backend \ No newline at end of file diff --git a/packages/server/CODEOWNERS b/packages/server/CODEOWNERS new file mode 100644 index 0000000000..291ffa073e --- /dev/null +++ b/packages/server/CODEOWNERS @@ -0,0 +1 @@ +* @budibase/backend \ No newline at end of file diff --git a/packages/worker/CODEOWNERS b/packages/worker/CODEOWNERS new file mode 100644 index 0000000000..291ffa073e --- /dev/null +++ b/packages/worker/CODEOWNERS @@ -0,0 +1 @@ +* @budibase/backend \ No newline at end of file From fb3c072165cc48b128ab8bcbdb11e33c73e6654f Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 29 Nov 2023 09:22:47 +0000 Subject: [PATCH 31/77] Capitalise @Budibase. --- packages/backend-core/CODEOWNERS | 2 +- packages/server/CODEOWNERS | 2 +- packages/worker/CODEOWNERS | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/backend-core/CODEOWNERS b/packages/backend-core/CODEOWNERS index 291ffa073e..84313fb9cf 100644 --- a/packages/backend-core/CODEOWNERS +++ b/packages/backend-core/CODEOWNERS @@ -1 +1 @@ -* @budibase/backend \ No newline at end of file +* @Budibase/backend \ No newline at end of file diff --git a/packages/server/CODEOWNERS b/packages/server/CODEOWNERS index 291ffa073e..84313fb9cf 100644 --- a/packages/server/CODEOWNERS +++ b/packages/server/CODEOWNERS @@ -1 +1 @@ -* @budibase/backend \ No newline at end of file +* @Budibase/backend \ No newline at end of file diff --git a/packages/worker/CODEOWNERS b/packages/worker/CODEOWNERS index 291ffa073e..84313fb9cf 100644 --- a/packages/worker/CODEOWNERS +++ b/packages/worker/CODEOWNERS @@ -1 +1 @@ -* @budibase/backend \ No newline at end of file +* @Budibase/backend \ No newline at end of file From a5a3b12936e85755e71920ec738d0879332b58c2 Mon Sep 17 00:00:00 2001 From: melohagan <101575380+melohagan@users.noreply.github.com> Date: Wed, 29 Nov 2023 10:10:59 +0000 Subject: [PATCH 32/77] Return false don't throw (#12460) Co-authored-by: Sam Rose --- packages/shared-core/src/filters.ts | 2 +- .../shared-core/src/tests/filters.test.ts | 36 +++++++++---------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/packages/shared-core/src/filters.ts b/packages/shared-core/src/filters.ts index 564e8a52c9..5e24b640d4 100644 --- a/packages/shared-core/src/filters.ts +++ b/packages/shared-core/src/filters.ts @@ -315,7 +315,7 @@ export const runLuceneQuery = (docs: any[], query?: SearchQuery) => { new Date(docValue).getTime() > new Date(testValue.high).getTime() ) } - throw "Cannot perform range filter - invalid type." + return false } ) diff --git a/packages/shared-core/src/tests/filters.test.ts b/packages/shared-core/src/tests/filters.test.ts index 6f488cffbd..bddd6cb1f0 100644 --- a/packages/shared-core/src/tests/filters.test.ts +++ b/packages/shared-core/src/tests/filters.test.ts @@ -130,32 +130,28 @@ describe("runLuceneQuery", () => { expect(runLuceneQuery(docs, query).map(row => row.order_id)).toEqual([2]) }) - it("should throw an error is an invalid doc value is passed into a range filter", async () => { + it("should return return all docs if an invalid doc value is passed into a range filter", async () => { + const docs = [ + { + order_id: 4, + customer_id: 1758, + order_status: 5, + order_date: "{{ Binding.INVALID }}", + required_date: "2017-03-05T00:00:00.000Z", + shipped_date: "2017-03-03T00:00:00.000Z", + store_id: 2, + staff_id: 7, + description: undefined, + label: "", + }, + ] const query = buildQuery("range", { order_date: { low: "2016-01-04T00:00:00.000Z", high: "2016-01-11T00:00:00.000Z", }, }) - expect(() => - runLuceneQuery( - [ - { - order_id: 4, - customer_id: 1758, - order_status: 5, - order_date: "INVALID", - required_date: "2017-03-05T00:00:00.000Z", - shipped_date: "2017-03-03T00:00:00.000Z", - store_id: 2, - staff_id: 7, - description: undefined, - label: "", - }, - ], - query - ) - ).toThrowError("Cannot perform range filter - invalid type.") + expect(runLuceneQuery(docs, query)).toEqual(docs) }) it("should return rows with matches on empty filter", () => { From 37dc8ba6e4d480e5a13b27d9367ea36868eeea4a Mon Sep 17 00:00:00 2001 From: melohagan <101575380+melohagan@users.noreply.github.com> Date: Wed, 29 Nov 2023 10:23:21 +0000 Subject: [PATCH 33/77] Only export selected columns (#12438) * Only export selected columns * Refactor and unit test --- .../src/sdk/app/rows/search/external.ts | 16 +++++------ .../src/sdk/app/rows/search/internal.ts | 5 ++-- .../server/src/sdk/tests/rows/row.spec.ts | 28 ++++++++++++++++++- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/packages/server/src/sdk/app/rows/search/external.ts b/packages/server/src/sdk/app/rows/search/external.ts index 2fc6caeb39..8465f997e3 100644 --- a/packages/server/src/sdk/app/rows/search/external.ts +++ b/packages/server/src/sdk/app/rows/search/external.ts @@ -133,9 +133,14 @@ export async function exportRows( let result = await search({ tableId, query: requestQuery, sort, sortOrder }) let rows: Row[] = [] + let headers + + if (!tableName) { + throw new HTTPError("Could not find table name.", 400) + } + const schema = datasource.entities[tableName].schema // Filter data to only specified columns if required - if (columns && columns.length) { for (let i = 0; i < result.rows.length; i++) { rows[i] = {} @@ -143,22 +148,17 @@ export async function exportRows( rows[i][column] = result.rows[i][column] } } + headers = columns } else { rows = result.rows } - if (!tableName) { - throw new HTTPError("Could not find table name.", 400) - } - const schema = datasource.entities[tableName].schema let exportRows = cleanExportRows(rows, schema, format, columns) - let headers = Object.keys(schema) - let content: string switch (format) { case exporters.Format.CSV: - content = exporters.csv(headers, exportRows) + content = exporters.csv(headers ?? Object.keys(schema), exportRows) break case exporters.Format.JSON: content = exporters.json(exportRows) diff --git a/packages/server/src/sdk/app/rows/search/internal.ts b/packages/server/src/sdk/app/rows/search/internal.ts index 87a33c0ba0..22cb3985b7 100644 --- a/packages/server/src/sdk/app/rows/search/internal.ts +++ b/packages/server/src/sdk/app/rows/search/internal.ts @@ -110,7 +110,7 @@ export async function exportRows( let rows: Row[] = [] let schema = table.schema - + let headers // Filter data to only specified columns if required if (columns && columns.length) { for (let i = 0; i < result.length; i++) { @@ -119,6 +119,7 @@ export async function exportRows( rows[i][column] = result[i][column] } } + headers = columns } else { rows = result } @@ -127,7 +128,7 @@ export async function exportRows( if (format === Format.CSV) { return { fileName: "export.csv", - content: csv(Object.keys(rows[0]), exportRows), + content: csv(headers ?? Object.keys(rows[0]), exportRows), } } else if (format === Format.JSON) { return { diff --git a/packages/server/src/sdk/tests/rows/row.spec.ts b/packages/server/src/sdk/tests/rows/row.spec.ts index af3d405e15..8b01356e35 100644 --- a/packages/server/src/sdk/tests/rows/row.spec.ts +++ b/packages/server/src/sdk/tests/rows/row.spec.ts @@ -18,7 +18,6 @@ jest.mock("../../../utilities/rowProcessor", () => ({ jest.mock("../../../api/controllers/view/exporters", () => ({ ...jest.requireActual("../../../api/controllers/view/exporters"), - csv: jest.fn(), Format: { CSV: "csv", }, @@ -102,5 +101,32 @@ describe("external row sdk", () => { new HTTPError("Could not find table name.", 400) ) }) + + it("should only export specified columns", async () => { + mockDatasourcesGet.mockImplementation(async () => ({ + entities: { + tablename: { + schema: { + name: {}, + age: {}, + dob: {}, + }, + }, + }, + })) + const headers = ["name", "dob"] + + const result = await exportRows({ + tableId: "datasource__tablename", + format: Format.CSV, + query: {}, + columns: headers, + }) + + expect(result).toEqual({ + fileName: "export.csv", + content: `"name","dob"`, + }) + }) }) }) From c2a82bb02190a493db79b7b101f6f38752e972e1 Mon Sep 17 00:00:00 2001 From: melohagan <101575380+melohagan@users.noreply.github.com> Date: Wed, 29 Nov 2023 14:48:50 +0000 Subject: [PATCH 34/77] FIX broken references in a list of actions (#12459) * Refactor * Update action bindings on delete * Update action bindings on move * Fix with additional tests * Ensure visible binding is updated on drag release * fix * Refresh visible binding when action is deleted * Refactor * Refactor --- .../builder/src/builderStore/dataBinding.js | 84 ++++ .../builderStore/store/automation/index.js | 33 +- .../builderStore/tests/dataBinding.test.js | 459 ++++++++++++++++++ .../ButtonActionDrawer.svelte | 32 +- 4 files changed, 581 insertions(+), 27 deletions(-) diff --git a/packages/builder/src/builderStore/dataBinding.js b/packages/builder/src/builderStore/dataBinding.js index 85ac822006..d86e94aba2 100644 --- a/packages/builder/src/builderStore/dataBinding.js +++ b/packages/builder/src/builderStore/dataBinding.js @@ -29,6 +29,12 @@ const CAPTURE_VAR_INSIDE_TEMPLATE = /{{([^}]+)}}/g const CAPTURE_VAR_INSIDE_JS = /\$\("([^")]+)"\)/g const CAPTURE_HBS_TEMPLATE = /{{[\S\s]*?}}/g +const UpdateReferenceAction = { + ADD: "add", + DELETE: "delete", + MOVE: "move", +} + /** * Gets all bindable data context fields and instance fields. */ @@ -1226,3 +1232,81 @@ export const runtimeToReadableBinding = ( "readableBinding" ) } + +/** + * Used to update binding references for automation or action steps + * + * @param obj - The object to be updated + * @param originalIndex - The original index of the step being moved. Not applicable to add/delete. + * @param modifiedIndex - The new index of the step being modified + * @param action - Used to determine if a step is being added, deleted or moved + * @param label - The binding text that describes the steps + */ +export const updateReferencesInObject = ({ + obj, + modifiedIndex, + action, + label, + originalIndex, +}) => { + const stepIndexRegex = new RegExp(`{{\\s*${label}\\.(\\d+)\\.`, "g") + const updateActionStep = (str, index, replaceWith) => + str.replace(`{{ ${label}.${index}.`, `{{ ${label}.${replaceWith}.`) + for (const key in obj) { + if (typeof obj[key] === "string") { + let matches + while ((matches = stepIndexRegex.exec(obj[key])) !== null) { + const referencedStep = parseInt(matches[1]) + if ( + action === UpdateReferenceAction.ADD && + referencedStep >= modifiedIndex + ) { + obj[key] = updateActionStep( + obj[key], + referencedStep, + referencedStep + 1 + ) + } else if ( + action === UpdateReferenceAction.DELETE && + referencedStep > modifiedIndex + ) { + obj[key] = updateActionStep( + obj[key], + referencedStep, + referencedStep - 1 + ) + } else if (action === UpdateReferenceAction.MOVE) { + if (referencedStep === originalIndex) { + obj[key] = updateActionStep(obj[key], referencedStep, modifiedIndex) + } else if ( + modifiedIndex <= referencedStep && + modifiedIndex < originalIndex + ) { + obj[key] = updateActionStep( + obj[key], + referencedStep, + referencedStep + 1 + ) + } else if ( + modifiedIndex >= referencedStep && + modifiedIndex > originalIndex + ) { + obj[key] = updateActionStep( + obj[key], + referencedStep, + referencedStep - 1 + ) + } + } + } + } else if (typeof obj[key] === "object" && obj[key] !== null) { + updateReferencesInObject({ + obj: obj[key], + modifiedIndex, + action, + label, + originalIndex, + }) + } + } +} diff --git a/packages/builder/src/builderStore/store/automation/index.js b/packages/builder/src/builderStore/store/automation/index.js index ba2458f414..af83f73dc6 100644 --- a/packages/builder/src/builderStore/store/automation/index.js +++ b/packages/builder/src/builderStore/store/automation/index.js @@ -4,6 +4,7 @@ import { cloneDeep } from "lodash/fp" import { generate } from "shortid" import { selectedAutomation } from "builderStore" import { notifications } from "@budibase/bbui" +import { updateReferencesInObject } from "builderStore/dataBinding" const initialAutomationState = { automations: [], @@ -22,34 +23,14 @@ export const getAutomationStore = () => { return store } -const updateReferencesInObject = (obj, modifiedIndex, action) => { - const regex = /{{\s*steps\.(\d+)\./g - for (const key in obj) { - if (typeof obj[key] === "string") { - let matches - while ((matches = regex.exec(obj[key])) !== null) { - const referencedStep = parseInt(matches[1]) - if (action === "add" && referencedStep >= modifiedIndex) { - obj[key] = obj[key].replace( - `{{ steps.${referencedStep}.`, - `{{ steps.${referencedStep + 1}.` - ) - } else if (action === "delete" && referencedStep > modifiedIndex) { - obj[key] = obj[key].replace( - `{{ steps.${referencedStep}.`, - `{{ steps.${referencedStep - 1}.` - ) - } - } - } else if (typeof obj[key] === "object" && obj[key] !== null) { - updateReferencesInObject(obj[key], modifiedIndex, action) - } - } -} - const updateStepReferences = (steps, modifiedIndex, action) => { steps.forEach(step => { - updateReferencesInObject(step.inputs, modifiedIndex, action) + updateReferencesInObject({ + obj: step.inputs, + modifiedIndex, + action, + label: "steps", + }) }) } diff --git a/packages/builder/src/builderStore/tests/dataBinding.test.js b/packages/builder/src/builderStore/tests/dataBinding.test.js index 47f6564749..039e33a94d 100644 --- a/packages/builder/src/builderStore/tests/dataBinding.test.js +++ b/packages/builder/src/builderStore/tests/dataBinding.test.js @@ -2,6 +2,7 @@ import { expect, describe, it, vi } from "vitest" import { runtimeToReadableBinding, readableToRuntimeBinding, + updateReferencesInObject, } from "../dataBinding" vi.mock("@budibase/frontend-core") @@ -84,3 +85,461 @@ describe("readableToRuntimeBinding", () => { ).toEqual(`Hello {{ [user].[firstName] }}! The count is {{ count }}.`) }) }) + +describe("updateReferencesInObject", () => { + it("should increment steps in sequence on 'add'", () => { + let obj = [ + { + id: "a0", + parameters: { + text: "Alpha", + }, + }, + { + id: "a1", + parameters: { + text: "Apple", + }, + }, + { + id: "b2", + parameters: { + text: "Banana {{ actions.1.row }}", + }, + }, + { + id: "c3", + parameters: { + text: "Carrot {{ actions.1.row }}", + }, + }, + { + id: "d4", + parameters: { + text: "Dog {{ actions.3.row }}", + }, + }, + { + id: "e5", + parameters: { + text: "Eagle {{ actions.4.row }}", + }, + }, + ] + updateReferencesInObject({ + obj, + modifiedIndex: 0, + action: "add", + label: "actions", + }) + + expect(obj).toEqual([ + { + id: "a0", + parameters: { + text: "Alpha", + }, + }, + { + id: "a1", + parameters: { + text: "Apple", + }, + }, + { + id: "b2", + parameters: { + text: "Banana {{ actions.2.row }}", + }, + }, + { + id: "c3", + parameters: { + text: "Carrot {{ actions.2.row }}", + }, + }, + { + id: "d4", + parameters: { + text: "Dog {{ actions.4.row }}", + }, + }, + { + id: "e5", + parameters: { + text: "Eagle {{ actions.5.row }}", + }, + }, + ]) + }) + + it("should decrement steps in sequence on 'delete'", () => { + let obj = [ + { + id: "a1", + parameters: { + text: "Apple", + }, + }, + { + id: "b2", + parameters: { + text: "Banana {{ actions.1.row }}", + }, + }, + { + id: "d4", + parameters: { + text: "Dog {{ actions.3.row }}", + }, + }, + { + id: "e5", + parameters: { + text: "Eagle {{ actions.4.row }}", + }, + }, + ] + updateReferencesInObject({ + obj, + modifiedIndex: 2, + action: "delete", + label: "actions", + }) + + expect(obj).toEqual([ + { + id: "a1", + parameters: { + text: "Apple", + }, + }, + { + id: "b2", + parameters: { + text: "Banana {{ actions.1.row }}", + }, + }, + { + id: "d4", + parameters: { + text: "Dog {{ actions.2.row }}", + }, + }, + { + id: "e5", + parameters: { + text: "Eagle {{ actions.3.row }}", + }, + }, + ]) + }) + + it("should handle on 'move' to a lower index", () => { + let obj = [ + { + id: "a1", + parameters: { + text: "Apple", + }, + }, + { + id: "b2", + parameters: { + text: "Banana {{ actions.0.row }}", + }, + }, + { + id: "e5", + parameters: { + text: "Eagle {{ actions.3.row }}", + }, + }, + { + id: "c3", + parameters: { + text: "Carrot {{ actions.0.row }}", + }, + }, + { + id: "d4", + parameters: { + text: "Dog {{ actions.2.row }}", + }, + }, + ] + updateReferencesInObject({ + obj, + modifiedIndex: 2, + action: "move", + label: "actions", + originalIndex: 4, + }) + + expect(obj).toEqual([ + { + id: "a1", + parameters: { + text: "Apple", + }, + }, + { + id: "b2", + parameters: { + text: "Banana {{ actions.0.row }}", + }, + }, + { + id: "e5", + parameters: { + text: "Eagle {{ actions.4.row }}", + }, + }, + { + id: "c3", + parameters: { + text: "Carrot {{ actions.0.row }}", + }, + }, + { + id: "d4", + parameters: { + text: "Dog {{ actions.3.row }}", + }, + }, + ]) + }) + + it("should handle on 'move' to a higher index", () => { + let obj = [ + { + id: "b2", + parameters: { + text: "Banana {{ actions.0.row }}", + }, + }, + { + id: "c3", + parameters: { + text: "Carrot {{ actions.0.row }}", + }, + }, + { + id: "a1", + parameters: { + text: "Apple", + }, + }, + { + id: "d4", + parameters: { + text: "Dog {{ actions.2.row }}", + }, + }, + { + id: "e5", + parameters: { + text: "Eagle {{ actions.3.row }}", + }, + }, + ] + updateReferencesInObject({ + obj, + modifiedIndex: 2, + action: "move", + label: "actions", + originalIndex: 0, + }) + + expect(obj).toEqual([ + { + id: "b2", + parameters: { + text: "Banana {{ actions.2.row }}", + }, + }, + { + id: "c3", + parameters: { + text: "Carrot {{ actions.2.row }}", + }, + }, + { + id: "a1", + parameters: { + text: "Apple", + }, + }, + { + id: "d4", + parameters: { + text: "Dog {{ actions.1.row }}", + }, + }, + { + id: "e5", + parameters: { + text: "Eagle {{ actions.3.row }}", + }, + }, + ]) + }) + + it("should handle on 'move' of action being referenced, dragged to a higher index", () => { + let obj = [ + { + "##eventHandlerType": "Validate Form", + id: "cCD0Dwcnq", + }, + { + "##eventHandlerType": "Close Screen Modal", + id: "3fbbIOfN0H", + }, + { + "##eventHandlerType": "Save Row", + parameters: { + tableId: "ta_bb_employee", + }, + id: "aehg5cTmhR", + }, + { + "##eventHandlerType": "Close Side Panel", + id: "mzkpf86cxo", + }, + { + "##eventHandlerType": "Navigate To", + id: "h0uDFeJa8A", + }, + { + parameters: { + autoDismiss: true, + type: "success", + message: "{{ actions.1.row }}", + }, + "##eventHandlerType": "Show Notification", + id: "JEI5lAyJZ", + }, + ] + updateReferencesInObject({ + obj, + modifiedIndex: 2, + action: "move", + label: "actions", + originalIndex: 1, + }) + + expect(obj).toEqual([ + { + "##eventHandlerType": "Validate Form", + id: "cCD0Dwcnq", + }, + { + "##eventHandlerType": "Close Screen Modal", + id: "3fbbIOfN0H", + }, + { + "##eventHandlerType": "Save Row", + parameters: { + tableId: "ta_bb_employee", + }, + id: "aehg5cTmhR", + }, + { + "##eventHandlerType": "Close Side Panel", + id: "mzkpf86cxo", + }, + { + "##eventHandlerType": "Navigate To", + id: "h0uDFeJa8A", + }, + { + parameters: { + autoDismiss: true, + type: "success", + message: "{{ actions.2.row }}", + }, + "##eventHandlerType": "Show Notification", + id: "JEI5lAyJZ", + }, + ]) + }) + + it("should handle on 'move' of action being referenced, dragged to a lower index", () => { + let obj = [ + { + "##eventHandlerType": "Save Row", + parameters: { + tableId: "ta_bb_employee", + }, + id: "aehg5cTmhR", + }, + { + "##eventHandlerType": "Validate Form", + id: "cCD0Dwcnq", + }, + { + "##eventHandlerType": "Close Screen Modal", + id: "3fbbIOfN0H", + }, + { + "##eventHandlerType": "Close Side Panel", + id: "mzkpf86cxo", + }, + { + "##eventHandlerType": "Navigate To", + id: "h0uDFeJa8A", + }, + { + parameters: { + autoDismiss: true, + type: "success", + message: "{{ actions.4.row }}", + }, + "##eventHandlerType": "Show Notification", + id: "JEI5lAyJZ", + }, + ] + updateReferencesInObject({ + obj, + modifiedIndex: 0, + action: "move", + label: "actions", + originalIndex: 4, + }) + + expect(obj).toEqual([ + { + "##eventHandlerType": "Save Row", + parameters: { + tableId: "ta_bb_employee", + }, + id: "aehg5cTmhR", + }, + { + "##eventHandlerType": "Validate Form", + id: "cCD0Dwcnq", + }, + { + "##eventHandlerType": "Close Screen Modal", + id: "3fbbIOfN0H", + }, + { + "##eventHandlerType": "Close Side Panel", + id: "mzkpf86cxo", + }, + { + "##eventHandlerType": "Navigate To", + id: "h0uDFeJa8A", + }, + { + parameters: { + autoDismiss: true, + type: "success", + message: "{{ actions.0.row }}", + }, + "##eventHandlerType": "Show Notification", + id: "JEI5lAyJZ", + }, + ]) + }) +}) diff --git a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/ButtonActionDrawer.svelte b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/ButtonActionDrawer.svelte index f9541ea79f..109f9f62a2 100644 --- a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/ButtonActionDrawer.svelte +++ b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/ButtonActionDrawer.svelte @@ -15,6 +15,7 @@ getEventContextBindings, getActionBindings, makeStateBinding, + updateReferencesInObject, } from "builderStore/dataBinding" import { cloneDeep } from "lodash/fp" @@ -30,6 +31,7 @@ let actionQuery let selectedAction = actions?.length ? actions[0] : null + let originalActionIndex const setUpdateActions = actions => { return actions @@ -115,6 +117,14 @@ if (isSelected) { selectedAction = actions?.length ? actions[0] : null } + + // Update action binding references + updateReferencesInObject({ + obj: actions, + modifiedIndex: index, + action: "delete", + label: "actions", + }) } const toggleActionList = () => { @@ -146,9 +156,29 @@ function handleDndConsider(e) { actions = e.detail.items + + // set the initial index of the action being dragged + if (e.detail.info.trigger === "draggedEntered") { + originalActionIndex = actions.findIndex( + action => action.id === e.detail.info.id + ) + } } function handleDndFinalize(e) { actions = e.detail.items + + // Update action binding references + updateReferencesInObject({ + obj: actions, + modifiedIndex: actions.findIndex( + action => action.id === e.detail.info.id + ), + action: "move", + label: "actions", + originalIndex: originalActionIndex, + }) + + originalActionIndex = -1 } const getAllBindings = (actionBindings, eventContextBindings, actions) => { @@ -289,7 +319,7 @@ {#if selectedActionComponent && !showAvailableActions} - {#key selectedAction.id} + {#key (selectedAction.id, originalActionIndex)}
Date: Wed, 29 Nov 2023 14:49:55 +0000 Subject: [PATCH 35/77] Removing codecov upload during Budibase CI/on PRs, right now this isn't much use due to the NX caching - if we wish to have accurate code coverage reports we will need to run a separate job periodically to check coverage by running the whole suite, with no caching. --- .github/workflows/budibase_ci.yml | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index 6e04ca6f67..cb713c93ac 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -99,11 +99,6 @@ jobs: else yarn test --ignore=@budibase/worker --ignore=@budibase/server --ignore=@budibase/pro fi - - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos - name: codecov-umbrella - verbose: true test-worker: runs-on: ubuntu-latest @@ -129,12 +124,6 @@ jobs: yarn test --scope=@budibase/worker fi - - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN || github.token }} # not required for public repos - name: codecov-umbrella - verbose: true - test-server: runs-on: ubuntu-latest steps: @@ -159,12 +148,6 @@ jobs: yarn test --scope=@budibase/server fi - - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN || github.token }} # not required for public repos - name: codecov-umbrella - verbose: true - test-pro: runs-on: ubuntu-latest if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Budibase/budibase' From 5ac30510c685a558dfe332cd734ba3cf55aaa0e5 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Wed, 29 Nov 2023 15:14:53 +0000 Subject: [PATCH 36/77] Bump version to 2.13.20 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index e54b76c5e9..deb273884d 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.13.19", + "version": "2.13.20", "npmClient": "yarn", "packages": [ "packages/*" From b86640772ba07a16d077d93973810a9c5f365aa4 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 29 Nov 2023 18:45:48 +0000 Subject: [PATCH 37/77] Fix for saving relationships that have the same field name used on both sides, previously this could cause a relationship to be cleared depending on how the relationship schema was configured. There is a chance when saving that this won't happen as which side of the relationship is denoted by doc1 and doc2 is random, so when this happens is random. Using the table to pick the correct side is safer than just using the field name. --- .../server/src/api/controllers/static/index.ts | 6 ++++-- .../server/src/db/linkedRows/LinkController.ts | 16 +++++++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/packages/server/src/api/controllers/static/index.ts b/packages/server/src/api/controllers/static/index.ts index 4c5415a6c6..2963546e7f 100644 --- a/packages/server/src/api/controllers/static/index.ts +++ b/packages/server/src/api/controllers/static/index.ts @@ -24,7 +24,7 @@ import AWS from "aws-sdk" import fs from "fs" import sdk from "../../../sdk" import * as pro from "@budibase/pro" -import { App, Ctx, ProcessAttachmentResponse, Upload } from "@budibase/types" +import { App, Ctx, ProcessAttachmentResponse } from "@budibase/types" const send = require("koa-send") @@ -212,7 +212,9 @@ export const serveBuilderPreview = async function (ctx: Ctx) { if (!env.isJest()) { let appId = context.getAppId() - const previewHbs = loadHandlebarsFile(`${__dirname}/preview.hbs`) + const templateLoc = join(__dirname, "templates") + const previewLoc = fs.existsSync(templateLoc) ? templateLoc : __dirname + const previewHbs = loadHandlebarsFile(join(previewLoc, "preview.hbs")) ctx.body = await processString(previewHbs, { clientLibPath: objectStore.clientLibraryUrl(appId!, appInfo.version), }) diff --git a/packages/server/src/db/linkedRows/LinkController.ts b/packages/server/src/db/linkedRows/LinkController.ts index c4eed1169a..f52694465f 100644 --- a/packages/server/src/db/linkedRows/LinkController.ts +++ b/packages/server/src/db/linkedRows/LinkController.ts @@ -251,9 +251,19 @@ class LinkController { // find the docs that need to be deleted let toDeleteDocs = thisFieldLinkDocs .filter(doc => { - let correctDoc = - doc.doc1.fieldName === fieldName ? doc.doc2 : doc.doc1 - return rowField.indexOf(correctDoc.rowId) === -1 + let correctDoc + if ( + doc.doc1.tableId === table._id! && + doc.doc1.fieldName === fieldName + ) { + correctDoc = doc.doc2 + } else if ( + doc.doc2.tableId === table._id! && + doc.doc2.fieldName === fieldName + ) { + correctDoc = doc.doc1 + } + return correctDoc && rowField.indexOf(correctDoc.rowId) === -1 }) .map(doc => { return { ...doc, _deleted: true } From 160fbf21258823fe0fd06941e9061fc4719699b7 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 29 Nov 2023 19:53:56 +0000 Subject: [PATCH 38/77] Adding test case and fixing issue that it revealed with external tables as well. --- .../server/src/api/routes/tests/row.spec.ts | 35 +++++++++++++++++++ .../src/sdk/app/tables/external/utils.ts | 12 ++++--- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index de49441f3a..ba80f36b1b 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -517,9 +517,22 @@ describe.each([ }) describe("patch", () => { + let otherTable: Table + beforeAll(async () => { const tableConfig = generateTableConfig() table = await createTable(tableConfig) + const otherTableConfig = generateTableConfig() + // need a short name of table here - for relationship tests + otherTableConfig.name = "a" + otherTableConfig.schema.relationship = { + name: "relationship", + type: FieldType.LINK, + fieldName: "relationship", + tableId: table._id!, + relationshipType: RelationshipType.ONE_TO_MANY, + } + otherTable = await createTable(otherTableConfig) }) it("should update only the fields that are supplied", async () => { @@ -615,6 +628,28 @@ describe.each([ expect(getResp.body.user1[0]._id).toEqual(user2._id) expect(getResp.body.user2[0]._id).toEqual(user2._id) }) + + it("should be able to update relationships when both columns are same name", async () => { + let row = await config.api.row.save(table._id!, { + name: "test", + description: "test", + }) + let row2 = await config.api.row.save(otherTable._id!, { + name: "test", + description: "test", + relationship: [row._id], + }) + row = (await config.api.row.get(table._id!, row._id!)).body + expect(row.relationship.length).toBe(1) + const resp = await config.api.row.patch(table._id!, { + _id: row._id!, + _rev: row._rev!, + tableId: row.tableId!, + name: "test2", + relationship: [row2._id], + }) + expect(resp.relationship.length).toBe(1) + }) }) describe("destroy", () => { diff --git a/packages/server/src/sdk/app/tables/external/utils.ts b/packages/server/src/sdk/app/tables/external/utils.ts index bde812dd3d..50ea98eb39 100644 --- a/packages/server/src/sdk/app/tables/external/utils.ts +++ b/packages/server/src/sdk/app/tables/external/utils.ts @@ -1,5 +1,6 @@ import { Datasource, + FieldType, ManyToManyRelationshipFieldMetadata, ManyToOneRelationshipFieldMetadata, OneToManyRelationshipFieldMetadata, @@ -42,10 +43,13 @@ export function cleanupRelationships( for (let [relatedKey, relatedSchema] of Object.entries( relatedTable.schema )) { - if ( - relatedSchema.type === FieldTypes.LINK && - relatedSchema.fieldName === foreignKey - ) { + if (relatedSchema.type !== FieldType.LINK) { + continue + } + // if they both have the same field name it will appear as if it needs to be removed, + // don't cleanup in this scenario + const sameFieldNameForBoth = relatedSchema.name === schema.name + if (relatedSchema.fieldName === foreignKey && !sameFieldNameForBoth) { delete relatedTable.schema[relatedKey] } } From 4fcb4d56776aee881d7351a01c43c39036c5170a Mon Sep 17 00:00:00 2001 From: Christos Alexiou Date: Wed, 29 Nov 2023 23:39:41 +0200 Subject: [PATCH 39/77] fix: use /health and remove 301 --- charts/budibase/templates/alb-ingress.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/budibase/templates/alb-ingress.yaml b/charts/budibase/templates/alb-ingress.yaml index 6cd1cf2cba..fd38364ba6 100644 --- a/charts/budibase/templates/alb-ingress.yaml +++ b/charts/budibase/templates/alb-ingress.yaml @@ -7,8 +7,8 @@ metadata: kubernetes.io/ingress.class: alb alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: ip - alb.ingress.kubernetes.io/success-codes: 200,301 - alb.ingress.kubernetes.io/healthcheck-path: / + alb.ingress.kubernetes.io/success-codes: '200' + alb.ingress.kubernetes.io/healthcheck-path: '/health' {{- if .Values.ingress.certificateArn }} alb.ingress.kubernetes.io/actions.ssl-redirect: '{"Type": "redirect", "RedirectConfig": { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_301"}}' alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]' From 3192a594c73528a7eca8561d1fe8e18ab929783b Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Thu, 30 Nov 2023 08:58:01 +0000 Subject: [PATCH 40/77] Allow querying of users table from automation --- .../src/components/automation/SetupPanel/TableSelector.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/components/automation/SetupPanel/TableSelector.svelte b/packages/builder/src/components/automation/SetupPanel/TableSelector.svelte index 3434219384..1645ded66b 100644 --- a/packages/builder/src/components/automation/SetupPanel/TableSelector.svelte +++ b/packages/builder/src/components/automation/SetupPanel/TableSelector.svelte @@ -22,7 +22,7 @@