diff --git a/packages/bbui/src/Form/FieldLabel.svelte b/packages/bbui/src/Form/FieldLabel.svelte
index b070df8cae..3606d77c7b 100644
--- a/packages/bbui/src/Form/FieldLabel.svelte
+++ b/packages/bbui/src/Form/FieldLabel.svelte
@@ -1,19 +1,24 @@
-
+
+
+
diff --git a/packages/bbui/src/Tooltip/TooltipWrapper.svelte b/packages/bbui/src/Tooltip/TooltipWrapper.svelte
new file mode 100644
index 0000000000..c587dec1dc
--- /dev/null
+++ b/packages/bbui/src/Tooltip/TooltipWrapper.svelte
@@ -0,0 +1,60 @@
+
+
+
+
+ {#if tooltip}
+
+
(showTooltip = true)}
+ on:mouseleave={() => (showTooltip = false)}
+ >
+
+
+ {#if showTooltip}
+
+
+
+ {/if}
+
+ {/if}
+
+
+
diff --git a/packages/builder/cypress/setup.js b/packages/builder/cypress/setup.js
index a6e1220fd4..a1f0e3dbf6 100644
--- a/packages/builder/cypress/setup.js
+++ b/packages/builder/cypress/setup.js
@@ -40,7 +40,7 @@ async function run() {
// it will cause environment module to be loaded prematurely
const server = require("../../server/dist/app")
process.env.PORT = WORKER_PORT
- const worker = require("../../worker/src/index")
+ const worker = require("../../worker/dist/index")
// reload main port for rest of system
process.env.PORT = MAIN_PORT
server.on("close", () => console.log("Server Closed"))
diff --git a/packages/builder/cypress/support/commands.js b/packages/builder/cypress/support/commands.js
index e67057344a..d99e2b271f 100644
--- a/packages/builder/cypress/support/commands.js
+++ b/packages/builder/cypress/support/commands.js
@@ -35,7 +35,13 @@ Cypress.Commands.add("login", () => {
Cypress.Commands.add("createApp", name => {
cy.visit(`localhost:${Cypress.env("PORT")}/builder`)
cy.wait(500)
- cy.contains(/Start from scratch/).dblclick()
+ cy.request(`${Cypress.config().baseUrl}api/applications?status=all`)
+ .its("body")
+ .then(body => {
+ if (body.length > 0) {
+ cy.get(".spectrum-Button").contains("Create app").click({ force: true })
+ }
+ })
cy.get(".spectrum-Modal").within(() => {
cy.get("input").eq(0).type(name).should("have.value", name).blur()
cy.get(".spectrum-ButtonGroup").contains("Create app").click()
diff --git a/packages/builder/package.json b/packages/builder/package.json
index 189497dd73..bfc8d4395b 100644
--- a/packages/builder/package.json
+++ b/packages/builder/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/builder",
- "version": "1.0.43",
+ "version": "1.0.49-alpha.5",
"license": "GPL-3.0",
"private": true,
"scripts": {
@@ -65,10 +65,10 @@
}
},
"dependencies": {
- "@budibase/bbui": "^1.0.43",
- "@budibase/client": "^1.0.43",
+ "@budibase/bbui": "^1.0.49-alpha.5",
+ "@budibase/client": "^1.0.49-alpha.5",
"@budibase/colorpicker": "1.1.2",
- "@budibase/string-templates": "^1.0.43",
+ "@budibase/string-templates": "^1.0.49-alpha.5",
"@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1",
diff --git a/packages/builder/src/builderStore/api.js b/packages/builder/src/builderStore/api.js
index 897d3a74db..a932799701 100644
--- a/packages/builder/src/builderStore/api.js
+++ b/packages/builder/src/builderStore/api.js
@@ -1,11 +1,20 @@
import { store } from "./index"
import { get as svelteGet } from "svelte/store"
import { removeCookie, Cookies } from "./cookies"
+import { auth } from "stores/portal"
const apiCall =
method =>
async (url, body, headers = { "Content-Type": "application/json" }) => {
headers["x-budibase-app-id"] = svelteGet(store).appId
+ headers["x-budibase-api-version"] = "1"
+
+ // add csrf token if authenticated
+ const user = svelteGet(auth).user
+ if (user && user.csrfToken) {
+ headers["x-csrf-token"] = user.csrfToken
+ }
+
const json = headers["Content-Type"] === "application/json"
const resp = await fetch(url, {
method: method,
diff --git a/packages/builder/src/builderStore/cookies.js b/packages/builder/src/builderStore/cookies.js
index a84f1a4f20..cb4e46ec86 100644
--- a/packages/builder/src/builderStore/cookies.js
+++ b/packages/builder/src/builderStore/cookies.js
@@ -1,16 +1,26 @@
export const Cookies = {
Auth: "budibase:auth",
CurrentApp: "budibase:currentapp",
+ ReturnUrl: "budibase:returnurl",
+}
+
+export function setCookie(name, value) {
+ if (getCookie(name)) {
+ removeCookie(name)
+ }
+ window.document.cookie = `${name}=${value}; Path=/;`
}
export function getCookie(cookieName) {
- return document.cookie.split(";").some(cookie => {
- return cookie.trim().startsWith(`${cookieName}=`)
- })
+ const value = `; ${document.cookie}`
+ const parts = value.split(`; ${cookieName}=`)
+ if (parts.length === 2) {
+ return parts[1].split(";").shift()
+ }
}
export function removeCookie(cookieName) {
if (getCookie(cookieName)) {
- document.cookie = `${cookieName}=; Max-Age=-99999999;`
+ document.cookie = `${cookieName}=; Max-Age=-99999999; Path=/;`
}
}
diff --git a/packages/builder/src/builderStore/index.js b/packages/builder/src/builderStore/index.js
index 23704556ad..5181e756c6 100644
--- a/packages/builder/src/builderStore/index.js
+++ b/packages/builder/src/builderStore/index.js
@@ -1,6 +1,5 @@
import { getFrontendStore } from "./store/frontend"
import { getAutomationStore } from "./store/automation"
-import { getHostingStore } from "./store/hosting"
import { getThemeStore } from "./store/theme"
import { derived, writable } from "svelte/store"
import { FrontendTypes, LAYOUT_NAMES } from "../constants"
@@ -9,7 +8,6 @@ import { findComponent } from "./componentUtils"
export const store = getFrontendStore()
export const automationStore = getAutomationStore()
export const themeStore = getThemeStore()
-export const hostingStore = getHostingStore()
export const currentAsset = derived(store, $store => {
const type = $store.currentFrontEndType
diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js
index fdfe450edf..0d740e08e0 100644
--- a/packages/builder/src/builderStore/store/frontend.js
+++ b/packages/builder/src/builderStore/store/frontend.js
@@ -2,7 +2,6 @@ import { get, writable } from "svelte/store"
import { cloneDeep } from "lodash/fp"
import {
allScreens,
- hostingStore,
currentAsset,
mainLayout,
selectedComponent,
@@ -100,7 +99,6 @@ export const getFrontendStore = () => {
version: application.version,
revertableVersion: application.revertableVersion,
}))
- await hostingStore.actions.fetch()
// Initialise backend stores
const [_integrations] = await Promise.all([
diff --git a/packages/builder/src/builderStore/store/hosting.js b/packages/builder/src/builderStore/store/hosting.js
deleted file mode 100644
index fb174c2663..0000000000
--- a/packages/builder/src/builderStore/store/hosting.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import { writable } from "svelte/store"
-import api, { get } from "../api"
-
-const INITIAL_HOSTING_UI_STATE = {
- appUrl: "",
- deployedApps: {},
- deployedAppNames: [],
- deployedAppUrls: [],
-}
-
-export const getHostingStore = () => {
- const store = writable({ ...INITIAL_HOSTING_UI_STATE })
- store.actions = {
- fetch: async () => {
- const response = await api.get("/api/hosting/urls")
- const urls = await response.json()
- store.update(state => {
- state.appUrl = urls.app
- return state
- })
- },
- fetchDeployedApps: async () => {
- let deployments = await (await get("/api/hosting/apps")).json()
- store.update(state => {
- state.deployedApps = deployments
- state.deployedAppNames = Object.values(deployments).map(app => app.name)
- state.deployedAppUrls = Object.values(deployments).map(app => app.url)
- return state
- })
- return deployments
- },
- }
- return store
-}
diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte
index 752f291019..0d73f3d36d 100644
--- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte
+++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte
@@ -22,8 +22,10 @@
RelationshipTypes,
ALLOWABLE_STRING_OPTIONS,
ALLOWABLE_NUMBER_OPTIONS,
+ ALLOWABLE_JSON_OPTIONS,
ALLOWABLE_STRING_TYPES,
ALLOWABLE_NUMBER_TYPES,
+ ALLOWABLE_JSON_TYPES,
SWITCHABLE_TYPES,
} from "constants/backend"
import { getAutoColumnInformation, buildAutoColumn } from "builderStore/utils"
@@ -126,7 +128,12 @@
}
}
+ function cancelEdit() {
+ field.name = originalName
+ }
+
function deleteColumn() {
+ field.name = deleteColName
if (field.name === $tables.selected.primaryDisplay) {
notifications.error("You cannot delete the display column")
} else {
@@ -145,6 +152,7 @@
delete field.subtype
delete field.tableId
delete field.relationshipType
+ delete field.formulaType
// Add in defaults and initial definition
const definition = fieldDefinitions[event.detail?.toUpperCase()]
@@ -156,6 +164,9 @@
if (field.type === LINK_TYPE) {
field.relationshipType = RelationshipTypes.MANY_TO_MANY
}
+ if (field.type === FORMULA_TYPE) {
+ field.formulaType = "dynamic"
+ }
}
function onChangeRequired(e) {
@@ -236,6 +247,11 @@
ALLOWABLE_NUMBER_TYPES.indexOf(field.type) !== -1
) {
return ALLOWABLE_NUMBER_OPTIONS
+ } else if (
+ originalName &&
+ ALLOWABLE_JSON_TYPES.indexOf(field.type) !== -1
+ ) {
+ return ALLOWABLE_JSON_OPTIONS
} else if (!external) {
return [
...Object.values(fieldDefinitions),
@@ -307,6 +323,7 @@
title={originalName ? "Edit Column" : "Create Column"}
confirmText="Save Column"
onConfirm={saveColumn}
+ onCancel={cancelEdit}
disabled={invalid}
>
{:else if field.type === FORMULA_TYPE}
+ {#if !table.sql}
+
@@ -118,9 +141,9 @@
diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/GoogleSheets.svelte b/packages/builder/src/components/backend/DatasourceNavigator/icons/GoogleSheets.svelte
new file mode 100644
index 0000000000..0d376e4400
--- /dev/null
+++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/GoogleSheets.svelte
@@ -0,0 +1,184 @@
+
+
+
diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js b/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js
index 56ae03dcc3..350fccf73f 100644
--- a/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js
+++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js
@@ -11,6 +11,7 @@ import ArangoDB from "./ArangoDB.svelte"
import Rest from "./Rest.svelte"
import Budibase from "./Budibase.svelte"
import Oracle from "./Oracle.svelte"
+import GoogleSheets from "./GoogleSheets.svelte"
export default {
BUDIBASE: Budibase,
@@ -26,4 +27,5 @@ export default {
ARANGODB: ArangoDB,
REST: Rest,
ORACLE: Oracle,
+ GOOGLE_SHEETS: GoogleSheets,
}
diff --git a/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte b/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte
index dc5831b905..71df33b967 100644
--- a/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte
+++ b/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte
@@ -6,6 +6,7 @@
import { IntegrationNames, IntegrationTypes } from "constants/backend"
import CreateTableModal from "components/backend/TableNavigator/modals/CreateTableModal.svelte"
import DatasourceConfigModal from "components/backend/DatasourceNavigator/modals/DatasourceConfigModal.svelte"
+ import GoogleDatasourceConfigModal from "components/backend/DatasourceNavigator/modals/GoogleDatasourceConfigModal.svelte"
import { createRestDatasource } from "builderStore/datasource"
import { goto } from "@roxi/routify"
import ImportRestQueriesModal from "./ImportRestQueriesModal.svelte"
@@ -38,6 +39,7 @@
plus: selected.plus,
config,
schema: selected.datasource,
+ auth: selected.auth,
}
checkShowImport()
}
@@ -79,7 +81,11 @@
-
+ {#if integration?.auth?.type === "google"}
+
+ {:else}
+
+ {/if}
diff --git a/packages/builder/src/components/backend/DatasourceNavigator/modals/DatasourceConfigModal.svelte b/packages/builder/src/components/backend/DatasourceNavigator/modals/DatasourceConfigModal.svelte
index da8c0515b7..97168358cf 100644
--- a/packages/builder/src/components/backend/DatasourceNavigator/modals/DatasourceConfigModal.svelte
+++ b/packages/builder/src/components/backend/DatasourceNavigator/modals/DatasourceConfigModal.svelte
@@ -51,13 +51,9 @@
>Connect your database to Budibase using the config below.