diff --git a/.github/stale.yml b/.github/stale.yml deleted file mode 100644 index 2a2c10cb7d..0000000000 --- a/.github/stale.yml +++ /dev/null @@ -1,19 +0,0 @@ -# Configuration for probot-stale - https://github.com/probot/stale -# Number of days of inactivity before an Issue or Pull Request becomes stale -daysUntilStale: 60 -# Number of days of inactivity before an Issue or Pull Request with the stale label is closed. -# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. -daysUntilClose: false -# Issues with these labels will never be considered stale -exemptLabels: - - pinned - - security - - roadmap -# Label to use when marking an issue as stale -staleLabel: stale -# Comment to post when marking an issue as stale. Set to `false` to disable -markComment: > - This issue has been automatically marked as stale because it has not had - recent activity. -# Comment to post when closing a stale issue. Set to `false` to disable -closeComment: false diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index 659dbc4974..9509a22e99 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -12,9 +12,6 @@ on: - master - develop pull_request: - branches: - - master - - develop workflow_dispatch: env: @@ -162,7 +159,7 @@ jobs: run: | cd qa-core yarn setup - yarn test:ci + yarn serve:test:self:ci env: BB_ADMIN_USER_EMAIL: admin BB_ADMIN_USER_PASSWORD: admin diff --git a/.github/workflows/release-develop.yml b/.github/workflows/release-develop.yml index a7bf041eb5..e8d88fa136 100644 --- a/.github/workflows/release-develop.yml +++ b/.github/workflows/release-develop.yml @@ -6,7 +6,7 @@ concurrency: on: push: tags: - - v*-alpha.* + - ".*-alpha.*" workflow_dispatch: env: diff --git a/.github/workflows/release-master.yml b/.github/workflows/release-master.yml index 9a86f50c04..7f8b8f1d55 100644 --- a/.github/workflows/release-master.yml +++ b/.github/workflows/release-master.yml @@ -6,9 +6,9 @@ concurrency: on: push: tags: - - "v[0-9]+.[0-9]+.[0-9]+" + - "[0-9]+.[0-9]+.[0-9]+" # Exclude all pre-releases - - "!v*[0-9]+.[0-9]+.[0-9]+-*" + - "!*[0-9]+.[0-9]+.[0-9]+-*" env: # Posthog token used by ui at build time @@ -98,7 +98,7 @@ jobs: git fetch mkdir sync echo "Packaging chart to sync dir" - helm package charts/budibase --version 0.0.0-master --app-version v"$RELEASE_VERSION" --destination sync + helm package charts/budibase --version 0.0.0-master --app-version "$RELEASE_VERSION" --destination sync echo "Packaging successful" git checkout gh-pages echo "Indexing helm repo" diff --git a/.github/workflows/release-selfhost.yml b/.github/workflows/release-selfhost.yml index f4524e99dc..39ee812726 100644 --- a/.github/workflows/release-selfhost.yml +++ b/.github/workflows/release-selfhost.yml @@ -43,7 +43,7 @@ jobs: run: | docker login -u $DOCKER_USER -p $DOCKER_PASSWORD - release_tag=v${{ env.RELEASE_VERSION }} + release_tag=${{ env.RELEASE_VERSION }} # Pull apps and worker images docker pull budibase/apps:$release_tag @@ -108,8 +108,8 @@ jobs: - name: Perform Github Release uses: softprops/action-gh-release@v1 with: - name: v${{ env.RELEASE_VERSION }} - tag_name: v${{ env.RELEASE_VERSION }} + name: ${{ env.RELEASE_VERSION }} + tag_name: ${{ env.RELEASE_VERSION }} generate_release_notes: true files: | packages/cli/build/cli-win.exe diff --git a/.github/workflows/release-singleimage.yml b/.github/workflows/release-singleimage.yml index 5408b48ef8..92f21bd649 100644 --- a/.github/workflows/release-singleimage.yml +++ b/.github/workflows/release-singleimage.yml @@ -71,7 +71,7 @@ jobs: context: . push: true platforms: linux/amd64,linux/arm64 - tags: budibase/budibase,budibase/budibase:v${{ env.RELEASE_VERSION }} + tags: budibase/budibase,budibase/budibase:${{ env.RELEASE_VERSION }} file: ./hosting/single/Dockerfile - name: Tag and release Budibase Azure App Service docker image uses: docker/build-push-action@v2 @@ -80,5 +80,5 @@ jobs: push: true platforms: linux/amd64 build-args: TARGETBUILD=aas - tags: budibase/budibase-aas,budibase/budibase-aas:v${{ env.RELEASE_VERSION }} + tags: budibase/budibase-aas,budibase/budibase-aas:${{ env.RELEASE_VERSION }} file: ./hosting/single/Dockerfile diff --git a/.github/workflows/stale_bot.yml b/.github/workflows/stale_bot.yml new file mode 100644 index 0000000000..8cda3a9342 --- /dev/null +++ b/.github/workflows/stale_bot.yml @@ -0,0 +1,29 @@ +name: Close stale issues and PRs # https://github.com/actions/stale +on: + workflow_dispatch: + schedule: + - cron: '30 1 * * *' # 1:30 every morning + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v8 + with: + # stale rules + days-before-stale: 60 + days-before-pr-stale: 7 + stale-issue-label: stale + stale-issue-message: "This issue has been automatically marked as stale because it has not had any activity for 60 days." + + # close rules + # days after being marked as stale to close + days-before-close: 30 + close-issue-label: closed-stale + close-issue-message: This issue has been automatically closed it has not had any activity in 90 days." + days-before-pr-close: 7 + + # exemptions + exempt-pr-labels: pinned,security,roadmap + + diff --git a/.tool-versions b/.tool-versions index 094292d096..da92e03885 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -nodejs 14.20.1 +nodejs 14.21.3 python 3.10.0 \ No newline at end of file diff --git a/charts/budibase/values.yaml b/charts/budibase/values.yaml index 2d89e81b7f..74e4c52654 100644 --- a/charts/budibase/values.yaml +++ b/charts/budibase/values.yaml @@ -209,7 +209,7 @@ services: # Override values in couchDB subchart couchdb: ## clusterSize is the initial size of the CouchDB cluster. - clusterSize: 3 + clusterSize: 1 allowAdminParty: false # Secret Management diff --git a/hosting/.env b/hosting/.env index c2b6d55eef..8a0756c0e3 100644 --- a/hosting/.env +++ b/hosting/.env @@ -28,3 +28,4 @@ BB_ADMIN_USER_PASSWORD= # A path that is watched for plugin bundles. Any bundles found are imported automatically/ PLUGINS_DIR= +ROLLING_LOG_MAX_SIZE= \ No newline at end of file diff --git a/hosting/single/nginx/nginx-default-site.conf b/hosting/single/nginx/nginx-default-site.conf index 3903c0647d..d2f8e229c6 100644 --- a/hosting/single/nginx/nginx-default-site.conf +++ b/hosting/single/nginx/nginx-default-site.conf @@ -22,6 +22,16 @@ server { proxy_pass http://127.0.0.1:4001; } + location /embed { + rewrite /embed/(.*) /app/$1 break; + proxy_pass http://127.0.0.1:4001; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header x-budibase-embed "true"; + add_header x-budibase-embed "true"; + add_header Content-Security-Policy "frame-ancestors *"; + } + location = / { proxy_pass http://127.0.0.1:4001; } diff --git a/lerna.json b/lerna.json index 5c20645fec..070a50bdac 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.8.2-alpha.5", + "version": "2.8.16-alpha.3", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/nx.json b/nx.json index fc0712eed4..3df61886c2 100644 --- a/nx.json +++ b/nx.json @@ -1,9 +1,13 @@ { "tasksRunnerOptions": { "default": { - "runner": "nx/tasks-runners/default", + "runner": "nx-cloud", "options": { - "cacheableOperations": ["build", "test"] + "cacheableOperations": [ + "build", + "test" + ], + "accessToken": "YWNiYzc5NTEtMzMzZC00NDhjLTgyNjktZTllMjI1MzM4OGQxfHJlYWQtd3JpdGU=" } } }, @@ -11,7 +15,9 @@ "dev:builder": { "dependsOn": [ { - "projects": ["@budibase/string-templates"], + "projects": [ + "@budibase/string-templates" + ], "target": "build" } ] diff --git a/package.json b/package.json index a4b830d40b..3afe279e00 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "private": true, "devDependencies": { "@esbuild-plugins/tsconfig-paths": "^0.1.2", - "@nx/js": "16.2.1", + "@nx/js": "16.4.3", "@rollup/plugin-json": "^4.0.2", "@typescript-eslint/parser": "5.45.0", "esbuild": "^0.17.18", @@ -13,9 +13,11 @@ "husky": "^8.0.3", "js-yaml": "^4.1.0", "kill-port": "^1.6.1", - "lerna": "7.0.2", + "lerna": "7.1.1", "madge": "^6.0.0", "minimist": "^1.2.8", + "nx": "16.4.3", + "nx-cloud": "16.0.5", "prettier": "2.8.8", "prettier-plugin-svelte": "^2.3.0", "rimraf": "^3.0.2", @@ -44,15 +46,15 @@ "restore": "yarn run clean && yarn run bootstrap && yarn run build", "nuke": "yarn run nuke:packages && yarn run nuke:docker", "nuke:packages": "yarn run restore", - "nuke:docker": "lerna run --stream --parallel dev:stack:nuke", + "nuke:docker": "lerna run --stream dev:stack:nuke", "clean": "lerna clean", "kill-builder": "kill-port 3000", "kill-server": "kill-port 4001 4002", "kill-all": "yarn run kill-builder && yarn run kill-server", - "dev": "yarn run kill-all && lerna run --stream --parallel dev:builder --stream", - "dev:noserver": "yarn run kill-builder && lerna run --stream dev:stack:up && lerna run --stream --parallel dev:builder --ignore @budibase/backend-core --ignore @budibase/server --ignore @budibase/worker", - "dev:server": "yarn run kill-server && yarn build --projects=@budibase/client && lerna run --stream --parallel dev:builder --scope @budibase/worker --scope @budibase/server", - "dev:built": "yarn run kill-all && cd packages/server && yarn dev:stack:up && cd ../../ && lerna run --stream --parallel dev:built", + "dev": "yarn run kill-all && lerna run --stream dev:builder --stream", + "dev:noserver": "yarn run kill-builder && lerna run --stream dev:stack:up && lerna run --stream dev:builder --ignore @budibase/backend-core --ignore @budibase/server --ignore @budibase/worker", + "dev:server": "yarn run kill-server && lerna run --stream dev:builder --scope @budibase/worker --scope @budibase/server", + "dev:built": "yarn run kill-all && cd packages/server && yarn dev:stack:up && cd ../../ && lerna run --stream dev:built", "dev:docker": "yarn build:docker:pre && docker-compose -f hosting/docker-compose.build.yaml -f hosting/docker-compose.dev.yaml --env-file hosting/.env up --build --scale proxy-service=0", "test": "lerna run --stream test --stream", "lint:eslint": "eslint packages qa-core --max-warnings=0", @@ -106,6 +108,5 @@ "@budibase/string-templates": "0.0.0", "@budibase/types": "0.0.0" }, - "dependencies": { - } + "dependencies": {} } diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 4a1ed5c373..7f3c064c92 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -51,6 +51,7 @@ "pouchdb": "7.3.0", "pouchdb-find": "7.2.2", "redlock": "4.2.0", + "rotating-file-stream": "3.1.0", "sanitize-s3-objectkey": "0.0.1", "semver": "7.3.7", "tar-fs": "2.1.1", diff --git a/packages/backend-core/src/auth/auth.ts b/packages/backend-core/src/auth/auth.ts index fb2fd2cf51..0100a2d0e2 100644 --- a/packages/backend-core/src/auth/auth.ts +++ b/packages/backend-core/src/auth/auth.ts @@ -159,7 +159,7 @@ export async function updateUserOAuth(userId: string, oAuthConfig: any) { try { const db = getGlobalDB() - const dbUser = await db.get(userId) + const dbUser = await db.get(userId) //Do not overwrite the refresh token if a valid one is not provided. if (typeof details.refreshToken !== "string") { diff --git a/packages/backend-core/src/cache/user.ts b/packages/backend-core/src/cache/user.ts index b514c3af9b..8281bfca62 100644 --- a/packages/backend-core/src/cache/user.ts +++ b/packages/backend-core/src/cache/user.ts @@ -12,7 +12,7 @@ const EXPIRY_SECONDS = 3600 */ async function populateFromDB(userId: string, tenantId: string) { const db = tenancy.getTenantDB(tenantId) - const user = await db.get(userId) + const user = await db.get(userId) user.budibaseAccess = true if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { const account = await accounts.getAccount(user.email) diff --git a/packages/backend-core/src/db/lucene.ts b/packages/backend-core/src/db/lucene.ts index d501bb2166..a491451a62 100644 --- a/packages/backend-core/src/db/lucene.ts +++ b/packages/backend-core/src/db/lucene.ts @@ -433,6 +433,9 @@ export class QueryBuilder { if (!value) { return null } + if (typeof value === "boolean") { + return `(*:* AND !${key}:${value})` + } return `!${key}:${builder.preprocess(value, allPreProcessingOpts)}` }) } diff --git a/packages/backend-core/src/db/searchIndexes/searchIndexes.ts b/packages/backend-core/src/db/searchIndexes/searchIndexes.ts index f03259b47f..b953e3516e 100644 --- a/packages/backend-core/src/db/searchIndexes/searchIndexes.ts +++ b/packages/backend-core/src/db/searchIndexes/searchIndexes.ts @@ -5,7 +5,7 @@ export async function createUserIndex() { const db = getGlobalDB() let designDoc try { - designDoc = await db.get("_design/database") + designDoc = await db.get("_design/database") } catch (err: any) { if (err.status === 404) { designDoc = { _id: "_design/database" } diff --git a/packages/backend-core/src/environment.ts b/packages/backend-core/src/environment.ts index c0785ef419..b8d2eb2a54 100644 --- a/packages/backend-core/src/environment.ts +++ b/packages/backend-core/src/environment.ts @@ -47,7 +47,10 @@ function httpLogging() { return process.env.HTTP_LOGGING } -function findVersion() { +function getPackageJsonFields(): { + VERSION: string + SERVICE_NAME: string +} { function findFileInAncestors( fileName: string, currentDir: string @@ -69,10 +72,14 @@ function findVersion() { try { const packageJsonFile = findFileInAncestors("package.json", process.cwd()) const content = readFileSync(packageJsonFile!, "utf-8") - return JSON.parse(content).version + const parsedContent = JSON.parse(content) + return { + VERSION: parsedContent.version, + SERVICE_NAME: parsedContent.name, + } } catch { // throwing an error here is confusing/causes backend-core to be hard to import - return undefined + return { VERSION: "", SERVICE_NAME: "" } } } @@ -154,7 +161,7 @@ const environment = { ENABLE_SSO_MAINTENANCE_MODE: selfHosted ? process.env.ENABLE_SSO_MAINTENANCE_MODE : false, - VERSION: findVersion(), + ...getPackageJsonFields(), DISABLE_PINO_LOGGER: process.env.DISABLE_PINO_LOGGER, OFFLINE_MODE: process.env.OFFLINE_MODE, _set(key: any, value: any) { @@ -162,6 +169,7 @@ const environment = { // @ts-ignore environment[key] = value }, + ROLLING_LOG_MAX_SIZE: process.env.ROLLING_LOG_MAX_SIZE || "10M", } // clean up any environment variable edge cases diff --git a/packages/backend-core/src/logging/index.ts b/packages/backend-core/src/logging/index.ts index b87062c478..0824fa681b 100644 --- a/packages/backend-core/src/logging/index.ts +++ b/packages/backend-core/src/logging/index.ts @@ -1,6 +1,4 @@ export * as correlation from "./correlation/correlation" export { logger } from "./pino/logger" export * from "./alerts" - -// turn off or on context logging i.e. tenantId, appId etc -export let LOG_CONTEXT = true +export * as system from "./system" diff --git a/packages/backend-core/src/logging/pino/logger.ts b/packages/backend-core/src/logging/pino/logger.ts index c96bc83e04..f2024db72b 100644 --- a/packages/backend-core/src/logging/pino/logger.ts +++ b/packages/backend-core/src/logging/pino/logger.ts @@ -1,9 +1,12 @@ -import env from "../../environment" import pino, { LoggerOptions } from "pino" +import pinoPretty from "pino-pretty" + +import { IdentityType } from "@budibase/types" +import env from "../../environment" import * as context from "../../context" import * as correlation from "../correlation" -import { IdentityType } from "@budibase/types" -import { LOG_CONTEXT } from "../index" + +import { localFileDestination } from "../system" // LOGGER @@ -16,22 +19,27 @@ if (!env.DISABLE_PINO_LOGGER) { return { level: label.toUpperCase() } }, bindings: () => { - return {} + return { + service: env.SERVICE_NAME, + } }, }, timestamp: () => `,"timestamp":"${new Date(Date.now()).toISOString()}"`, } + const destinations: pino.DestinationStream[] = [] + if (env.isDev()) { - pinoOptions.transport = { - target: "pino-pretty", - options: { - singleLine: true, - }, - } + destinations.push(pinoPretty({ singleLine: true })) } - pinoInstance = pino(pinoOptions) + if (env.SELF_HOSTED) { + destinations.push(localFileDestination()) + } + + pinoInstance = destinations.length + ? pino(pinoOptions, pino.multistream(destinations)) + : pino(pinoOptions) // CONSOLE OVERRIDES @@ -83,15 +91,13 @@ if (!env.DISABLE_PINO_LOGGER) { let contextObject = {} - if (LOG_CONTEXT) { - contextObject = { - tenantId: getTenantId(), - appId: getAppId(), - automationId: getAutomationId(), - identityId: identity?._id, - identityType: identity?.type, - correlationId: correlation.getId(), - } + contextObject = { + tenantId: getTenantId(), + appId: getAppId(), + automationId: getAutomationId(), + identityId: identity?._id, + identityType: identity?.type, + correlationId: correlation.getId(), } const mergingObject: any = { diff --git a/packages/backend-core/src/logging/system.ts b/packages/backend-core/src/logging/system.ts new file mode 100644 index 0000000000..d918c6efd6 --- /dev/null +++ b/packages/backend-core/src/logging/system.ts @@ -0,0 +1,81 @@ +import fs from "fs" +import path from "path" +import * as rfs from "rotating-file-stream" + +import env from "../environment" +import { budibaseTempDir } from "../objectStore" + +const logsFileName = `budibase.log` +const budibaseLogsHistoryFileName = "budibase-logs-history.txt" + +const logsPath = path.join(budibaseTempDir(), "systemlogs") + +function getFullPath(fileName: string) { + return path.join(logsPath, fileName) +} + +export function getSingleFileMaxSizeInfo(totalMaxSize: string) { + const regex = /(\d+)([A-Za-z])/ + const match = totalMaxSize?.match(regex) + if (!match) { + console.warn(`totalMaxSize does not have a valid value`, { + totalMaxSize, + }) + return undefined + } + + const size = +match[1] + const unit = match[2] + if (size === 1) { + switch (unit) { + case "B": + return { size: `${size}B`, totalHistoryFiles: 1 } + case "K": + return { size: `${(size * 1000) / 2}B`, totalHistoryFiles: 1 } + case "M": + return { size: `${(size * 1000) / 2}K`, totalHistoryFiles: 1 } + case "G": + return { size: `${(size * 1000) / 2}M`, totalHistoryFiles: 1 } + default: + return undefined + } + } + + if (size % 2 === 0) { + return { size: `${size / 2}${unit}`, totalHistoryFiles: 1 } + } + + return { size: `1${unit}`, totalHistoryFiles: size - 1 } +} + +export function localFileDestination() { + const fileInfo = getSingleFileMaxSizeInfo(env.ROLLING_LOG_MAX_SIZE) + const outFile = rfs.createStream(logsFileName, { + // As we have a rolling size, we want to half the max size + size: fileInfo?.size, + path: logsPath, + maxFiles: fileInfo?.totalHistoryFiles || 1, + immutable: true, + history: budibaseLogsHistoryFileName, + initialRotation: false, + }) + + return outFile +} + +export function getLogReadStream() { + const streams = [] + const historyFile = getFullPath(budibaseLogsHistoryFileName) + if (fs.existsSync(historyFile)) { + const fileContent = fs.readFileSync(historyFile, "utf-8") + const historyFiles = fileContent.split("\n") + for (const historyFile of historyFiles.filter(x => x)) { + streams.push(fs.readFileSync(historyFile)) + } + } + + streams.push(fs.readFileSync(getFullPath(logsFileName))) + + const combinedContent = Buffer.concat(streams) + return combinedContent +} diff --git a/packages/backend-core/src/logging/tests/system.spec.ts b/packages/backend-core/src/logging/tests/system.spec.ts new file mode 100644 index 0000000000..b84d8e8456 --- /dev/null +++ b/packages/backend-core/src/logging/tests/system.spec.ts @@ -0,0 +1,61 @@ +import { getSingleFileMaxSizeInfo } from "../system" + +describe("system", () => { + describe("getSingleFileMaxSizeInfo", () => { + it.each([ + ["100B", "50B"], + ["200K", "100K"], + ["20M", "10M"], + ["4G", "2G"], + ])( + "Halving even number (%s) returns halved size and 1 history file (%s)", + (totalValue, expectedMaxSize) => { + const result = getSingleFileMaxSizeInfo(totalValue) + expect(result).toEqual({ + size: expectedMaxSize, + totalHistoryFiles: 1, + }) + } + ) + + it.each([ + ["5B", "1B", 4], + ["17K", "1K", 16], + ["21M", "1M", 20], + ["3G", "1G", 2], + ])( + "Halving an odd number (%s) returns as many files as size (-1) (%s)", + (totalValue, expectedMaxSize, totalHistoryFiles) => { + const result = getSingleFileMaxSizeInfo(totalValue) + expect(result).toEqual({ + size: expectedMaxSize, + totalHistoryFiles, + }) + } + ) + + it.each([ + ["1B", "1B"], + ["1K", "500B"], + ["1M", "500K"], + ["1G", "500M"], + ])( + "Halving '%s' returns halved unit (%s)", + (totalValue, expectedMaxSize) => { + const result = getSingleFileMaxSizeInfo(totalValue) + expect(result).toEqual({ + size: expectedMaxSize, + totalHistoryFiles: 1, + }) + } + ) + + it.each([[undefined], [""], ["50"], ["wrongvalue"]])( + "Halving wrongly formatted value ('%s') returns undefined", + totalValue => { + const result = getSingleFileMaxSizeInfo(totalValue!) + expect(result).toBeUndefined() + } + ) + }) +}) diff --git a/packages/backend-core/src/users.ts b/packages/backend-core/src/users.ts index 166136df3c..b49058f546 100644 --- a/packages/backend-core/src/users.ts +++ b/packages/backend-core/src/users.ts @@ -67,9 +67,9 @@ export const bulkUpdateGlobalUsers = async (users: User[]) => { export async function getById(id: string, opts?: GetOpts): Promise { const db = context.getGlobalDB() - let user = await db.get(id) + let user = await db.get(id) if (opts?.cleanup) { - user = removeUserPassword(user) + user = removeUserPassword(user) as User } return user } diff --git a/packages/bbui/src/Button/Button.svelte b/packages/bbui/src/Button/Button.svelte index 91affdb6c7..9e49d84d44 100644 --- a/packages/bbui/src/Button/Button.svelte +++ b/packages/bbui/src/Button/Button.svelte @@ -1,6 +1,7 @@ - + + + diff --git a/packages/bbui/src/Form/Core/Combobox.svelte b/packages/bbui/src/Form/Core/Combobox.svelte index 2835b3cd40..b68a24d8db 100644 --- a/packages/bbui/src/Form/Core/Combobox.svelte +++ b/packages/bbui/src/Form/Core/Combobox.svelte @@ -82,7 +82,7 @@ {#if open}
(open = false)} />
    diff --git a/packages/bbui/src/Popover/Popover.svelte b/packages/bbui/src/Popover/Popover.svelte index 20207a85da..9f951a6a7e 100644 --- a/packages/bbui/src/Popover/Popover.svelte +++ b/packages/bbui/src/Popover/Popover.svelte @@ -90,6 +90,6 @@ .spectrum-Popover { min-width: var(--spectrum-global-dimension-size-2000); border-color: var(--spectrum-global-color-gray-300); - overflow: visible; + overflow: auto; } diff --git a/packages/bbui/src/Tooltip/AbsTooltip.svelte b/packages/bbui/src/Tooltip/AbsTooltip.svelte new file mode 100644 index 0000000000..9be7251445 --- /dev/null +++ b/packages/bbui/src/Tooltip/AbsTooltip.svelte @@ -0,0 +1,157 @@ + + + + +
    (hovered = true)} + on:mouseleave={() => (hovered = false)} +> + +
    + +{#if visible && text && left != null && top != null} + + + {text} + + + +{/if} + + diff --git a/packages/bbui/src/Tooltip/TempTooltip.svelte b/packages/bbui/src/Tooltip/TempTooltip.svelte new file mode 100644 index 0000000000..0d590b1ec6 --- /dev/null +++ b/packages/bbui/src/Tooltip/TempTooltip.svelte @@ -0,0 +1,39 @@ + + + + + diff --git a/packages/bbui/src/index.js b/packages/bbui/src/index.js index a9d71723f8..97762d2b3a 100644 --- a/packages/bbui/src/index.js +++ b/packages/bbui/src/index.js @@ -36,6 +36,12 @@ export { default as Layout } from "./Layout/Layout.svelte" export { default as Page } from "./Layout/Page.svelte" export { default as Link } from "./Link/Link.svelte" export { default as Tooltip } from "./Tooltip/Tooltip.svelte" +export { default as TempTooltip } from "./Tooltip/TempTooltip.svelte" +export { + default as AbsTooltip, + TooltipPosition, + TooltipType, +} from "./Tooltip/AbsTooltip.svelte" export { default as TooltipWrapper } from "./Tooltip/TooltipWrapper.svelte" export { default as Menu } from "./Menu/Menu.svelte" export { default as MenuSection } from "./Menu/Section.svelte" diff --git a/packages/builder/src/builderStore/index.js b/packages/builder/src/builderStore/index.js index 16a87810ad..2ca8057b48 100644 --- a/packages/builder/src/builderStore/index.js +++ b/packages/builder/src/builderStore/index.js @@ -127,8 +127,12 @@ export const selectedAutomation = derived(automationStore, $automationStore => { export const userSelectedResourceMap = derived(userStore, $userStore => { let map = {} $userStore.forEach(user => { - if (user.builderMetadata?.selectedResourceId) { - map[user.builderMetadata?.selectedResourceId] = user + const resource = user.builderMetadata?.selectedResourceId + if (resource) { + if (!map[resource]) { + map[resource] = [] + } + map[resource].push(user) } }) return map diff --git a/packages/builder/src/builderStore/store/automation/index.js b/packages/builder/src/builderStore/store/automation/index.js index 9e5516c512..4ebf0515d6 100644 --- a/packages/builder/src/builderStore/store/automation/index.js +++ b/packages/builder/src/builderStore/store/automation/index.js @@ -248,4 +248,36 @@ const automationActions = store => ({ } await store.actions.save(newAutomation) }, + replace: async (automationId, automation) => { + if (!automation) { + store.update(state => { + // Remove the automation + state.automations = state.automations.filter( + x => x._id !== automationId + ) + // Select a new automation if required + if (automationId === state.selectedAutomationId) { + store.actions.select(state.automations[0]?._id) + } + return state + }) + } else { + const index = get(store).automations.findIndex( + x => x._id === automation._id + ) + if (index === -1) { + // Automation addition + store.update(state => ({ + ...state, + automations: [...state.automations, automation], + })) + } else { + // Automation update + store.update(state => { + state.automations[index] = automation + return state + }) + } + } + }, }) diff --git a/packages/builder/src/builderStore/websocket.js b/packages/builder/src/builderStore/websocket.js index 87195bed25..6121831c38 100644 --- a/packages/builder/src/builderStore/websocket.js +++ b/packages/builder/src/builderStore/websocket.js @@ -1,5 +1,10 @@ import { createWebsocket } from "@budibase/frontend-core" -import { userStore, store, deploymentStore } from "builderStore" +import { + userStore, + store, + deploymentStore, + automationStore, +} from "builderStore" import { datasources, tables } from "stores/backend" import { get } from "svelte/store" import { auth } from "stores/portal" @@ -67,5 +72,10 @@ export const createBuilderWebsocket = appId => { } ) + // Automations + socket.onOther(BuilderSocketEvent.AutomationChange, ({ id, automation }) => { + automationStore.actions.replace(id, automation) + }) + return socket } diff --git a/packages/builder/src/components/automation/AutomationPanel/AutomationList.svelte b/packages/builder/src/components/automation/AutomationPanel/AutomationList.svelte index 80d65a5cb6..cce0f4eeab 100644 --- a/packages/builder/src/components/automation/AutomationPanel/AutomationList.svelte +++ b/packages/builder/src/components/automation/AutomationPanel/AutomationList.svelte @@ -1,6 +1,10 @@
    - {#each $automationStore.automations.sort(aut => aut.name) as automation, idx} + {#each $automationStore.automations.sort(aut => aut.name) as automation} 0} icon="ShareAndroid" text={automation.name} selected={automation._id === selectedAutomationId} on:click={() => selectAutomation(automation._id)} + selectedBy={$userSelectedResourceMap[automation._id]} > @@ -40,6 +44,5 @@ flex-direction: column; justify-content: flex-start; align-items: stretch; - margin: 0 calc(-1 * var(--spacing-xl)); } diff --git a/packages/builder/src/components/automation/AutomationPanel/AutomationPanel.svelte b/packages/builder/src/components/automation/AutomationPanel/AutomationPanel.svelte index 85e6a5faa3..fc52b7323a 100644 --- a/packages/builder/src/components/automation/AutomationPanel/AutomationPanel.svelte +++ b/packages/builder/src/components/automation/AutomationPanel/AutomationPanel.svelte @@ -11,8 +11,8 @@ - + diff --git a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte index 823dcc432b..cece075860 100644 --- a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte +++ b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte @@ -15,6 +15,7 @@ Icon, Checkbox, DatePicker, + Detail, } from "@budibase/bbui" import CreateWebhookModal from "components/automation/Shared/CreateWebhookModal.svelte" import { automationStore, selectedAutomation } from "builderStore" @@ -32,7 +33,7 @@ import CodeEditor from "components/common/CodeEditor/CodeEditor.svelte" import { bindingsToCompletions, - jsAutocomplete, + hbAutocomplete, EditorModes, } from "components/common/CodeEditor" import FilterDrawer from "components/design/settings/controls/FilterEditor/FilterDrawer.svelte" @@ -55,6 +56,7 @@ let drawer let fillWidth = true let inputData + let codeBindingOpen = false $: filters = lookForFilters(schemaProperties) || [] $: tempFilters = filters @@ -70,6 +72,13 @@ $: queryLimit = tableId?.includes("datasource") ? "∞" : "1000" $: isTrigger = block?.type === "TRIGGER" $: isUpdateRow = stepId === ActionStepID.UPDATE_ROW + $: codeMode = + stepId === "EXECUTE_BASH" ? EditorModes.Handlebars : EditorModes.JS + + $: stepCompletions = + codeMode === EditorModes.Handlebars + ? [hbAutocomplete([...bindingsToCompletions(bindings, codeMode)])] + : [] /** * TODO - Remove after November 2023 @@ -489,6 +498,18 @@ /> {:else if value.customType === "code"} + {#if codeMode == EditorModes.JS} + (codeBindingOpen = !codeBindingOpen)} + quiet + icon={codeBindingOpen ? "ChevronDown" : "ChevronRight"} + > + Bindings + + {#if codeBindingOpen} +
    {JSON.stringify(bindings, null, 2)}
    + {/if} + {/if} { @@ -496,19 +517,22 @@ onChange({ detail: e.detail }, key) inputData[key] = e.detail }} - completions={[ - jsAutocomplete([ - ...bindingsToCompletions(bindings, EditorModes.JS), - ]), - ]} - mode={EditorModes.JS} + completions={stepCompletions} + mode={codeMode} + autocompleteEnabled={codeMode != EditorModes.JS} height={500} />
    - -
    -
    Add available bindings by typing $
    -
    + {#if codeMode == EditorModes.Handlebars} + +
    +
    + Add available bindings by typing + }} + +
    +
    + {/if}
    {:else if value.customType === "loopOption"} diff --git a/packages/builder/src/components/backend/DataTable/buttons/TableFilterButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/TableFilterButton.svelte index ce6a3f0c51..b96738ab1a 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/TableFilterButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/TableFilterButton.svelte @@ -17,7 +17,7 @@ $: text = getText(filters) const getText = filters => { - const count = filters?.length + const count = filters?.filter(filter => filter.field)?.length return count ? `Filter (${count})` : "Filter" } diff --git a/packages/builder/src/components/backend/Datasources/TableImportSelection/tableSelectionStore.js b/packages/builder/src/components/backend/Datasources/TableImportSelection/tableSelectionStore.js index 3cb3ef96a6..6235ea397a 100644 --- a/packages/builder/src/components/backend/Datasources/TableImportSelection/tableSelectionStore.js +++ b/packages/builder/src/components/backend/Datasources/TableImportSelection/tableSelectionStore.js @@ -10,9 +10,8 @@ export const createTableSelectionStore = (integration, datasource) => { datasources.getTableNames(datasource).then(tableNames => { tableNamesStore.set(tableNames) - selectedTableNamesStore.set( - tableNames.filter(tableName => datasource.entities[tableName]) + tableNames.filter(tableName => datasource.entities?.[tableName]) ) loadingStore.set(false) diff --git a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte index bfca91afaa..d13a7c30db 100644 --- a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte +++ b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte @@ -2,21 +2,13 @@ import { goto, url } from "@roxi/routify" import { tables } from "stores/backend" import { notifications } from "@budibase/bbui" - import { - Input, - Label, - ModalContent, - Toggle, - Divider, - Layout, - } from "@budibase/bbui" + import { Input, Label, ModalContent, Layout } from "@budibase/bbui" import { datasources } from "stores/backend" import TableDataImport from "../TableDataImport.svelte" import { BUDIBASE_INTERNAL_DB_ID, BUDIBASE_DATASOURCE_TYPE, } from "constants/backend" - import { buildAutoColumn, getAutoColumnInformation } from "builderStore/utils" $: tableNames = $tables.list.map(table => table.name) $: selectedSource = $datasources.list.find( @@ -43,28 +35,12 @@ } let error = "" - let autoColumns = getAutoColumnInformation() + let schema = {} let rows = [] let allValid = true let displayColumn = null - function getAutoColumns() { - const selectedAutoColumns = {} - - Object.entries(autoColumns).forEach(([subtype, column]) => { - if (column.enabled) { - selectedAutoColumns[column.name] = buildAutoColumn( - name, - column.name, - subtype - ) - } - }) - - return selectedAutoColumns - } - function checkValid(evt) { const tableName = evt.target.value if (tableNames.includes(tableName)) { @@ -77,7 +53,7 @@ async function saveTable() { let newTable = { name, - schema: { ...schema, ...getAutoColumns() }, + schema: { ...schema }, rows, type: "internal", sourceId: targetDatasourceId, @@ -118,21 +94,6 @@ bind:value={name} {error} /> -
    - -
    -
    - - - -
    -
    - - -
    -
    - -
    - - diff --git a/packages/builder/src/components/backend/TableNavigator/popovers/EditViewPopover.svelte b/packages/builder/src/components/backend/TableNavigator/popovers/EditViewPopover.svelte index b637f46f5e..99f19935a1 100644 --- a/packages/builder/src/components/backend/TableNavigator/popovers/EditViewPopover.svelte +++ b/packages/builder/src/components/backend/TableNavigator/popovers/EditViewPopover.svelte @@ -35,9 +35,8 @@ try { const isSelected = decodeURIComponent($params.viewName) === $views.selectedViewName - const name = view.name const id = view.tableId - await views.delete(name) + await views.delete(view) notifications.success("View deleted") if (isSelected) { $goto(`./table/${id}`) diff --git a/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte b/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte index dadca85fac..74f160710f 100644 --- a/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte +++ b/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte @@ -7,6 +7,8 @@ closeBrackets, completionKeymap, closeBracketsKeymap, + acceptCompletion, + completionStatus, } from "@codemirror/autocomplete" import { EditorView, @@ -34,7 +36,8 @@ defaultKeymap, historyKeymap, history, - indentWithTab, + indentMore, + indentLess, } from "@codemirror/commands" import { Compartment } from "@codemirror/state" import { javascript } from "@codemirror/lang-javascript" @@ -48,6 +51,7 @@ export let mode = EditorModes.Handlebars export let value = "" export let placeholder = null + export let autocompleteEnabled = true // Export a function to expose caret position export const getCaretPosition = () => { @@ -107,6 +111,22 @@ let isDark = !currentTheme.includes("light") let themeConfig = new Compartment() + const indentWithTabCustom = { + key: "Tab", + run: view => { + if (completionStatus(view.state) == "active") { + acceptCompletion(view) + return true + } + indentMore(view) + return true + }, + shift: view => { + indentLess(view) + return true + }, + } + const buildKeymap = () => { const baseMap = [ ...closeBracketsKeymap, @@ -114,7 +134,7 @@ ...historyKeymap, ...foldKeymap, ...completionKeymap, - indentWithTab, + indentWithTabCustom, ] return baseMap } @@ -131,12 +151,6 @@ syntaxHighlighting(oneDarkHighlightStyle, { fallback: true }), highlightActiveLineGutter(), highlightSpecialChars(), - autocompletion({ - override: [...completions], - closeOnBlur: true, - icons: false, - optionClass: () => "autocomplete-option", - }), EditorView.lineWrapping, EditorView.updateListener.of(v => { const docStr = v.state.doc?.toString() @@ -159,11 +173,16 @@ const buildExtensions = base => { const complete = [...base] - if (mode.name == "javascript") { - complete.push(javascript()) - complete.push(highlightWhitespace()) - complete.push(lineNumbers()) - complete.push(foldGutter()) + + if (autocompleteEnabled) { + complete.push( + autocompletion({ + override: [...completions], + closeOnBlur: true, + icons: false, + optionClass: () => "autocomplete-option", + }) + ) complete.push( EditorView.inputHandler.of((view, from, to, insert) => { if (insert === "$") { @@ -193,6 +212,13 @@ ) } + if (mode.name == "javascript") { + complete.push(javascript()) + complete.push(highlightWhitespace()) + complete.push(lineNumbers()) + complete.push(foldGutter()) + } + if (placeholder) { complete.push(placeholderFn(placeholder)) } diff --git a/packages/builder/src/components/common/CreationPage.svelte b/packages/builder/src/components/common/CreationPage.svelte new file mode 100644 index 0000000000..9631e9d961 --- /dev/null +++ b/packages/builder/src/components/common/CreationPage.svelte @@ -0,0 +1,39 @@ + + +
    +
    + {#if showClose} + + {/if} +
    +
    + {heading} +
    + +
    + + diff --git a/packages/builder/src/components/common/NavItem.svelte b/packages/builder/src/components/common/NavItem.svelte index 467827dbf6..7d61751364 100644 --- a/packages/builder/src/components/common/NavItem.svelte +++ b/packages/builder/src/components/common/NavItem.svelte @@ -2,6 +2,7 @@ import { Icon } from "@budibase/bbui" import { createEventDispatcher, getContext } from "svelte" import { helpers } from "@budibase/shared-core" + import { UserAvatars } from "@budibase/frontend-core" export let icon export let withArrow = false @@ -98,21 +99,25 @@
    {/if} -
    {text}
    +
    + {text} + {#if selectedBy} + + {/if} +
    + {#if withActions}
    {/if} + {#if $$slots.right}
    {/if}
- {#if selectedBy} -
{helpers.getUserLabel(selectedBy)}
- {/if}
diff --git a/packages/builder/src/components/design/settings/controls/ColumnEditor/ColumnEditor.svelte b/packages/builder/src/components/design/settings/controls/ColumnEditor/ColumnEditor.svelte index 5f1abdbeca..cebb429ac4 100644 --- a/packages/builder/src/components/design/settings/controls/ColumnEditor/ColumnEditor.svelte +++ b/packages/builder/src/components/design/settings/controls/ColumnEditor/ColumnEditor.svelte @@ -20,6 +20,7 @@ let drawer let boundValue + $: text = getText(value) $: datasource = getDatasourceForProvider($currentAsset, componentInstance) $: schema = getSchema($currentAsset, datasource) $: options = allowCellEditing @@ -31,6 +32,17 @@ allowLinks: true, }) + const getText = value => { + if (!value?.length) { + return "All columns" + } + let text = `${value.length} column` + if (value.length !== 1) { + text += "s" + } + return text + } + const getSchema = (asset, datasource) => { const schema = getSchemaForDatasource(asset, datasource).schema @@ -76,7 +88,7 @@
- Configure columns + {text}
diff --git a/packages/builder/src/components/design/settings/controls/FieldConfiguration/FieldConfiguration.svelte b/packages/builder/src/components/design/settings/controls/FieldConfiguration/FieldConfiguration.svelte index 2778ce4f74..f9dccf586c 100644 --- a/packages/builder/src/components/design/settings/controls/FieldConfiguration/FieldConfiguration.svelte +++ b/packages/builder/src/components/design/settings/controls/FieldConfiguration/FieldConfiguration.svelte @@ -12,25 +12,36 @@ export let componentInstance export let value = [] - const convertOldColumnFormat = oldColumns => { - if (typeof oldColumns?.[0] === "string") { - value = oldColumns.map(field => ({ name: field, displayName: field })) - } - } - - $: convertOldColumnFormat(value) - const dispatch = createEventDispatcher() let drawer let boundValue + $: text = getText(value) + $: convertOldColumnFormat(value) $: datasource = getDatasourceForProvider($currentAsset, componentInstance) $: schema = getSchema($currentAsset, datasource) $: options = Object.keys(schema || {}) $: sanitisedValue = getValidColumns(value, options) $: updateBoundValue(sanitisedValue) + const getText = value => { + if (!value?.length) { + return "All fields" + } + let text = `${value.length} field` + if (value.length !== 1) { + text += "s" + } + return text + } + + const convertOldColumnFormat = oldColumns => { + if (typeof oldColumns?.[0] === "string") { + value = oldColumns.map(field => ({ name: field, displayName: field })) + } + } + const getSchema = (asset, datasource) => { const schema = getSchemaForDatasource(asset, datasource).schema @@ -75,7 +86,10 @@ } -Configure fields +
+ {text} +
+ Configure the fields in your form. @@ -83,3 +97,9 @@ + + diff --git a/packages/builder/src/components/design/settings/controls/FilterEditor/FilterEditor.svelte b/packages/builder/src/components/design/settings/controls/FilterEditor/FilterEditor.svelte index 9d48789947..88c3842f54 100644 --- a/packages/builder/src/components/design/settings/controls/FilterEditor/FilterEditor.svelte +++ b/packages/builder/src/components/design/settings/controls/FilterEditor/FilterEditor.svelte @@ -20,7 +20,7 @@ $: datasource = getDatasourceForProvider($currentAsset, componentInstance) $: schema = getSchemaForDatasource($currentAsset, datasource)?.schema $: schemaFields = Object.values(schema || {}) - $: text = getText(value) + $: text = getText(value?.filter(filter => filter.field)) async function saveFilter() { dispatch("change", tempValue) diff --git a/packages/builder/src/components/portal/onboarding/TourPopover.svelte b/packages/builder/src/components/portal/onboarding/TourPopover.svelte index d4958b386e..d959a6ef95 100644 --- a/packages/builder/src/components/portal/onboarding/TourPopover.svelte +++ b/packages/builder/src/components/portal/onboarding/TourPopover.svelte @@ -1,5 +1,5 @@
@@ -18,11 +15,6 @@ {text || ""}
{/if} - {#if tooltip} -
- -
- {/if} diff --git a/packages/builder/src/components/start/AppRow.svelte b/packages/builder/src/components/start/AppRow.svelte index 50e6b8466a..2e7719987d 100644 --- a/packages/builder/src/components/start/AppRow.svelte +++ b/packages/builder/src/components/start/AppRow.svelte @@ -2,12 +2,12 @@ import { Heading, Body, Button, Icon } from "@budibase/bbui" import { processStringSync } from "@budibase/string-templates" import { goto } from "@roxi/routify" - import { UserAvatar } from "@budibase/frontend-core" + import { UserAvatars } from "@budibase/frontend-core" export let app export let lockedAction - $: editing = app?.lockedBy != null + $: editing = app.sessions?.length const handleDefaultClick = () => { if (window.innerWidth < 640) { @@ -41,7 +41,7 @@
{#if editing} Currently editing - + {:else if app.updatedAt} {processStringSync("Updated {{ duration time 'millisecond' }} ago", { time: new Date().getTime() - new Date(app.updatedAt).getTime(), diff --git a/packages/builder/src/components/start/CreateAppModal.svelte b/packages/builder/src/components/start/CreateAppModal.svelte index f29b7067c3..9e831f5bd9 100644 --- a/packages/builder/src/components/start/CreateAppModal.svelte +++ b/packages/builder/src/components/start/CreateAppModal.svelte @@ -15,8 +15,6 @@ import { createValidationStore } from "helpers/validation/yup" import * as appValidation from "helpers/validation/yup/app" import TemplateCard from "components/common/TemplateCard.svelte" - import createFromScratchScreen from "builderStore/store/screenTemplates/createFromScratchScreen" - import { Roles } from "constants/backend" import { lowercase } from "helpers" export let template @@ -142,21 +140,6 @@ // Create user await auth.setInitInfo({}) - // Create a default home screen if no template was selected - if (template == null) { - let defaultScreenTemplate = createFromScratchScreen.create() - defaultScreenTemplate.routing.route = "/home" - defaultScreenTemplate.routing.roldId = Roles.BASIC - try { - await store.actions.screens.save(defaultScreenTemplate) - } catch (err) { - console.error("Could not create a default application screen", err) - notifications.warning( - "Encountered an issue creating the default screen." - ) - } - } - $goto(`/builder/app/${createdApp.instance._id}`) } catch (error) { creating = false diff --git a/packages/builder/src/pages/builder/app/[application]/_components/PreviewOverlay.svelte b/packages/builder/src/pages/builder/app/[application]/_components/PreviewOverlay.svelte index d069d1b4c7..eedff6c2a7 100644 --- a/packages/builder/src/pages/builder/app/[application]/_components/PreviewOverlay.svelte +++ b/packages/builder/src/pages/builder/app/[application]/_components/PreviewOverlay.svelte @@ -15,6 +15,7 @@ } onMount(() => { + window.isBuilder = true window.closePreview = () => { store.update(state => ({ ...state, diff --git a/packages/builder/src/pages/builder/app/[application]/_components/UserAvatars.svelte b/packages/builder/src/pages/builder/app/[application]/_components/UserAvatars.svelte deleted file mode 100644 index 0cd8c5bf6b..0000000000 --- a/packages/builder/src/pages/builder/app/[application]/_components/UserAvatars.svelte +++ /dev/null @@ -1,28 +0,0 @@ - - -
- {#each uniqueUsers as user} - - {/each} -
- - diff --git a/packages/builder/src/pages/builder/app/[application]/_layout.svelte b/packages/builder/src/pages/builder/app/[application]/_layout.svelte index e6c79335a9..872151b4a3 100644 --- a/packages/builder/src/pages/builder/app/[application]/_layout.svelte +++ b/packages/builder/src/pages/builder/app/[application]/_layout.svelte @@ -15,6 +15,7 @@ Heading, Modal, notifications, + TooltipPosition, } from "@budibase/bbui" import AppActions from "components/deploy/AppActions.svelte" import { API } from "api" @@ -25,8 +26,8 @@ import TourWrap from "components/portal/onboarding/TourWrap.svelte" import TourPopover from "components/portal/onboarding/TourPopover.svelte" import BuilderSidePanel from "./_components/BuilderSidePanel.svelte" - import UserAvatars from "./_components/UserAvatars.svelte" - import { TOUR_KEYS, TOURS } from "components/portal/onboarding/tours.js" + import { UserAvatars } from "@budibase/frontend-core" + import { TOUR_KEYS } from "components/portal/onboarding/tours.js" import PreviewOverlay from "./_components/PreviewOverlay.svelte" export let application @@ -86,17 +87,10 @@ // Check if onboarding is enabled. if (isEnabled(TENANT_FEATURE_FLAGS.ONBOARDING_TOUR)) { if (!$auth.user?.onboardedAt) { - // Determine the correct step - const activeNav = $layout.children.find(c => $isActive(c.path)) - const onboardingTour = TOURS[TOUR_KEYS.TOUR_BUILDER_ONBOARDING] - const targetStep = activeNav - ? onboardingTour.find(step => step.route === activeNav?.path) - : null await store.update(state => ({ ...state, onboarding: true, tourKey: TOUR_KEYS.TOUR_BUILDER_ONBOARDING, - tourStepKey: targetStep?.id, })) } else { // Feature tour date @@ -172,7 +166,11 @@
- +
@@ -228,7 +226,7 @@ .top-nav { flex: 0 0 60px; background: var(--background); - padding: 0 var(--spacing-xl); + padding-left: var(--spacing-xl); display: grid; grid-template-columns: 1fr auto 1fr; flex-direction: row; diff --git a/packages/builder/src/pages/builder/app/[application]/automation/_layout.svelte b/packages/builder/src/pages/builder/app/[application]/automation/_layout.svelte index 74dfe671ab..0afe257e60 100644 --- a/packages/builder/src/pages/builder/app/[application]/automation/_layout.svelte +++ b/packages/builder/src/pages/builder/app/[application]/automation/_layout.svelte @@ -8,6 +8,10 @@ import { onDestroy, onMount } from "svelte" import { syncURLToState } from "helpers/urlStateSync" import * as routify from "@roxi/routify" + import { store } from "builderStore" + + $: automationId = $selectedAutomation?._id + $: store.actions.websocket.selectResource(automationId) // Keep URL and state in sync for selected screen ID const stopSyncing = syncURLToState({ diff --git a/packages/builder/src/pages/builder/app/[application]/data/datasource/[datasourceId]/_components/panels/Queries/RestImportButton.svelte b/packages/builder/src/pages/builder/app/[application]/data/datasource/[datasourceId]/_components/panels/Queries/RestImportButton.svelte index 682284adca..7f179d18b4 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/datasource/[datasourceId]/_components/panels/Queries/RestImportButton.svelte +++ b/packages/builder/src/pages/builder/app/[application]/data/datasource/[datasourceId]/_components/panels/Queries/RestImportButton.svelte @@ -2,11 +2,12 @@ import { Button, Modal } from "@budibase/bbui" import ImportQueriesModal from "./RestImportQueriesModal.svelte" + export let datasourceId let importQueriesModal - +
diff --git a/packages/builder/src/pages/builder/app/[application]/data/new.svelte b/packages/builder/src/pages/builder/app/[application]/data/new.svelte index 4eb9ce16e1..b2aca33c6a 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/new.svelte +++ b/packages/builder/src/pages/builder/app/[application]/data/new.svelte @@ -7,14 +7,14 @@ } from "stores/backend" import { hasData } from "stores/selectors" - import { Icon, notifications, Heading, Body } from "@budibase/bbui" + import { notifications, Body, Icon, AbsTooltip } from "@budibase/bbui" import { params, goto } from "@roxi/routify" import CreateExternalDatasourceModal from "./_components/CreateExternalDatasourceModal/index.svelte" import CreateInternalTableModal from "./_components/CreateInternalTableModal.svelte" import DatasourceOption from "./_components/DatasourceOption.svelte" import IntegrationIcon from "components/backend/DatasourceNavigator/IntegrationIcon.svelte" + import CreationPage from "components/common/CreationPage.svelte" import ICONS from "components/backend/DatasourceNavigator/icons/index.js" - import FontAwesomeIcon from "components/common/FontAwesomeIcon.svelte" let internalTableModal let externalDatasourceModal @@ -46,25 +46,16 @@ bind:this={externalDatasourceModal} /> -
-
- {#if hasData($datasources, $tables)} - - {/if} -
-
- Add new data source -
- + $goto("./table")} + heading="Add new data source" +>
Get started with our Budibase DB - + + +
@@ -113,37 +104,19 @@ {/each}
-
+ diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/ScreenDropdownMenu.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/ScreenDropdownMenu.svelte index ec965ed659..fd5ddd9459 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/ScreenDropdownMenu.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/ScreenDropdownMenu.svelte @@ -9,7 +9,7 @@ Helpers, notifications, } from "@budibase/bbui" - import ScreenDetailsModal from "./ScreenDetailsModal.svelte" + import ScreenDetailsModal from "components/design/ScreenDetailsModal.svelte" import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl" import { makeComponentUnique } from "builderStore/componentUtils" diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/ScreenListPanel.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/ScreenListPanel.svelte index 92a88abb13..6362af3073 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/ScreenListPanel.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/ScreenListPanel.svelte @@ -1,17 +1,16 @@ - - - - newScreenModal.show()} initalScreens={!selectedTemplates ? [] : [...selectedTemplates]} /> @@ -198,7 +194,6 @@ newScreenModal.show()} initialUrl={blankScreenUrl} /> diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/DatasourceModal.svelte b/packages/builder/src/pages/builder/app/[application]/design/_components/DatasourceModal.svelte similarity index 100% rename from packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/DatasourceModal.svelte rename to packages/builder/src/pages/builder/app/[application]/design/_components/DatasourceModal.svelte diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/ScreenRoleModal.svelte b/packages/builder/src/pages/builder/app/[application]/design/_components/ScreenRoleModal.svelte similarity index 93% rename from packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/ScreenRoleModal.svelte rename to packages/builder/src/pages/builder/app/[application]/design/_components/ScreenRoleModal.svelte index cde8047b97..5d73b7961c 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/ScreenRoleModal.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/_components/ScreenRoleModal.svelte @@ -40,14 +40,14 @@ - Select which level of access you want your screens to have + Select the level of access required to see these screens