diff --git a/hosting/couchdb/Dockerfile b/hosting/couchdb/Dockerfile index b95fa348f8..cfbe0b2840 100644 --- a/hosting/couchdb/Dockerfile +++ b/hosting/couchdb/Dockerfile @@ -4,7 +4,7 @@ # Dockerfile. Only modifications related to upgrading from Debian bullseye to # bookworm have been included. The `runner` image contains Budibase's # customisations to the image, e.g. adding Clouseau. -FROM node:20-slim AS base +FROM node:22-slim AS base # Add CouchDB user account to make sure the IDs are assigned consistently RUN groupadd -g 5984 -r couchdb && useradd -u 5984 -d /opt/couchdb -g couchdb couchdb diff --git a/hosting/single/Dockerfile b/hosting/single/Dockerfile index 75b1a12d12..50d5b4f7dc 100644 --- a/hosting/single/Dockerfile +++ b/hosting/single/Dockerfile @@ -1,5 +1,5 @@ ARG BASEIMG=budibase/couchdb:v3.3.3-sqs-v2.1.1 -FROM node:20-slim AS build +FROM node:22-slim AS build # install node-gyp dependencies RUN apt-get update && apt-get install -y --no-install-recommends g++ make python3 jq diff --git a/lerna.json b/lerna.json index 84cec83277..c20b677183 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", - "version": "3.10.6", + "version": "3.10.7", "npmClient": "yarn", "concurrency": 20, "command": { diff --git a/packages/builder/src/App.svelte b/packages/builder/src/App.svelte index 866a28a4e6..3d51a60779 100644 --- a/packages/builder/src/App.svelte +++ b/packages/builder/src/App.svelte @@ -1,9 +1,12 @@ diff --git a/packages/builder/src/components/common/UpdateAppForm.svelte b/packages/builder/src/components/common/UpdateAppForm.svelte index 8d4a0a6e6c..b65cfc71b1 100644 --- a/packages/builder/src/components/common/UpdateAppForm.svelte +++ b/packages/builder/src/components/common/UpdateAppForm.svelte @@ -1,5 +1,12 @@ -
- - -
{ - formPopover.show() - }} - > - - - - -
-
- - { - formPopoverOpen = false - }} - on:open={() => { - formPopoverOpen = true + + +
{ + $goto(`/builder/app/${$appStore.appId}/settings/general`) }} > - -
- { - formPopover.hide() - }} - /> -
-
- + + + + +
diff --git a/packages/builder/src/components/deploy/AppActions.svelte b/packages/builder/src/components/deploy/AppActions.svelte index f8f9cd261c..e3b11b31b0 100644 --- a/packages/builder/src/components/deploy/AppActions.svelte +++ b/packages/builder/src/components/deploy/AppActions.svelte @@ -1,139 +1,34 @@ @@ -164,161 +59,50 @@ - -
-
- - Preview - -
-
- -
{ - if (!appActionPopoverOpen) { - lastOpened = new Date() - appActionPopover.show() - } else { - appActionPopover.hide() - } - }} - > -
-
- - - Publish - - -
-
- { - appActionPopoverOpen = false - }} - on:open={() => { - appActionPopoverOpen = true - }} +
+ -
- -
-
+ Publish + - - - Are you sure you want to unpublish the app {selectedApp?.name}? - - - - - {#if showNpsSurvey}
{/if} + + + +
+ + + App published successfully +
+ + + + +
+
+ diff --git a/packages/builder/src/components/deploy/RevertModal.svelte b/packages/builder/src/components/deploy/RevertModal.svelte index a8c7b04d9f..19ecb31dcf 100644 --- a/packages/builder/src/components/deploy/RevertModal.svelte +++ b/packages/builder/src/components/deploy/RevertModal.svelte @@ -35,7 +35,7 @@ {} + export let onChange = undefined export let bindings = [] export let componentBindings = [] export let nested = false export let propertyFocus = false - export let info = null + export let info = undefined export let disableBindings = false export let wide = false - export let contextAccess = null + export let contextAccess = undefined + const dispatch = createEventDispatcher() let highlightType let domElement @@ -93,10 +95,11 @@ innerVal = parseInt(innerVal) } - if (typeof innerVal === "string") { - onChange(replaceBindings(innerVal)) - } else { - onChange(innerVal) + const dispatchVal = + typeof innerVal === "string" ? replaceBindings(innerVal) : innerVal + dispatch("change", dispatchVal) + if (onChange) { + onChange(dispatchVal) } } diff --git a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte index cf491b142e..bfc04e7c12 100644 --- a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte +++ b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte @@ -14,7 +14,12 @@ Button, FancySelect, } from "@budibase/bbui" - import { builderStore, appStore, roles, appPublished } from "@/stores/builder" + import { + builderStore, + appStore, + roles, + deploymentStore, + } from "@/stores/builder" import { groups, licensing, @@ -620,7 +625,7 @@
- {#if !$appPublished} + {#if !$deploymentStore.isPublished}
- import { - Layout, - Label, - ColorPicker, - notifications, - Icon, - Body, - } from "@budibase/bbui" - import { themeStore, appStore } from "@/stores/builder" - import { DefaultAppTheme } from "@/constants" - import AppThemeSelect from "./AppThemeSelect.svelte" - import ButtonRoundnessSelect from "./ButtonRoundnessSelect.svelte" - import PropertyControl from "@/components/design/settings/controls/PropertyControl.svelte" - - $: customTheme = $themeStore.customTheme || {} - - const update = async (property, value) => { - try { - themeStore.saveCustom({ [property]: value }, $appStore.appId) - } catch (error) { - notifications.error("Error updating custom theme") - } - } - - -
- - - These settings apply to all screens. PDFs are always light theme. - -
- - - - - - - update("buttonBorderRadius", e.detail)} - /> - - update("primaryColor", val)} - props={{ - spectrumTheme: $themeStore.theme, - }} - /> - update("primaryColorHover", val)} - props={{ - spectrumTheme: $themeStore.theme, - }} - /> - - - diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Screen/index.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Screen/index.svelte index be7d48f88f..a7452deade 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Screen/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Screen/index.svelte @@ -1,13 +1,8 @@ {#if $selectedScreen} @@ -17,37 +12,8 @@ borderLeft wide > -
-
- {#each tabs as tab} - { - activeTab = tab - }} - > - {capitalise(tab)} - - {/each} -
-
- {#if activeTab === "theme"} - - {:else} - - {/if} + {/if} - - 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 be409eff09..4044f089a2 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 @@ -1,13 +1,27 @@
@@ -17,13 +31,24 @@
- {#if !isPDF} - {#if $appStore.clientFeatures.devicePreview} - +
+ {#if !isPDF} + {#if $appStore.clientFeatures.devicePreview} + + {/if} + {/if} - - {/if} - + +
+ + + Preview +
@@ -56,7 +81,7 @@ } .header { display: flex; - margin-bottom: 9px; + margin: 0 6px 4px 0; } .header-left { @@ -76,4 +101,10 @@ .content { flex: 1 1 auto; } + .actions { + display: flex; + flex-direction: row; + gap: 4px; + align-items: center; + } diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/DevicePreviewSelect.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/DevicePreviewSelect.svelte deleted file mode 100644 index 436571dd2e..0000000000 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/DevicePreviewSelect.svelte +++ /dev/null @@ -1,25 +0,0 @@ - - - - previewStore.setDevice("desktop")} - /> - previewStore.setDevice("tablet")} - /> - previewStore.setDevice("mobile")} - /> - diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Screen/AppThemeSelect.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/Theme/AppThemeSelect.svelte similarity index 100% rename from packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Screen/AppThemeSelect.svelte rename to packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/Theme/AppThemeSelect.svelte diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Screen/ButtonRoundnessSelect.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/Theme/ButtonRoundnessSelect.svelte similarity index 100% rename from packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Screen/ButtonRoundnessSelect.svelte rename to packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/Theme/ButtonRoundnessSelect.svelte diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/Theme/ThemeSettings.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/Theme/ThemeSettings.svelte new file mode 100644 index 0000000000..c35831dbd2 --- /dev/null +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/Theme/ThemeSettings.svelte @@ -0,0 +1,101 @@ + + + + + + + +
+ + + These settings apply to all screens.
+ PDFs are always light theme. + +
+ + + + + + + update("buttonBorderRadius", e.detail)} + /> + + update("primaryColor", e.detail)} + props={{ + spectrumTheme: $themeStore.theme, + }} + /> + update("primaryColorHover", e.detail)} + props={{ + spectrumTheme: $themeStore.theme, + }} + /> + +
+ + diff --git a/packages/builder/src/pages/builder/app/[application]/settings/_layout.svelte b/packages/builder/src/pages/builder/app/[application]/settings/_layout.svelte index 56c7131891..cf698623a6 100644 --- a/packages/builder/src/pages/builder/app/[application]/settings/_layout.svelte +++ b/packages/builder/src/pages/builder/app/[application]/settings/_layout.svelte @@ -1,11 +1,7 @@ @@ -14,6 +10,11 @@ + - - - -
- - { - deleteModal.show() - }} - /> - -
@@ -82,19 +52,7 @@
- - diff --git a/packages/builder/src/pages/builder/app/[application]/settings/general.svelte b/packages/builder/src/pages/builder/app/[application]/settings/general.svelte new file mode 100644 index 0000000000..9d55083664 --- /dev/null +++ b/packages/builder/src/pages/builder/app/[application]/settings/general.svelte @@ -0,0 +1,220 @@ + + + + + General settings + Control app version, deployment and settings + + + App info + + + Deployment + {#if $deploymentStore.isPublished} +
+ + + {$deploymentStore.lastPublished} +
+ + + + +
+
+ + +
+ {:else} +
+ + + Your app hasn't been published yet and isn't available to users + +
+
+ +
+ {/if} + + + App version + {#if updateAvailable} + + The app is currently using version {$appStore.version} + but version {$appStore.upgradableVersion} is available. +
+ Updates can contain new features, performance improvements and bug fixes. + +
+ +
+ {:else} + + The app is currently using version {$appStore.version}. +
+ You're running the latest! + +
+ +
+ {/if} +
+ + + Export + + Export your app for backup or to share it with someone else + + +
+ + +
+ + + Import + Import an app export bundle to update this app + +
+ +
+ + Danger zone +
+ +
+
+ + + + + + + + + + + + + Are you sure you want to unpublish the app {selectedApp?.name}? + + + + + + + diff --git a/packages/builder/src/pages/builder/app/[application]/settings/index.svelte b/packages/builder/src/pages/builder/app/[application]/settings/index.svelte index 1c763724a1..eeecdfea0e 100644 --- a/packages/builder/src/pages/builder/app/[application]/settings/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/settings/index.svelte @@ -1,5 +1,5 @@ diff --git a/packages/builder/src/pages/builder/app/[application]/settings/name-and-url.svelte b/packages/builder/src/pages/builder/app/[application]/settings/name-and-url.svelte deleted file mode 100644 index 846dacc002..0000000000 --- a/packages/builder/src/pages/builder/app/[application]/settings/name-and-url.svelte +++ /dev/null @@ -1,13 +0,0 @@ - - - - - Name and URL - Edit your app's name and URL - - - - diff --git a/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte b/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte index 78080e8640..ea14b2d1b1 100644 --- a/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte +++ b/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte @@ -107,7 +107,7 @@
- Progressive Web App + Progressive web app {#if !pwaEnabled} Enterprise diff --git a/packages/builder/src/pages/builder/app/[application]/settings/version.svelte b/packages/builder/src/pages/builder/app/[application]/settings/version.svelte deleted file mode 100644 index 6a780555a9..0000000000 --- a/packages/builder/src/pages/builder/app/[application]/settings/version.svelte +++ /dev/null @@ -1,56 +0,0 @@ - - - - - Version - See the current version of your app and check for updates - - - {#if updateAvailable} - - The app is currently using version {$appStore.version} - but version {$appStore.upgradableVersion} is available. -
- Updates can contain new features, performance improvements and bug fixes. - -
- -
- {:else} - - The app is currently using version {$appStore.version}. -
- You're running the latest! - -
- -
- {/if} -
- - diff --git a/packages/builder/src/pages/builder/portal/users/users/[userId].svelte b/packages/builder/src/pages/builder/portal/users/users/[userId].svelte index d479bb52a2..b3b3807853 100644 --- a/packages/builder/src/pages/builder/portal/users/users/[userId].svelte +++ b/packages/builder/src/pages/builder/portal/users/users/[userId].svelte @@ -149,7 +149,7 @@ // check if access via group for creator const foundGroup = $groups?.find( - group => group.roles[prodAppId] || group.builder?.apps[prodAppId] + group => group.roles?.[prodAppId] || group.builder?.apps[prodAppId] ) if (foundGroup.builder?.apps[prodAppId]) { return Constants.Roles.CREATOR diff --git a/packages/builder/src/stores/builder/deployment.ts b/packages/builder/src/stores/builder/deployment.ts new file mode 100644 index 0000000000..73e6480550 --- /dev/null +++ b/packages/builder/src/stores/builder/deployment.ts @@ -0,0 +1,143 @@ +import { type Writable, get, type Readable, derived } from "svelte/store" +import { API } from "@/api" +import { notifications } from "@budibase/bbui" +import { DeploymentProgressResponse, DeploymentStatus } from "@budibase/types" +import analytics, { Events, EventSource } from "@/analytics" +import { appsStore } from "@/stores/portal/apps" +import { DerivedBudiStore } from "@/stores/BudiStore" +import { appStore } from "./app" +import { processStringSync } from "@budibase/string-templates" + +interface DeploymentState { + deployments: DeploymentProgressResponse[] + isPublishing: boolean +} + +interface DerivedDeploymentState extends DeploymentState { + isPublished: boolean + lastPublished?: string +} + +class DeploymentStore extends DerivedBudiStore< + DeploymentState, + DerivedDeploymentState +> { + constructor() { + const makeDerivedStore = ( + store: Writable + ): Readable => { + return derived( + [store, appStore, appsStore], + ([$store, $appStore, $appsStore]) => { + // Determine whether the app is published + const app = $appsStore.apps.find(app => app.devId === $appStore.appId) + const deployments = $store.deployments.filter( + x => x.status === DeploymentStatus.SUCCESS + ) + const isPublished = + app?.status === "published" && !!deployments.length + + // Generate last published string + let lastPublished = undefined + if (isPublished) { + lastPublished = processStringSync( + "Your app was last published {{ duration time 'millisecond' }} ago", + { + time: + new Date().getTime() - + new Date(deployments[0].updatedAt).getTime(), + } + ) + } + return { + ...$store, + isPublished, + lastPublished, + } + } + ) + } + super( + { + deployments: [], + isPublishing: false, + }, + makeDerivedStore + ) + this.load = this.load.bind(this) + this.publishApp = this.publishApp.bind(this) + this.completePublish = this.completePublish.bind(this) + this.unpublishApp = this.unpublishApp.bind(this) + } + + async load() { + try { + const deployments = await API.getAppDeployments() + this.update(state => ({ + ...state, + deployments, + })) + } catch (err) { + notifications.error("Error fetching deployments") + } + } + + async publishApp(showNotification = true) { + try { + this.update(state => ({ ...state, isPublishing: true })) + await API.publishAppChanges(get(appStore).appId) + if (showNotification) { + notifications.send("App published successfully", { + type: "success", + icon: "GlobeCheck", + }) + } + await this.completePublish() + } catch (error: any) { + analytics.captureException(error) + const message = error?.message ? ` - ${error.message}` : "" + notifications.error(`Error publishing app${message}`) + } + this.update(state => ({ ...state, isPublishing: false })) + } + + async completePublish() { + try { + await appsStore.load() + await this.load() + } catch (err) { + notifications.error("Error refreshing app") + } + } + + async unpublishApp() { + if (!get(this.derivedStore).isPublished) { + return + } + try { + await API.unpublishApp(get(appStore).appId) + await appsStore.load() + notifications.send("App unpublished", { + type: "success", + icon: "GlobeStrike", + }) + } catch (err) { + notifications.error("Error unpublishing app") + } + } + + viewPublishedApp() { + const app = get(appStore) + analytics.captureEvent(Events.APP_VIEW_PUBLISHED, { + appId: app.appId, + eventSource: EventSource.PORTAL, + }) + if (get(appStore).url) { + window.open(`/app${app.url}`) + } else { + window.open(`/${app.appId}`) + } + } +} + +export const deploymentStore = new DeploymentStore() diff --git a/packages/builder/src/stores/builder/deployments.ts b/packages/builder/src/stores/builder/deployments.ts deleted file mode 100644 index 122ee43c20..0000000000 --- a/packages/builder/src/stores/builder/deployments.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { writable, type Writable } from "svelte/store" -import { API } from "@/api" -import { notifications } from "@budibase/bbui" -import { DeploymentProgressResponse } from "@budibase/types" - -export const createDeploymentStore = () => { - let store: Writable = writable([]) - - const load = async (): Promise => { - try { - store.set(await API.getAppDeployments()) - } catch (err) { - notifications.error("Error fetching deployments") - } - } - - return { - subscribe: store.subscribe, - load, - } -} - -export const deploymentStore = createDeploymentStore() diff --git a/packages/builder/src/stores/builder/index.ts b/packages/builder/src/stores/builder/index.ts index 7b4455216a..75b4dc6313 100644 --- a/packages/builder/src/stores/builder/index.ts +++ b/packages/builder/src/stores/builder/index.ts @@ -14,7 +14,7 @@ import { evaluationContext, } from "./automations" import { userStore, userSelectedResourceMap, isOnlyUser } from "./users" -import { deploymentStore } from "./deployments" +import { deploymentStore } from "./deployment" import { contextMenuStore } from "./contextMenu" import { snippets } from "./snippets" import { @@ -36,7 +36,6 @@ import { queries } from "./queries" import { flags } from "./flags" import { rowActions } from "./rowActions" import componentTreeNodesStore from "./componentTreeNodes" -import { appPublished } from "./published" import { oauth2 } from "./oauth2" import { FetchAppPackageResponse } from "@budibase/types" @@ -75,7 +74,6 @@ export { hoverStore, snippets, rowActions, - appPublished, evaluationContext, screenComponentsList, screenComponentErrors, diff --git a/packages/builder/src/stores/builder/published.ts b/packages/builder/src/stores/builder/published.ts deleted file mode 100644 index c38f3bb718..0000000000 --- a/packages/builder/src/stores/builder/published.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { appStore } from "./app" -import { appsStore } from "@/stores/portal/apps" -import { deploymentStore } from "./deployments" -import { derived, type Readable } from "svelte/store" -import { DeploymentProgressResponse, DeploymentStatus } from "@budibase/types" - -export const appPublished: Readable = derived( - [appStore, appsStore, deploymentStore], - ([$appStore, $appsStore, $deploymentStore]) => { - const app = $appsStore.apps.find(app => app.devId === $appStore.appId) - const deployments = $deploymentStore.filter( - (x: DeploymentProgressResponse) => x.status === DeploymentStatus.SUCCESS - ) - return app?.status === "published" && deployments.length > 0 - } -) diff --git a/packages/builder/src/stores/portal/admin.test.js b/packages/builder/src/stores/portal/admin.test.js index 8924a5e6fb..1ebbcb3dd9 100644 --- a/packages/builder/src/stores/portal/admin.test.js +++ b/packages/builder/src/stores/portal/admin.test.js @@ -2,10 +2,10 @@ import { it, expect, describe, beforeEach, vi } from "vitest" import { AdminStore } from "./admin" import { writable, get } from "svelte/store" import { API } from "@/api" -import { auth } from "@/stores/portal" +import { auth } from "./auth" import { banner } from "@budibase/bbui" -vi.mock("@/stores/portal", () => { +vi.mock("./auth", () => { return { auth: vi.fn() } }) diff --git a/packages/builder/src/stores/portal/admin.ts b/packages/builder/src/stores/portal/admin.ts index 6ac8b00b73..baeb3a66f0 100644 --- a/packages/builder/src/stores/portal/admin.ts +++ b/packages/builder/src/stores/portal/admin.ts @@ -1,6 +1,6 @@ import { get } from "svelte/store" import { API } from "@/api" -import { auth } from "@/stores/portal" +import { auth } from "./auth" import { banner } from "@budibase/bbui" import { ConfigChecklistResponse, diff --git a/packages/builder/src/stores/portal/auth.ts b/packages/builder/src/stores/portal/auth.ts index b0b5fc4acb..2684ae8c78 100644 --- a/packages/builder/src/stores/portal/auth.ts +++ b/packages/builder/src/stores/portal/auth.ts @@ -1,6 +1,6 @@ import { get } from "svelte/store" import { API } from "@/api" -import { admin } from "@/stores/portal" +import { admin } from "./admin" import analytics from "@/analytics" import { BudiStore } from "@/stores/BudiStore" import { diff --git a/packages/client/manifest.json b/packages/client/manifest.json index 46b1251da1..67f2b045b7 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -639,6 +639,7 @@ "hAlign": "center", "vAlign": "center" }, + "styles": ["margin", "background", "font"], "settings": [ { "type": "text", diff --git a/packages/client/src/components/app/Button.svelte b/packages/client/src/components/app/Button.svelte index 29bdd46a70..6183ed4252 100644 --- a/packages/client/src/components/app/Button.svelte +++ b/packages/client/src/components/app/Button.svelte @@ -23,6 +23,9 @@ $: $component.editing && node?.focus() $: componentText = getComponentText(text, $builderStore, $component) + $: customBg = + $component.styles?.normal?.background || + $component.styles?.normal?.["background-image"] const getComponentText = (text, builderState, componentState) => { if (componentState.editing) { @@ -49,16 +52,17 @@ {#key $component.editing}