1
0
Fork 0
mirror of synced 2024-06-01 18:20:18 +12:00

Overview Tab refactoring and general updates to the homepage

This commit is contained in:
Dean 2022-05-06 15:52:49 +01:00
parent 1c5990b9d7
commit fffb4b0174
11 changed files with 265 additions and 201 deletions

View file

@ -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
}
</script>
@ -57,6 +62,7 @@
quiet
secondary
icon="LockClosed"
size={buttonSize}
on:click={() => {
appLockModal.show()
}}
@ -93,6 +99,7 @@
<Button
secondary
quiet={lockedBy && lockedByYou}
disabled={processing}
on:click={() => {
appLockModal.hide()
}}
@ -104,12 +111,17 @@
{#if lockedByYou && lockExpiry > 0}
<Button
secondary
disabled={processing}
on:click={() => {
releaseLock()
appLockModal.hide()
}}
>
<span class="unlock">Release Lock</span>
{#if processing}
<ProgressCircle overBackground={true} size="S" />
{:else}
<span class="unlock">Release Lock</span>
{/if}
</Button>
{/if}
</ButtonGroup>

View file

@ -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 @@
}
</script>
<div class="icon-wrapper" class:highlight={updateAvailable}>
<Icon name="Refresh" hoverable on:click={updateModal.show} />
</div>
{#if !hideIcon}
<div class="icon-wrapper" class:highlight={updateAvailable}>
<Icon name="Refresh" hoverable on:click={updateModal.show} />
</div>
{/if}
<Modal bind:this={updateModal}>
<ModalContent
title="App version"

View file

@ -36,7 +36,7 @@
{/if}
</div>
<div class="desktop">
<AppLockModal {app} />
<AppLockModal {app} buttonSize="S" />
</div>
<div class="desktop">
<div class="app-status">

View file

@ -2,7 +2,6 @@
import {
Heading,
Layout,
Detail,
Button,
Input,
Select,
@ -312,48 +311,7 @@
{welcomeBody}
</Body>
</Layout>
<div class="buttons">
<Button
dataCy="create-app-btn"
size="M"
icon="Add"
cta
on:click={initiateAppCreation}
>
{createAppButtonText}
</Button>
{#if $apps?.length > 0}
<Button
icon="Experience"
size="M"
quiet
secondary
on:click={$goto("/builder/portal/apps/templates")}
>
Templates
</Button>
{/if}
{#if !$apps?.length}
<Button
dataCy="import-app-btn"
icon="Import"
size="L"
quiet
secondary
on:click={initiateAppImport}
>
Import app
</Button>
{/if}
</div>
</div>
<div>
<Layout gap="S" justifyItems="center">
<img class="img-logo img-size" alt="logo" src={Logo} />
</Layout>
</div>
<Divider size="S" />
</div>
{#if !$apps?.length && $templates?.length}
@ -363,7 +321,40 @@
{#if enrichedApps.length}
<Layout noPadding gap="S">
<div class="title">
<Detail size="L">Apps</Detail>
<div class="buttons">
<Button
dataCy="create-app-btn"
size="M"
icon="Add"
cta
on:click={initiateAppCreation}
>
{createAppButtonText}
</Button>
{#if $apps?.length > 0}
<Button
icon="Experience"
size="M"
quiet
secondary
on:click={$goto("/builder/portal/apps/templates")}
>
Templates
</Button>
{/if}
{#if !$apps?.length}
<Button
dataCy="import-app-btn"
icon="Import"
size="L"
quiet
secondary
on:click={initiateAppImport}
>
Import app
</Button>
{/if}
</div>
{#if enrichedApps.length > 1}
<div class="app-actions">
{#if cloud}
@ -394,7 +385,7 @@
</div>
{/if}
</div>
<Divider size="S" />
<div class="appTable">
{#each filteredApps as app (app.appId)}
<AppRow
@ -475,9 +466,6 @@
.app-actions :global(> button) {
margin-right: 10px;
}
.title .welcome > .buttons {
padding-top: 30px;
}
.title {
display: flex;
flex-direction: row;

View file

@ -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()
})
</script>
<Page wide>
@ -187,7 +163,14 @@
disabled={!isPublished}
on:click={viewApp}>View app</Button
>
<Button size="M" cta icon="Edit" on:click={editApp(selectedApp)}>
<Button
size="M"
cta
icon="Edit"
on:click={() => {
editApp(selectedApp)
}}
>
<span>Edit</span>
</Button>
</ButtonGroup>
@ -213,11 +196,8 @@
<Tab title="Backups">
<div class="container">Backups contents</div>
</Tab>
<Tab title="App Version">
<VersionTab app={selectedApp} />
</Tab>
<Tab title="Self-host">
<SelfHostTab />
<Tab title="Settings">
<SettingsTab app={selectedApp} />
</Tab>
</Tabs>
{:catch error}

View file

@ -1,17 +1,43 @@
<script>
import DashCard from "../../../../../components/common/DashCard.svelte"
import DashCard from "components/common/DashCard.svelte"
import { AppStatus } from "constants"
import { Icon, Heading, Link, Avatar } from "@budibase/bbui"
import { Icon, Heading, Link, Avatar, notifications } from "@budibase/bbui"
import { store } from "builderStore"
import clientPackage from "@budibase/client/package.json"
import { processStringSync } from "@budibase/string-templates"
import { users, auth } from "stores/portal"
export let app
export let deployments
export let navigateTab
const userInit = async () => {
try {
await users.init()
} catch (error) {
notifications.error("Error getting user list")
}
}
let userPromise = userInit()
$: updateAvailable = clientPackage.version !== $store.version
$: isPublished = app.status === AppStatus.DEPLOYED
$: isPublished = app && app?.status === AppStatus.DEPLOYED
$: appEditorId = !app?.updatedBy ? $auth.user._id : app?.updatedBy
$: appEditorText = appEditor?.firstName || appEditor?.email
$: filteredUsers = !appEditorId
? []
: $users.filter(user => user._id === appEditorId)
$: appEditor = filteredUsers.length ? filteredUsers[0] : null
const getInitials = user => {
let initials = ""
initials += user.firstName ? user.firstName[0] : ""
initials += user.lastName ? user.lastName[0] : ""
return initials == "" ? user.email[0] : initials
}
</script>
<div class="overview-tab">
@ -46,14 +72,21 @@
</div>
</DashCard>
<DashCard title={"Last Edited"}>
{app.updatedAt}
<div class="last-edited-content">
<!-- Where is this information sourced? auditLog > Placeholder, metadata -->
<div class="updated-by">
<!-- Add a link to the user? new window? -->
<Avatar size="M" initials={app.updatedBy.initials} />
<div>{app.updatedBy.firstName}</div>
</div>
{#await userPromise}
<Avatar size="M" initials={"-"} />
{:then _}
<div class="updated-by">
{#if appEditor}
<Avatar size="M" initials={getInitials(appEditor)} />
<div>
{appEditor._id === $auth.user._id ? "You" : appEditorText}
</div>
{/if}
</div>
{:catch error}
<p>Could not fetch user: {error.message}</p>
{/await}
<p class="last-edit-text">
{#if app}
{processStringSync(
@ -70,7 +103,7 @@
title={"App Version"}
showIcon={true}
action={() => {
navigateTab("App Version")
navigateTab("Settings")
}}
>
<div class="version-content">
@ -81,7 +114,7 @@
<Link
on:click={() => {
if (typeof navigateTab === "function") {
navigateTab("App Version")
navigateTab("Settings")
}
}}
>
@ -120,7 +153,12 @@
</div>
</div>
</DashCard>
<DashCard title={"Backups"}>
<DashCard
title={"Backups"}
action={() => {
navigateTab("Backups")
}}
>
<div class="backups-content">test</div>
</DashCard>
</div>

View file

@ -1,37 +0,0 @@
<script>
import { Layout, Divider, Heading, Body, Page, Button } from "@budibase/bbui"
const selfHostPath =
"https://docs.budibase.com/docs/hosting-methods#self-host-budibase"
</script>
<div class="self-host-tab">
<Page wide={false}>
<Layout noPadding>
<Layout gap="XS" noPadding>
<Heading>Self-host Budibase</Heading>
<Divider />
<Body>
<p>
Self-host Budibase for free to get unlimited apps and more - and it
only takes a few minutes!
</p>
<div class="page-action">
<Button
cta
on:click={() => {
window.open(selfHostPath, "_blank")
}}>Self-host Budibase</Button
>
</div>
</Body>
</Layout>
</Layout>
</Page>
</div>
<style>
.page-action {
padding-top: var(--spacing-xl);
}
</style>

View file

@ -0,0 +1,131 @@
<script>
import {
Layout,
Divider,
Heading,
Body,
Page,
Button,
Modal,
} from "@budibase/bbui"
import { store } from "builderStore"
import { auth } from "stores/portal"
import clientPackage from "@budibase/client/package.json"
import VersionModal from "components/deploy/VersionModal.svelte"
import UpdateAppModal from "components/start/UpdateAppModal.svelte"
export let app
let versionModal
let updatingModal
let selfHostPath =
"https://docs.budibase.com/docs/hosting-methods#self-host-budibase"
$: updateAvailable = clientPackage.version !== $store.version
$: appUrl = `${window.origin}/app${app?.url}`
$: lockedBy = app?.lockedBy
$: lockedByYou = $auth.user.email === lockedBy?.email
</script>
<div class="version-tab">
<Page wide={false}>
<Layout gap="XL" noPadding>
<Layout gap="XS" noPadding>
<Heading size="S">Name and Url</Heading>
<Divider />
<Body>
<div class="app-details">
<div class="app-name">
<div class="name-title detail-title">Name</div>
<div class="name">{app?.name}</div>
</div>
<div class="app-url">
<div class="url-title detail-title">Url Path</div>
<div class="url">{appUrl}</div>
</div>
</div>
<div class="page-action">
<Button
cta
secondary
on:click={() => {
updatingModal.show()
}}
disabled={lockedBy && !lockedByYou}
>
Edit
</Button>
</div>
</Body>
</Layout>
<Layout gap="XS" noPadding>
<Heading size="S">App Version</Heading>
<Divider />
<Body>
{#if updateAvailable}
<p>
The app is currently using version <strong>{app?.version}</strong>
but version <strong>{clientPackage.version}</strong> is available.
</p>
{:else}
<p>
The app is currently using version <strong>{app?.version}</strong
>. You're running the latest!
</p>
{/if}
<p>
Updates can contain new features, performance improvements and bug
fixes.
</p>
<div class="page-action">
<Button
cta
on:click={versionModal.show()}
disabled={!updateAvailable}>Update App</Button
>
</div>
</Body>
</Layout>
<Layout gap="XS" noPadding>
<Heading size="S">Self-host Budibase</Heading>
<Divider />
<Body>
<p>
Self-host Budibase for free to get unlimited apps and more - and it
only takes a few minutes!
</p>
<div class="page-action">
<Button
cta
on:click={() => {
window.open(selfHostPath, "_blank")
}}>Self-host Budibase</Button
>
</div>
</Body>
</Layout>
</Layout>
<VersionModal bind:this={versionModal} hideIcon={true} />
<Modal bind:this={updatingModal} padding={false} width="600px">
<UpdateAppModal {app} />
</Modal>
</Page>
</div>
<style>
.page-action {
padding-top: var(--spacing-xl);
}
.app-details {
display: flex;
flex-direction: column;
gap: var(--spacing-m);
}
.detail-title {
color: var(--spectrum-global-color-gray-600);
font-size: var(
--spectrum-alias-font-size-default,
var(--spectrum-global-dimension-font-size-100)
);
}
</style>

View file

@ -1,48 +0,0 @@
<script>
import { Layout, Divider, Heading, Body, Page, Button } from "@budibase/bbui"
import { store } from "builderStore"
import clientPackage from "@budibase/client/package.json"
export let app
//Review locking behaviour.
//The app just needs to be unlocked/lockedByYou to proceed?
$: updateAvailable = clientPackage.version !== $store.version
</script>
<div class="version-tab">
<Page wide={false}>
<Layout noPadding>
<Layout gap="XS" noPadding>
<Heading>App Version</Heading>
<Divider />
<Body>
{#if updateAvailable}
<p>
The app is currently using version <strong>{app?.version}</strong>
but version <strong>{clientPackage.version}</strong> is available.
</p>
{:else}
<p>
The app is currently using version <strong>{app?.version}</strong
>. You're running the latest!
</p>
{/if}
<p>
Updates can contain new features, performance improvements and bug
fixes.
</p>
<div class="page-action">
<Button cta on:click={() => {}}>Update App</Button>
</div>
</Body>
</Layout>
</Layout>
</Page>
</div>
<style>
.page-action {
padding-top: var(--spacing-xl);
}
</style>

View file

@ -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)