From 5104631d2097cbbd243eafde0b6a0c8f2ec5e41d Mon Sep 17 00:00:00 2001 From: Dean Date: Thu, 5 May 2022 12:52:17 +0100 Subject: [PATCH 1/9] Initial Commit for app overview --- .../src/components/common/AppLockModal.svelte | 135 ++++++++++ .../src/components/common/DashCard.svelte | 62 +++++ .../src/components/start/AppRow.svelte | 46 +--- .../pages/builder/portal/apps/index.svelte | 5 + .../overview/[application]/index.svelte | 254 ++++++++++++++++++ .../overview/_components/AutomationTab.svelte | 11 + .../overview/_components/OverviewTab.svelte | 174 ++++++++++++ .../overview/_components/SelfHostTab.svelte | 37 +++ .../_components/VersionModalBody.svelte | 0 .../overview/_components/VersionTab.svelte | 48 ++++ packages/server/src/middleware/builder.js | 27 +- packages/server/src/utilities/redis.js | 2 + 12 files changed, 763 insertions(+), 38 deletions(-) create mode 100644 packages/builder/src/components/common/AppLockModal.svelte create mode 100644 packages/builder/src/components/common/DashCard.svelte create mode 100644 packages/builder/src/pages/builder/portal/overview/[application]/index.svelte create mode 100644 packages/builder/src/pages/builder/portal/overview/_components/AutomationTab.svelte create mode 100644 packages/builder/src/pages/builder/portal/overview/_components/OverviewTab.svelte create mode 100644 packages/builder/src/pages/builder/portal/overview/_components/SelfHostTab.svelte create mode 100644 packages/builder/src/pages/builder/portal/overview/_components/VersionModalBody.svelte create mode 100644 packages/builder/src/pages/builder/portal/overview/_components/VersionTab.svelte diff --git a/packages/builder/src/components/common/AppLockModal.svelte b/packages/builder/src/components/common/AppLockModal.svelte new file mode 100644 index 0000000000..98da510a89 --- /dev/null +++ b/packages/builder/src/components/common/AppLockModal.svelte @@ -0,0 +1,135 @@ + + +
+ {#if lockedBy} + + {/if} +
+ + + +

+ Apps are locked to prevent work from being lost from overlapping changes + between your team. +

+ + {#if lockedByYou && lockExpiry > 0} + {processStringSync( + "This lock will expire in {{ duration time 'millisecond' }} from now", + { + time: lockExpiry, + } + )} + {/if} +
+ + + {#if lockedByYou && lockExpiry > 0} + + {/if} + +
+
+
+ + diff --git a/packages/builder/src/components/common/DashCard.svelte b/packages/builder/src/components/common/DashCard.svelte new file mode 100644 index 0000000000..c6d52489f6 --- /dev/null +++ b/packages/builder/src/components/common/DashCard.svelte @@ -0,0 +1,62 @@ + + +
+
{ + if (actionDefined) { + action() + } + }} + > + + {title} + + + {#if actionDefined} + + {/if} + +
+
+ +
+
+ + diff --git a/packages/builder/src/components/start/AppRow.svelte b/packages/builder/src/components/start/AppRow.svelte index ea2f005216..e81d04c4d2 100644 --- a/packages/builder/src/components/start/AppRow.svelte +++ b/packages/builder/src/components/start/AppRow.svelte @@ -1,22 +1,15 @@ @@ -26,7 +19,7 @@
-
editApp(app)}> +
appOverview(app)}> {app.name} @@ -43,19 +36,7 @@ {/if}
- - {#if app.lockedYou} - Locked by you - {:else if app.lockedOther} - Locked by {app.lockedBy.email} - {:else} - Open - {/if} - +
@@ -70,23 +51,15 @@
- {#if app.deployed} - - {:else} - - {/if} +
@@ -119,6 +92,7 @@ } .app-status { display: grid; + grid-gap: var(--spacing-s); grid-template-columns: 24px 100px; } .app-status span.disabled { diff --git a/packages/builder/src/pages/builder/portal/apps/index.svelte b/packages/builder/src/pages/builder/portal/apps/index.svelte index a25e7e2c6a..9b70068e10 100644 --- a/packages/builder/src/pages/builder/portal/apps/index.svelte +++ b/packages/builder/src/pages/builder/portal/apps/index.svelte @@ -182,6 +182,10 @@ window.open(`/${app.devId}`) } + const appOverview = app => { + $goto(`../overview/${app.devId}`) + } + const editApp = app => { if (app.lockedOther) { notifications.error( @@ -404,6 +408,7 @@ {deleteApp} {updateApp} {previewApp} + {appOverview} /> {/each}
diff --git a/packages/builder/src/pages/builder/portal/overview/[application]/index.svelte b/packages/builder/src/pages/builder/portal/overview/[application]/index.svelte new file mode 100644 index 0000000000..46fd95c1e9 --- /dev/null +++ b/packages/builder/src/pages/builder/portal/overview/[application]/index.svelte @@ -0,0 +1,254 @@ + + + + + + + + {#await promise} +
+ +
+ {:then _} +
+
+ +
+ {selectedApp?.name} +
{appUrl}
+
+
+
+ + + + + +
+
+ { + selectedTab = e.detail + }} + > + + + + +
Automation History contents
+
+ +
Backups contents
+
+ + + + + + +
+ {:catch error} +

Something went wrong: {error.message}

+ {/await} +
+
+ + diff --git a/packages/builder/src/pages/builder/portal/overview/_components/AutomationTab.svelte b/packages/builder/src/pages/builder/portal/overview/_components/AutomationTab.svelte new file mode 100644 index 0000000000..56dcc40f87 --- /dev/null +++ b/packages/builder/src/pages/builder/portal/overview/_components/AutomationTab.svelte @@ -0,0 +1,11 @@ + + +
+ + diff --git a/packages/builder/src/pages/builder/portal/overview/_components/OverviewTab.svelte b/packages/builder/src/pages/builder/portal/overview/_components/OverviewTab.svelte new file mode 100644 index 0000000000..4319f51e0c --- /dev/null +++ b/packages/builder/src/pages/builder/portal/overview/_components/OverviewTab.svelte @@ -0,0 +1,174 @@ + + +
+
+ +
+
+ {#if isPublished} + + Published + {:else} + + Unpublished + {/if} +
+ +

+ {#if deployments?.length} + {processStringSync( + "Last published {{ duration time 'millisecond' }} ago", + { + time: + new Date().getTime() - + new Date(deployments[0].updatedAt).getTime(), + } + )} + {/if} + {#if !deployments?.length} + - + {/if} +

+
+
+ + {app.updatedAt} +
+ +
+ + +
{app.updatedBy.firstName}
+
+

+ {#if app} + {processStringSync( + "Last edited {{ duration time 'millisecond' }} ago", + { + time: new Date().getTime() - new Date(app?.updatedAt).getTime(), + } + )} + {/if} +

+
+
+ { + navigateTab("App Version") + }} + > +
+ {app?.version} + {#if updateAvailable} +

+ New version {clientPackage.version} is available - + { + if (typeof navigateTab === "function") { + navigateTab("App Version") + } + }} + > + Update + +

+ {:else} +

You're running the latest!

+ {/if} +
+
+
+
+ { + navigateTab("Automation History") + }} + > +
+
+
+ 0 +
+ + Success +
+
+
+ 0 +
+ + Error +
+
+
+
+
+ +
test
+
+
+
+ + diff --git a/packages/builder/src/pages/builder/portal/overview/_components/SelfHostTab.svelte b/packages/builder/src/pages/builder/portal/overview/_components/SelfHostTab.svelte new file mode 100644 index 0000000000..16804a8eb6 --- /dev/null +++ b/packages/builder/src/pages/builder/portal/overview/_components/SelfHostTab.svelte @@ -0,0 +1,37 @@ + + +
+ + + + Self-host Budibase + + +

+ Self-host Budibase for free to get unlimited apps and more - and it + only takes a few minutes! +

+
+ +
+ +
+
+
+
+ + diff --git a/packages/builder/src/pages/builder/portal/overview/_components/VersionModalBody.svelte b/packages/builder/src/pages/builder/portal/overview/_components/VersionModalBody.svelte new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/builder/src/pages/builder/portal/overview/_components/VersionTab.svelte b/packages/builder/src/pages/builder/portal/overview/_components/VersionTab.svelte new file mode 100644 index 0000000000..583c6af603 --- /dev/null +++ b/packages/builder/src/pages/builder/portal/overview/_components/VersionTab.svelte @@ -0,0 +1,48 @@ + + +
+ + + + App Version + + + {#if updateAvailable} +

+ The app is currently using version {app?.version} + but version {clientPackage.version} is available. +

+ {:else} +

+ The app is currently using version {app?.version}. You're running the latest! +

+ {/if} +

+ Updates can contain new features, performance improvements and bug + fixes. +

+
+ +
+ +
+
+
+
+ + diff --git a/packages/server/src/middleware/builder.js b/packages/server/src/middleware/builder.js index f4568722c1..964efee451 100644 --- a/packages/server/src/middleware/builder.js +++ b/packages/server/src/middleware/builder.js @@ -6,7 +6,7 @@ const { setDebounce, } = require("../utilities/redis") const { doWithDB } = require("@budibase/backend-core/db") -const { DocumentTypes } = require("../db/utils") +const { DocumentTypes, getGlobalIDFromUserMetadataID } = require("../db/utils") const { PermissionTypes } = require("@budibase/backend-core/permissions") const { app: appCache } = require("@budibase/backend-core/cache") @@ -51,6 +51,21 @@ async function updateAppUpdatedAt(ctx) { await doWithDB(appId, async db => { const metadata = await db.get(DocumentTypes.APP_METADATA) metadata.updatedAt = new Date().toISOString() + + const getInitials = user => { + let initials = "" + initials += user.firstName ? user.firstName[0] : "" + initials += user.lastName ? user.lastName[0] : "" + return initials == "" ? undefined : initials + } + + metadata.updatedBy = { + email: ctx.user.email, + firstName: ctx.user.firstName, + lastName: ctx.user.lastName, + initials: getInitials(ctx.user), + _id: getGlobalIDFromUserMetadataID(ctx.user.userId), + } const response = await db.put(metadata) metadata._rev = response.rev await appCache.invalidateAppMetadata(appId, metadata) @@ -67,7 +82,15 @@ module.exports = async (ctx, permType) => { } const isBuilderApi = permType === PermissionTypes.BUILDER const referer = ctx.headers["referer"] - const editingApp = referer ? referer.includes(appId) : false + + const overviewPath = "/builder/portal/overview/" + const overviewContext = !referer ? false : referer.includes(overviewPath) + if (overviewContext) { + return + } + + const hasAppId = !referer ? false : referer.includes(appId) + const editingApp = referer ? hasAppId : false // check this is a builder call and editing if (!isBuilderApi || !editingApp) { return diff --git a/packages/server/src/utilities/redis.js b/packages/server/src/utilities/redis.js index ca2fa3254d..0c25bab27a 100644 --- a/packages/server/src/utilities/redis.js +++ b/packages/server/src/utilities/redis.js @@ -48,7 +48,9 @@ exports.updateLock = async (devAppId, user) => { ...user, userId: globalId, _id: globalId, + lockedAt: new Date().getTime(), } + await devAppClient.store(devAppId, inputUser, APP_DEV_LOCK_SECONDS) } From 8131b8fcaa568c32579706fb9d1a459756ad10fa Mon Sep 17 00:00:00 2001 From: Dean Date: Fri, 6 May 2022 15:52:49 +0100 Subject: [PATCH 2/9] Overview Tab refactoring and general updates to the homepage --- .../src/components/common/AppLockModal.svelte | 20 ++- .../src/components/deploy/VersionModal.svelte | 18 ++- .../src/components/start/AppRow.svelte | 2 +- .../pages/builder/portal/apps/index.svelte | 82 +++++------ .../overview/[application]/index.svelte | 50 ++----- .../overview/_components/OverviewTab.svelte | 64 +++++++-- .../overview/_components/SelfHostTab.svelte | 37 ----- .../overview/_components/SettingsTab.svelte | 131 ++++++++++++++++++ .../_components/VersionModalBody.svelte | 0 .../overview/_components/VersionTab.svelte | 48 ------- packages/server/src/middleware/builder.js | 14 +- 11 files changed, 265 insertions(+), 201 deletions(-) delete mode 100644 packages/builder/src/pages/builder/portal/overview/_components/SelfHostTab.svelte create mode 100644 packages/builder/src/pages/builder/portal/overview/_components/SettingsTab.svelte delete mode 100644 packages/builder/src/pages/builder/portal/overview/_components/VersionModalBody.svelte delete mode 100644 packages/builder/src/pages/builder/portal/overview/_components/VersionTab.svelte diff --git a/packages/builder/src/components/common/AppLockModal.svelte b/packages/builder/src/components/common/AppLockModal.svelte index 98da510a89..a08893a37d 100644 --- a/packages/builder/src/components/common/AppLockModal.svelte +++ b/packages/builder/src/components/common/AppLockModal.svelte @@ -5,20 +5,23 @@ ModalContent, Modal, notifications, + ProgressCircle, } from "@budibase/bbui" import { auth, apps } from "stores/portal" import { processStringSync } from "@budibase/string-templates" import { API } from "api" export let app + export let buttonSize = "M" - let APP_DEV_LOCK_SECONDS = 600 + let APP_DEV_LOCK_SECONDS = 600 //common area for this? let appLockModal + let processing = false $: lockedBy = app?.lockedBy $: lockedByYou = $auth.user.email === lockedBy?.email $: lockIdentifer = `${ - Object.prototype.hasOwnProperty.call(lockedBy, "firstName") + lockedBy && Object.prototype.hasOwnProperty.call(lockedBy, "firstName") ? lockedBy?.firstName : lockedBy?.email }` @@ -28,7 +31,7 @@ $: lockExpiry = getExpiryDuration(app) const getExpiryDuration = app => { - if (!app.lockedBy) { + if (!app?.lockedBy?.lockedAt) { return -1 } let expiry = @@ -37,6 +40,7 @@ } const releaseLock = async () => { + processing = true if (app) { try { await API.releaseAppLock(app.devId) @@ -48,6 +52,7 @@ } else { notifications.error("No application is selected") } + processing = false } @@ -57,6 +62,7 @@ quiet secondary icon="LockClosed" + size={buttonSize} on:click={() => { appLockModal.show() }} @@ -93,6 +99,7 @@ {/if} diff --git a/packages/builder/src/components/deploy/VersionModal.svelte b/packages/builder/src/components/deploy/VersionModal.svelte index 9707517c54..3ddc7a1115 100644 --- a/packages/builder/src/components/deploy/VersionModal.svelte +++ b/packages/builder/src/components/deploy/VersionModal.svelte @@ -11,6 +11,16 @@ import { API } from "api" import clientPackage from "@budibase/client/package.json" + export function show() { + updateModal.show() + } + + export function hide() { + updateModal.hide() + } + + export let hideIcon = false + let updateModal $: appId = $store.appId @@ -57,9 +67,11 @@ } -
- -
+{#if !hideIcon} +
+ +
+{/if}
- +
diff --git a/packages/builder/src/pages/builder/portal/apps/index.svelte b/packages/builder/src/pages/builder/portal/apps/index.svelte index 9b70068e10..9ee3854998 100644 --- a/packages/builder/src/pages/builder/portal/apps/index.svelte +++ b/packages/builder/src/pages/builder/portal/apps/index.svelte @@ -2,7 +2,6 @@ import { Heading, Layout, - Detail, Button, Input, Select, @@ -312,48 +311,7 @@ {welcomeBody} - -
- - {#if $apps?.length > 0} - - {/if} - {#if !$apps?.length} - - {/if} -
-
- - - -
-
{#if !$apps?.length && $templates?.length} @@ -363,7 +321,40 @@ {#if enrichedApps.length}
- Apps +
+ + {#if $apps?.length > 0} + + {/if} + {#if !$apps?.length} + + {/if} +
{#if enrichedApps.length > 1}
{#if cloud} @@ -394,7 +385,7 @@
{/if}
- +
{#each filteredApps as app (app.appId)} button) { margin-right: 10px; } - .title .welcome > .buttons { - padding-top: 30px; - } .title { display: flex; flex-direction: row; diff --git a/packages/builder/src/pages/builder/portal/overview/[application]/index.svelte b/packages/builder/src/pages/builder/portal/overview/[application]/index.svelte index 46fd95c1e9..b60f601b12 100644 --- a/packages/builder/src/pages/builder/portal/overview/[application]/index.svelte +++ b/packages/builder/src/pages/builder/portal/overview/[application]/index.svelte @@ -13,12 +13,9 @@ ProgressCircle, } from "@budibase/bbui" import OverviewTab from "../_components/OverviewTab.svelte" - import VersionTab from "../_components/VersionTab.svelte" - import SelfHostTab from "../_components/SelfHostTab.svelte" + import SettingsTab from "../_components/SettingsTab.svelte" import { API } from "api" - import { onMount } from "svelte" import { store } from "builderStore" - import { roles, flags } from "stores/backend" import { apps, auth } from "stores/portal" import analytics, { Events, EventSource } from "analytics" import { AppStatus } from "constants" @@ -37,7 +34,7 @@ $: lockedBy = selectedApp?.lockedBy $: lockedByYou = $auth.user.email === lockedBy?.email $: lockIdentifer = `${ - Object.prototype.hasOwnProperty.call(lockedBy, "firstName") + lockedBy && Object.prototype.hasOwnProperty.call(lockedBy, "firstName") ? lockedBy?.firstName : lockedBy?.email }` @@ -55,13 +52,7 @@ selectedApp?.status === AppStatus.DEPLOYED && latestDeployments?.length > 0 $: appUrl = `${window.origin}/app${selectedApp?.url}` - $: tabs = [ - "Overview", - "Automation History", - "Backups", - "App Version", - "Self-host", - ] + $: tabs = ["Overview", "Automation History", "Backups", "Settings"] $: selectedTab = "Overview" const handleTabChange = tabKey => { @@ -78,9 +69,8 @@ try { const pkg = await API.fetchAppPackage(application) await store.actions.initialise(pkg) - // await automationStore.actions.fetch() - await roles.fetch() - await flags.fetch() + await apps.load() + deployments = await fetchDeployments() return pkg } catch (error) { notifications.error(`Error initialising app: ${error?.message}`) @@ -108,8 +98,6 @@ } } - //Show prod: published, appUrl - //Behaviour incorrect. It should be enabled if at least 1 live deployment is available const viewApp = () => { if (isPublished) { analytics.captureEvent(Events.APP.VIEW_PUBLISHED, { @@ -129,18 +117,6 @@ } $goto(`../../../app/${app.devId}`) } - - onMount(async () => { - // if (!hasSynced && application) { - // try { - // await API.syncApp(application) - // } catch (error) { - // notifications.error("Failed to sync with production database") - // } - // hasSynced = true - // } - deployments = await fetchDeployments() - }) @@ -187,7 +163,14 @@ disabled={!isPublished} on:click={viewApp}>View app - @@ -213,11 +196,8 @@
Backups contents
- - - - - + + {:catch error} diff --git a/packages/builder/src/pages/builder/portal/overview/_components/OverviewTab.svelte b/packages/builder/src/pages/builder/portal/overview/_components/OverviewTab.svelte index 4319f51e0c..32bae0a6f5 100644 --- a/packages/builder/src/pages/builder/portal/overview/_components/OverviewTab.svelte +++ b/packages/builder/src/pages/builder/portal/overview/_components/OverviewTab.svelte @@ -1,17 +1,43 @@
@@ -46,14 +72,21 @@
- {app.updatedAt}
- -
- - -
{app.updatedBy.firstName}
-
+ {#await userPromise} + + {:then _} +
+ {#if appEditor} + +
+ {appEditor._id === $auth.user._id ? "You" : appEditorText} +
+ {/if} +
+ {:catch error} +

Could not fetch user: {error.message}

+ {/await}

{#if app} {processStringSync( @@ -70,7 +103,7 @@ title={"App Version"} showIcon={true} action={() => { - navigateTab("App Version") + navigateTab("Settings") }} >

@@ -81,7 +114,7 @@ { if (typeof navigateTab === "function") { - navigateTab("App Version") + navigateTab("Settings") } }} > @@ -120,7 +153,12 @@
- + { + navigateTab("Backups") + }} + >
test
diff --git a/packages/builder/src/pages/builder/portal/overview/_components/SelfHostTab.svelte b/packages/builder/src/pages/builder/portal/overview/_components/SelfHostTab.svelte deleted file mode 100644 index 16804a8eb6..0000000000 --- a/packages/builder/src/pages/builder/portal/overview/_components/SelfHostTab.svelte +++ /dev/null @@ -1,37 +0,0 @@ - - -
- - - - Self-host Budibase - - -

- Self-host Budibase for free to get unlimited apps and more - and it - only takes a few minutes! -

-
- -
- -
-
-
-
- - diff --git a/packages/builder/src/pages/builder/portal/overview/_components/SettingsTab.svelte b/packages/builder/src/pages/builder/portal/overview/_components/SettingsTab.svelte new file mode 100644 index 0000000000..1900ddd244 --- /dev/null +++ b/packages/builder/src/pages/builder/portal/overview/_components/SettingsTab.svelte @@ -0,0 +1,131 @@ + + +
+ + + + Name and Url + + +
+
+
Name
+
{app?.name}
+
+
+
Url Path
+
{appUrl}
+
+
+
+ +
+ +
+ + App Version + + + {#if updateAvailable} +

+ The app is currently using version {app?.version} + but version {clientPackage.version} is available. +

+ {:else} +

+ The app is currently using version {app?.version}. You're running the latest! +

+ {/if} +

+ Updates can contain new features, performance improvements and bug + fixes. +

+
+ +
+ +
+ + Self-host Budibase + + +

+ Self-host Budibase for free to get unlimited apps and more - and it + only takes a few minutes! +

+
+ +
+ +
+
+ + + + +
+
+ + diff --git a/packages/builder/src/pages/builder/portal/overview/_components/VersionModalBody.svelte b/packages/builder/src/pages/builder/portal/overview/_components/VersionModalBody.svelte deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/builder/src/pages/builder/portal/overview/_components/VersionTab.svelte b/packages/builder/src/pages/builder/portal/overview/_components/VersionTab.svelte deleted file mode 100644 index 583c6af603..0000000000 --- a/packages/builder/src/pages/builder/portal/overview/_components/VersionTab.svelte +++ /dev/null @@ -1,48 +0,0 @@ - - -
- - - - App Version - - - {#if updateAvailable} -

- The app is currently using version {app?.version} - but version {clientPackage.version} is available. -

- {:else} -

- The app is currently using version {app?.version}. You're running the latest! -

- {/if} -

- Updates can contain new features, performance improvements and bug - fixes. -

-
- -
- -
-
-
-
- - diff --git a/packages/server/src/middleware/builder.js b/packages/server/src/middleware/builder.js index 964efee451..b63a086d48 100644 --- a/packages/server/src/middleware/builder.js +++ b/packages/server/src/middleware/builder.js @@ -52,20 +52,8 @@ async function updateAppUpdatedAt(ctx) { const metadata = await db.get(DocumentTypes.APP_METADATA) metadata.updatedAt = new Date().toISOString() - const getInitials = user => { - let initials = "" - initials += user.firstName ? user.firstName[0] : "" - initials += user.lastName ? user.lastName[0] : "" - return initials == "" ? undefined : initials - } + metadata.updatedBy = getGlobalIDFromUserMetadataID(ctx.user.userId) - metadata.updatedBy = { - email: ctx.user.email, - firstName: ctx.user.firstName, - lastName: ctx.user.lastName, - initials: getInitials(ctx.user), - _id: getGlobalIDFromUserMetadataID(ctx.user.userId), - } const response = await db.put(metadata) metadata._rev = response.rev await appCache.invalidateAppMetadata(appId, metadata) From 5f1e7bdee00187bbe6987bd10142e37178c6dd8a Mon Sep 17 00:00:00 2001 From: Dean Date: Mon, 9 May 2022 14:41:53 +0100 Subject: [PATCH 3/9] Fixes for tab underline behaviour. Fix for overview initialisation via URL. Fix for clearing the store when navigating away from the overview tab --- packages/bbui/src/Tabs/Tab.svelte | 24 ++++++++-- .../src/components/common/AppLockModal.svelte | 2 +- .../src/components/common/EditableIcon.svelte | 47 +++++++++++++++++++ .../overview/[application]/index.svelte | 44 +++++++++++------ 4 files changed, 98 insertions(+), 19 deletions(-) create mode 100644 packages/builder/src/components/common/EditableIcon.svelte diff --git a/packages/bbui/src/Tabs/Tab.svelte b/packages/bbui/src/Tabs/Tab.svelte index 0aa59f7f8a..04791619dc 100644 --- a/packages/bbui/src/Tabs/Tab.svelte +++ b/packages/bbui/src/Tabs/Tab.svelte @@ -6,7 +6,7 @@ const dispatch = createEventDispatcher() let selected = getContext("tab") - let tab + let tab_internal let tabInfo const setTabInfo = () => { @@ -16,7 +16,7 @@ // We just need to get this off the main thread to fix this, by using // a 0ms timeout. setTimeout(() => { - tabInfo = tab?.getBoundingClientRect() + tabInfo = tab_internal?.getBoundingClientRect() if (tabInfo && $selected.title === title) { $selected.info = tabInfo } @@ -27,14 +27,30 @@ setTabInfo() }) + //Ensure that the underline is in the correct location + $: { + if ($selected.title === title && tab_internal) { + if ($selected.info?.left !== tab_internal.getBoundingClientRect().left) { + $selected = { + ...$selected, + info: tab_internal.getBoundingClientRect(), + } + } + } + } + const onClick = () => { - $selected = { ...$selected, title, info: tab.getBoundingClientRect() } + $selected = { + ...$selected, + title, + info: tab_internal.getBoundingClientRect(), + } dispatch("click") }
{lockedBy && !lockedByYou ? "Done" : "Cancel"} - {#if lockedByYou && lockExpiry > 0} + {#if lockedByYou} @@ -145,7 +156,11 @@ class="app-icon" style="color: {selectedApp?.icon?.color || ''}" > - +
@@ -167,6 +182,7 @@ size="M" cta icon="Edit" + disabled={lockedBy && !lockedByYou} on:click={() => { editApp(selectedApp) }} From 97210280d51c1339f332c4e0abb476df9eb38216 Mon Sep 17 00:00:00 2001 From: Dean Date: Mon, 9 May 2022 16:31:17 +0100 Subject: [PATCH 4/9] Added the app creation buttons back into the header of the page. Previously removed by mistake --- .../pages/builder/portal/apps/index.svelte | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/builder/src/pages/builder/portal/apps/index.svelte b/packages/builder/src/pages/builder/portal/apps/index.svelte index 9ee3854998..291eef0821 100644 --- a/packages/builder/src/pages/builder/portal/apps/index.svelte +++ b/packages/builder/src/pages/builder/portal/apps/index.svelte @@ -301,7 +301,7 @@ - + {#if loaded}
@@ -311,6 +311,29 @@ {welcomeBody} + {#if !$apps?.length} +
+ + +
+ {/if}
@@ -466,6 +489,9 @@ .app-actions :global(> button) { margin-right: 10px; } + .title .welcome > .buttons { + padding-top: var(--spacing-l); + } .title { display: flex; flex-direction: row; From 7ec75361bec4bf43cd30dc7d7572cb18e9a5ade8 Mon Sep 17 00:00:00 2001 From: Dean Date: Mon, 9 May 2022 18:00:04 +0100 Subject: [PATCH 5/9] Cypress test fixes --- .../cypress/integration/appPublishWorkflow.spec.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/builder/cypress/integration/appPublishWorkflow.spec.js b/packages/builder/cypress/integration/appPublishWorkflow.spec.js index d18233e0e7..71afd5accf 100644 --- a/packages/builder/cypress/integration/appPublishWorkflow.spec.js +++ b/packages/builder/cypress/integration/appPublishWorkflow.spec.js @@ -19,7 +19,7 @@ filterTests(['all'], () => { cy.get(".appTable .app-row-actions").eq(0) .within(() => { - cy.get(".spectrum-Button").contains("Preview") + cy.get(".spectrum-Button").contains("View") cy.get(".spectrum-Button").contains("Edit").click({ force: true }) }) @@ -57,7 +57,7 @@ filterTests(['all'], () => { cy.get(".appTable .app-row-actions").eq(0) .within(() => { - cy.get(".spectrum-Button").contains("View app") + cy.get(".spectrum-Button").contains("View") cy.get(".spectrum-Button").contains("Edit").click({ force: true }) }) @@ -66,7 +66,7 @@ filterTests(['all'], () => { cy.get("[data-cy='publish-popover-menu']").should("be.visible") .within(() => { cy.get("[data-cy='publish-popover-action']").should("exist") - cy.get("button").contains("View app").should("exist") + cy.get("button").contains("View").should("exist") cy.get(".publish-popover-message").should("have.text", "Last published a few seconds ago") }) }) @@ -84,7 +84,7 @@ filterTests(['all'], () => { cy.get(".appTable .app-row-actions").eq(0) .within(() => { - cy.get(".spectrum-Button").contains("View app") + cy.get(".spectrum-Button").contains("View") cy.get(".spectrum-Button").contains("Edit").click({ force: true }) }) From 43cd89a12179d6343788a871577894b0c15b1492 Mon Sep 17 00:00:00 2001 From: Dean Date: Tue, 10 May 2022 16:58:55 +0100 Subject: [PATCH 6/9] Cypress tests for the overview tab --- .../cypress/integration/appOverview.spec.js | 215 ++++++++++++++++++ packages/builder/cypress/support/commands.js | 62 +++++ .../src/components/common/AppLockModal.svelte | 14 +- .../src/components/common/DashCard.svelte | 3 +- .../src/components/start/AppRow.svelte | 2 +- .../overview/[application]/index.svelte | 5 +- .../overview/_components/OverviewTab.svelte | 7 +- 7 files changed, 297 insertions(+), 11 deletions(-) create mode 100644 packages/builder/cypress/integration/appOverview.spec.js diff --git a/packages/builder/cypress/integration/appOverview.spec.js b/packages/builder/cypress/integration/appOverview.spec.js new file mode 100644 index 0000000000..893386385e --- /dev/null +++ b/packages/builder/cypress/integration/appOverview.spec.js @@ -0,0 +1,215 @@ +import filterTests from "../support/filterTests" + +filterTests(['all'], () => { + context("Application Overview screen", () => { + before(() => { + cy.login() + cy.createTestApp() + }) + + /* + The ability to switch accounts will deffo be necessary + + x APP Screen + Click on app name + Click on edit + + x Edit Icon Functionality + + Lock button display and behaviour + + Edit button status and behaviour + + ** To ratifying alot of this stuff will require the app itself + cy.visit(`${Cypress.config().baseUrl}/builder`) + cy.wait(2000) + cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`) + .its("body") + .then(val => { + const findAppName = val.some(val => val.name == name) + if (findAppName) { + + Publish Card + x Never published + Unpublished + Should show the last valid publish date + x Published + Should show the valid publish date + Edit Card + D or DH or the first letter of their email. + Timestamp vaguely matches. + Version Card + How to get use cases? Available/Unavailable? + + Settings Tab + Edit Url + Edit Name + + Versioning? + + Tab behaviour + Click all 3 main tabs. + Check positioning. + Dashcard behaviour. Concentrate on the App version + Header behaviour on click + Quick link behaviour + + Locking behaviour + Button contents, D or DH or the first letter of their email. + Locked by you + Locked by someone else + + Timeout check. Apps > edit > back > app title + "This lock will expire in 10 minutes from now" + */ + + it("Should be accessible from the applications list", () => { + cy.visit(`${Cypress.config().baseUrl}/builder`) + + // Reusable command? toAppOverview + cy.get(".appTable .title").eq(0) + .invoke('attr', 'data-cy') + .then(($dataCy) => { + const dataCy = $dataCy; + cy.get(".appTable .name").eq(0).click() + + cy.location().should((loc) => { + expect(loc.pathname).to.eq('/builder/portal/overview/' + dataCy) + }) + }) + + cy.visit(`${Cypress.config().baseUrl}/builder`) + + cy.get(".appTable .title").eq(0) + .invoke('attr', 'data-cy') + .then(($dataCy) => { + const dataCy = $dataCy; + cy.get(".appTable .app-row-actions button").contains("View").click({force: true}) + + cy.location().should((loc) => { + expect(loc.pathname).to.eq('/builder/portal/overview/' + dataCy) + }) + }) + + }) + + // Find a more suitable place for this. + it("Should allow unlocking in the app list", () => { + cy.visit(`${Cypress.config().baseUrl}/builder`) + + cy.get(".appTable .lock-status").eq(0).contains("Locked by you").click() + + cy.unlockApp({ owned : true }) + + cy.get(".appTable").should("exist") + cy.get(".lock-status").should('not.be.visible') + }) + + it("Should allow unlocking in the app overview screen", () => { + cy.visit(`${Cypress.config().baseUrl}/builder`) + + cy.get(".appTable .app-row-actions button").contains("Edit").eq(0).click({force: true}) + cy.wait(1000) + cy.visit(`${Cypress.config().baseUrl}/builder`) + + cy.get(".appTable .name").eq(0).click() + + cy.get(".lock-status").eq(0).contains("Locked by you").click() + + cy.unlockApp({ owned : true }) + + cy.get(".lock-status").should("not.be.visible") + }) + + it("Should reflect the deploy state of an app that hasn't been published.", () => { + cy.visit(`${Cypress.config().baseUrl}/builder`) + + cy.get(".appTable .name").eq(0).click() + + cy.get(".header-right button.spectrum-Button[data-cy='view-app']").should("be.disabled") + + cy.get(".spectrum-Tabs-item.is-selected").contains("Overview") + cy.get(".overview-tab").should("be.visible") + + cy.get(".overview-tab [data-cy='app-status']").within(() => { + cy.get(".status-display").contains("Unpublished") + cy.get(".status-display .icon svg[aria-label='GlobeStrike']").should("exist") + cy.get(".status-text").contains("-") + }) + }) + + it("Should reflect the app deployment state", () => { + cy.visit(`${Cypress.config().baseUrl}/builder`) + cy.get(".appTable .app-row-actions button").contains("Edit").eq(0).click({force: true}) + + cy.get(".toprightnav button.spectrum-Button").contains("Publish").click({ force : true }) + cy.get(".spectrum-Modal [data-cy='deploy-app-modal']").should("be.visible") + .within(() => { + cy.get(".spectrum-Button").contains("Publish").click({ force : true }) + cy.wait(1000) + }); + + cy.visit(`${Cypress.config().baseUrl}/builder`) + cy.get(".appTable .name").eq(0).click() + + cy.get(".header-right button.spectrum-Button[data-cy='view-app']").should("not.be.disabled") + + cy.get(".overview-tab [data-cy='app-status']").within(() => { + cy.get(".status-display").contains("Published") + cy.get(".status-display .icon svg[aria-label='GlobeCheck']").should("exist") + cy.get(".status-text").contains("Last published a few seconds ago") + }) + }) + + it("Should reflect an application that has been unpublished", () => { + cy.visit(`${Cypress.config().baseUrl}/builder`) + cy.get(".appTable .app-row-actions button").contains("Edit").eq(0).click({force: true}) + + cy.get(".deployment-top-nav svg[aria-label='Globe']") + .click({ force: true }) + + cy.get("[data-cy='publish-popover-menu']").should("be.visible") + cy.get("[data-cy='publish-popover-menu'] [data-cy='publish-popover-action']") + .click({ force : true }) + + cy.get("[data-cy='unpublish-modal']").should("be.visible") + .within(() => { + cy.get(".confirm-wrap button").click({ force: true } + )}) + cy.wait(1000) + + cy.visit(`${Cypress.config().baseUrl}/builder`) + cy.get(".appTable .name").eq(0).click() + + cy.get(".overview-tab [data-cy='app-status']").within(() => { + cy.get(".status-display").contains("Unpublished") + cy.get(".status-display .icon svg[aria-label='GlobeStrike']").should("exist") + cy.get(".status-text").contains("Last published a few seconds ago") + }) + }) + + it("Should allow the editing of the application icon", () => { + cy.visit(`${Cypress.config().baseUrl}/builder`) + + cy.get(".appTable .name").eq(0).click() + + cy.get(".app-logo .edit-hover").should("exist").invoke("show").click() + + cy.customiseAppIcon() + + cy.get(".app-logo") + .within(() => { + cy.get('[aria-label]').eq(0).children() + .should('have.attr', 'xlink:href').and('not.contain', '#spectrum-icon-18-Apps') + cy.get(".app-icon") + .should('have.attr', 'style').and('contains', 'color') + }) + }) + + it("Should log out.", () => { + //You + //Last edited a few seconds ago. + cy.logOut(); + }) + }) +}) diff --git a/packages/builder/cypress/support/commands.js b/packages/builder/cypress/support/commands.js index ce1fe2ec50..c37bd0ffd0 100644 --- a/packages/builder/cypress/support/commands.js +++ b/packages/builder/cypress/support/commands.js @@ -32,6 +32,18 @@ Cypress.Commands.add("login", () => { }) }) +Cypress.Commands.add("basicOnboardAppUser", () => { + cy.createUser("another@budibase.com") +}) + +// log out. +// Cypress.Commands.add("logOut", () => { +// cy.visit(`${Cypress.config().baseUrl}/builder`) +// cy.get(".user-dropdown .avatar > .icon").click({ force: true }) + +// cy.wait(2000) +// }) + Cypress.Commands.add("closeModal", () => { cy.get(".spectrum-Modal").within(() => { cy.get(".close-icon").click() @@ -141,6 +153,56 @@ Cypress.Commands.add("deleteAllApps", () => { }) }) +Cypress.Commands.add("customiseAppIcon", () => { + // Select random icon + cy.get(".grid").within(() => { + cy.get(".icon-item") + .eq(Math.floor(Math.random() * 23) + 1) + .click() + }) + // Select random colour + cy.get(".fill").click() + cy.get(".colors").within(() => { + cy.get(".color") + .eq(Math.floor(Math.random() * 33) + 1) + .click() + }) + cy.intercept("**/applications/**").as("iconChange") + cy.get(".spectrum-Button").contains("Save").click({ force: true }) + cy.wait("@iconChange") + cy.get("@iconChange").its("response.statusCode").should("eq", 200) + cy.wait(1000) +}) + +Cypress.Commands.add("unlockApp", unlock_config => { + let config = { ...unlock_config } + + cy.get(".spectrum-Modal .spectrum-Dialog[data-cy='app-lock-modal']") + .should("be.visible") + .within(() => { + if (config.owned) { + cy.get(".spectrum-Dialog-heading").contains("Locked by you") + cy.get(".lock-expiry-body").contains( + "This lock will expire in 10 minutes from now" + ) + + cy.intercept("**/lock").as("unlockApp") + cy.get(".spectrum-Button") + .contains("Release Lock") + .click({ force: true }) + cy.wait("@unlockApp") + cy.get("@unlockApp").its("response.statusCode").should("eq", 200) + cy.get("@unlockApp").its("response.body").should("deep.equal", { + message: "Lock released successfully.", + }) + } else { + //Show the name ? + cy.get(".lock-expiry-body").should("not.be.visible") + cy.get(".spectrum-Button").contains("Done") + } + }) +}) + Cypress.Commands.add("createTestApp", () => { const appName = "Cypress Tests" cy.deleteApp(appName) diff --git a/packages/builder/src/components/common/AppLockModal.svelte b/packages/builder/src/components/common/AppLockModal.svelte index 931999ca95..a30d1c6698 100644 --- a/packages/builder/src/components/common/AppLockModal.svelte +++ b/packages/builder/src/components/common/AppLockModal.svelte @@ -87,12 +87,14 @@

{#if lockedByYou && lockExpiry > 0} - {processStringSync( - "This lock will expire in {{ duration time 'millisecond' }} from now", - { - time: lockExpiry, - } - )} + + {processStringSync( + "This lock will expire in {{ duration time 'millisecond' }} from now", + { + time: lockExpiry, + } + )} + {/if}
diff --git a/packages/builder/src/components/common/DashCard.svelte b/packages/builder/src/components/common/DashCard.svelte index c6d52489f6..1d7ea9120a 100644 --- a/packages/builder/src/components/common/DashCard.svelte +++ b/packages/builder/src/components/common/DashCard.svelte @@ -4,11 +4,12 @@ export let title = "" export let actionIcon export let action + export let dataCy $: actionDefined = typeof action === "function" -
+
{ diff --git a/packages/builder/src/components/start/AppRow.svelte b/packages/builder/src/components/start/AppRow.svelte index b329b7a84e..f197c76fdd 100644 --- a/packages/builder/src/components/start/AppRow.svelte +++ b/packages/builder/src/components/start/AppRow.svelte @@ -14,7 +14,7 @@ export let editIcon -
+
diff --git a/packages/builder/src/pages/builder/portal/overview/[application]/index.svelte b/packages/builder/src/pages/builder/portal/overview/[application]/index.svelte index 4159343d37..3dfc761a43 100644 --- a/packages/builder/src/pages/builder/portal/overview/[application]/index.svelte +++ b/packages/builder/src/pages/builder/portal/overview/[application]/index.svelte @@ -176,8 +176,11 @@ secondary icon="Globe" disabled={!isPublished} - on:click={viewApp}>View app + View app +
-
-
- -
- - - - App Version - - - {#if updateAvailable} + + + + + + App Version + + + {#if updateAvailable} +

+ The app is currently using version + {$store.version} + but version {clientPackage.version} is available. +

+ {:else} +

+ The app is currently using version + {$store.version}. You're running the latest! +

+ {/if}

- The app is currently using version {app?.version} - but version {clientPackage.version} is available. + Updates can contain new features, performance improvements and bug + fixes.

- {:else} +
+ +
+ +
+
+ + + Self-host Budibase + +

- The app is currently using version {app?.version}. You're running the latest! + Self-host Budibase for free to get unlimited apps and more - and + it only takes a few minutes!

- {/if} -

- Updates can contain new features, performance improvements and bug - fixes. -

-
- -
- -
- - Self-host Budibase - - -

- Self-host Budibase for free to get unlimited apps and more - and it - only takes a few minutes! -

-
- -
- -
+
+ +
+ + +
From 51952ee0f443d104359cade62ea7f3fceba13e9c Mon Sep 17 00:00:00 2001 From: Dean Date: Thu, 12 May 2022 09:24:19 +0100 Subject: [PATCH 8/9] Removed unused variables, hid the automation/backups tabs and removed the disable flag from the app version settings menu --- .../overview/[application]/index.svelte | 14 ++-- .../overview/_components/OverviewTab.svelte | 68 ++++++++++--------- .../overview/_components/SettingsTab.svelte | 9 +-- 3 files changed, 44 insertions(+), 47 deletions(-) diff --git a/packages/builder/src/pages/builder/portal/overview/[application]/index.svelte b/packages/builder/src/pages/builder/portal/overview/[application]/index.svelte index 3dfc761a43..703df5ccb6 100644 --- a/packages/builder/src/pages/builder/portal/overview/[application]/index.svelte +++ b/packages/builder/src/pages/builder/portal/overview/[application]/index.svelte @@ -209,12 +209,14 @@ navigateTab={handleTabChange} /> - -
Automation History contents
-
- -
Backups contents
-
+ {#if false} + +
Automation History contents
+
+ +
Backups contents
+
+ {/if} diff --git a/packages/builder/src/pages/builder/portal/overview/_components/OverviewTab.svelte b/packages/builder/src/pages/builder/portal/overview/_components/OverviewTab.svelte index 3ac8dcd728..37bf3f81b9 100644 --- a/packages/builder/src/pages/builder/portal/overview/_components/OverviewTab.svelte +++ b/packages/builder/src/pages/builder/portal/overview/_components/OverviewTab.svelte @@ -128,43 +128,45 @@
-
- { - navigateTab("Automation History") - }} - dataCy={"automation-history"} - > -
-
-
- 0 -
- - Success + {#if false} +
+ { + navigateTab("Automation History") + }} + dataCy={"automation-history"} + > +
+
+
+ 0 +
+ + Success +
-
-
- 0 -
- - Error +
+ 0 +
+ + Error +
-
-
- { - navigateTab("Backups") - }} - dataCy={"backups"} - > -
test
-
-
+ + { + navigateTab("Backups") + }} + dataCy={"backups"} + > +
test
+
+
+ {/if}