diff --git a/packages/backend-core/src/constants/misc.ts b/packages/backend-core/src/constants/misc.ts
index aee099e10a..e2fd975e40 100644
--- a/packages/backend-core/src/constants/misc.ts
+++ b/packages/backend-core/src/constants/misc.ts
@@ -28,6 +28,7 @@ export enum Config {
OIDC = "oidc",
OIDC_LOGOS = "logos_oidc",
SCIM = "scim",
+ AI = "AI",
}
export const MIN_VALID_DATE = new Date(-2147483647000)
diff --git a/packages/backend-core/src/sql/sql.ts b/packages/backend-core/src/sql/sql.ts
index 949d7edf1b..2b75752aec 100644
--- a/packages/backend-core/src/sql/sql.ts
+++ b/packages/backend-core/src/sql/sql.ts
@@ -1351,7 +1351,8 @@ class InternalBuilder {
schema.constraints?.presence === true ||
schema.type === FieldType.FORMULA ||
schema.type === FieldType.AUTO ||
- schema.type === FieldType.LINK
+ schema.type === FieldType.LINK ||
+ schema.type === FieldType.AI
) {
continue
}
diff --git a/packages/backend-core/tests/core/utilities/mocks/licenses.ts b/packages/backend-core/tests/core/utilities/mocks/licenses.ts
index bc9a3b635c..5ba6fb36a1 100644
--- a/packages/backend-core/tests/core/utilities/mocks/licenses.ts
+++ b/packages/backend-core/tests/core/utilities/mocks/licenses.ts
@@ -102,6 +102,14 @@ export const useAppBuilders = () => {
return useFeature(Feature.APP_BUILDERS)
}
+export const useBudibaseAI = () => {
+ return useFeature(Feature.BUDIBASE_AI)
+}
+
+export const useAICustomConfigs = () => {
+ return useFeature(Feature.AI_CUSTOM_CONFIGS)
+}
+
// QUOTAS
export const setAutomationLogsQuota = (value: number) => {
diff --git a/packages/bbui/src/ActionButton/ActionButton.svelte b/packages/bbui/src/ActionButton/ActionButton.svelte
index d3cec0f307..2401354fbb 100644
--- a/packages/bbui/src/ActionButton/ActionButton.svelte
+++ b/packages/bbui/src/ActionButton/ActionButton.svelte
@@ -1,15 +1,11 @@
-
- (showTooltip = true)}
on:mouseleave={() => (showTooltip = false)}
on:focus={() => (showTooltip = true)}
+ {disabled}
+ style={accentStyle}
>
-
- {#if longPressable}
-
-
-
- {/if}
- {#if icon}
-
-
-
- {/if}
- {#if $$slots}
-
- {/if}
- {#if tooltip && showTooltip}
-
-
-
- {/if}
-
-
+ {#if icon}
+
+
+
+ {/if}
+ {#if $$slots}
+
+ {/if}
+ {#if tooltip && showTooltip}
+
diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte
index c88317c79f..aca3e950c3 100644
--- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte
+++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte
@@ -190,7 +190,7 @@
{#if isTrigger && triggerInfo}
{/if}
{#if lastStep}
diff --git a/packages/builder/src/components/automation/AutomationPanel/AutomationNavItem.svelte b/packages/builder/src/components/automation/AutomationPanel/AutomationNavItem.svelte
index 6e4d7c0099..ec9b956190 100644
--- a/packages/builder/src/components/automation/AutomationPanel/AutomationNavItem.svelte
+++ b/packages/builder/src/components/automation/AutomationPanel/AutomationNavItem.svelte
@@ -9,6 +9,7 @@
import { sdk } from "@budibase/shared-core"
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
import UpdateAutomationModal from "components/automation/AutomationPanel/UpdateAutomationModal.svelte"
+ import UpdateRowActionModal from "components/automation/AutomationPanel/UpdateRowActionModal.svelte"
import NavItem from "components/common/NavItem.svelte"
export let automation
@@ -16,12 +17,16 @@
let confirmDeleteDialog
let updateAutomationDialog
+ let updateRowActionDialog
+
+ $: isRowAction = sdk.automations.isRowAction(automation)
async function deleteAutomation() {
try {
await automationStore.actions.delete(automation)
notifications.success("Automation deleted successfully")
} catch (error) {
+ console.error(error)
notifications.error("Error deleting automation")
}
}
@@ -36,42 +41,7 @@
}
const getContextMenuItems = () => {
- const isRowAction = sdk.automations.isRowAction(automation)
- const result = []
- if (!isRowAction) {
- result.push(
- ...[
- {
- icon: "Delete",
- name: "Delete",
- keyBind: null,
- visible: true,
- disabled: false,
- callback: confirmDeleteDialog.show,
- },
- {
- icon: "Edit",
- name: "Edit",
- keyBind: null,
- visible: true,
- disabled: !automation.definition.trigger,
- callback: updateAutomationDialog.show,
- },
- {
- icon: "Duplicate",
- name: "Duplicate",
- keyBind: null,
- visible: true,
- disabled:
- !automation.definition.trigger ||
- automation.definition.trigger?.name === "Webhook",
- callback: duplicateAutomation,
- },
- ]
- )
- }
-
- result.push({
+ const pause = {
icon: automation.disabled ? "CheckmarkCircle" : "Cancel",
name: automation.disabled ? "Activate" : "Pause",
keyBind: null,
@@ -83,8 +53,50 @@
automation.disabled
)
},
- })
- return result
+ }
+ const del = {
+ icon: "Delete",
+ name: "Delete",
+ keyBind: null,
+ visible: true,
+ disabled: false,
+ callback: confirmDeleteDialog.show,
+ }
+ if (!isRowAction) {
+ return [
+ {
+ icon: "Edit",
+ name: "Edit",
+ keyBind: null,
+ visible: true,
+ disabled: !automation.definition.trigger,
+ callback: updateAutomationDialog.show,
+ },
+ {
+ icon: "Duplicate",
+ name: "Duplicate",
+ keyBind: null,
+ visible: true,
+ disabled:
+ !automation.definition.trigger ||
+ automation.definition.trigger?.name === "Webhook",
+ callback: duplicateAutomation,
+ },
+ pause,
+ del,
+ ]
+ } else {
+ return [
+ {
+ icon: "Edit",
+ name: "Edit",
+ keyBind: null,
+ visible: true,
+ callback: updateRowActionDialog.show,
+ },
+ del,
+ ]
+ }
}
const openContextMenu = e => {
@@ -99,7 +111,9 @@
-
-
-
+
{automation.name}?
This action cannot be undone.
-
-
+{#if isRowAction}
+
+{:else}
+
+{/if}
diff --git a/packages/builder/src/components/automation/AutomationPanel/AutomationPanel.svelte b/packages/builder/src/components/automation/AutomationPanel/AutomationPanel.svelte
index 58eebfdd3e..6b96c4ebf5 100644
--- a/packages/builder/src/components/automation/AutomationPanel/AutomationPanel.svelte
+++ b/packages/builder/src/components/automation/AutomationPanel/AutomationPanel.svelte
@@ -3,13 +3,21 @@
import { Modal, notifications, Layout } from "@budibase/bbui"
import NavHeader from "components/common/NavHeader.svelte"
import { onMount } from "svelte"
- import { automationStore } from "stores/builder"
+ import { automationStore, tables } from "stores/builder"
import AutomationNavItem from "./AutomationNavItem.svelte"
+ import { TriggerStepID } from "constants/backend/automations"
export let modal
export let webhookModal
let searchString
+ const dsTriggers = [
+ TriggerStepID.ROW_SAVED,
+ TriggerStepID.ROW_UPDATED,
+ TriggerStepID.ROW_DELETED,
+ TriggerStepID.ROW_ACTION,
+ ]
+
$: filteredAutomations = $automationStore.automations
.filter(automation => {
return (
@@ -29,19 +37,47 @@
return lowerA > lowerB ? 1 : -1
})
- $: groupedAutomations = filteredAutomations.reduce((acc, auto) => {
- const catName = auto.definition?.trigger?.event || "No Trigger"
- acc[catName] ??= {
- icon: auto.definition?.trigger?.icon || "AlertCircle",
- name: (auto.definition?.trigger?.name || "No Trigger").toUpperCase(),
- entries: [],
- }
- acc[catName].entries.push(auto)
- return acc
- }, {})
+ $: groupedAutomations = groupAutomations(filteredAutomations)
$: showNoResults = searchString && !filteredAutomations.length
+ const groupAutomations = automations => {
+ let groups = {}
+
+ for (let auto of automations) {
+ let category = null
+ let dataTrigger = false
+
+ // Group by datasource if possible
+ if (dsTriggers.includes(auto.definition?.trigger?.stepId)) {
+ if (auto.definition.trigger.inputs?.tableId) {
+ const tableId = auto.definition.trigger.inputs?.tableId
+ category = $tables.list.find(x => x._id === tableId)?.name
+ }
+ }
+ // Otherwise group by trigger
+ if (!category) {
+ category = auto.definition?.trigger?.name || "No Trigger"
+ } else {
+ dataTrigger = true
+ }
+ groups[category] ??= {
+ icon: auto.definition?.trigger?.icon || "AlertCircle",
+ name: category.toUpperCase(),
+ entries: [],
+ dataTrigger,
+ }
+ groups[category].entries.push(auto)
+ }
+
+ return Object.values(groups).sort((a, b) => {
+ if (a.dataTrigger === b.dataTrigger) {
+ return a.name < b.name ? -1 : 1
+ }
+ return a.dataTrigger ? -1 : 1
+ })
+ }
+
onMount(async () => {
try {
await automationStore.actions.fetch()
@@ -88,16 +124,22 @@
diff --git a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte
index bf5fd702cd..cbac1bf2ff 100644
--- a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte
+++ b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte
@@ -62,6 +62,7 @@
} from "@budibase/types"
import { FIELDS } from "constants/backend"
import PropField from "./PropField.svelte"
+ import { utils } from "@budibase/shared-core"
export let block
export let testData
@@ -96,8 +97,14 @@
$: memoEnvVariables.set($environment.variables)
$: memoBlock.set(block)
- $: filters = lookForFilters(schemaProperties) || []
- $: tempFilters = filters
+ $: filters = lookForFilters(schemaProperties)
+ $: filterCount =
+ filters?.groups?.reduce((acc, group) => {
+ acc = acc += group?.filters?.length || 0
+ return acc
+ }, 0) || 0
+
+ $: tempFilters = cloneDeep(filters)
$: stepId = $memoBlock.stepId
$: automationBindings = getAvailableBindings(
@@ -791,14 +798,13 @@
break
}
}
- return filters || []
+ return utils.processSearchFilters(filters)
}
function saveFilters(key) {
- const filters = QueryUtils.buildQuery(tempFilters)
-
+ const query = QueryUtils.buildQuery(tempFilters)
onChange({
- [key]: filters,
+ [key]: query,
[`${key}-def`]: tempFilters, // need to store the builder definition in the automation
})
@@ -1027,18 +1033,24 @@
{:else if value.customType === AutomationCustomIOType.FILTERS || value.customType === AutomationCustomIOType.TRIGGER_FILTER}
-