Overview Tab refactoring and general updates to the homepage
This commit is contained in:
parent
1c5990b9d7
commit
fffb4b0174
|
@ -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()
|
||||
}}
|
||||
>
|
||||
{#if processing}
|
||||
<ProgressCircle overBackground={true} size="S" />
|
||||
{:else}
|
||||
<span class="unlock">Release Lock</span>
|
||||
{/if}
|
||||
</Button>
|
||||
{/if}
|
||||
</ButtonGroup>
|
||||
|
|
|
@ -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>
|
||||
|
||||
{#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"
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
{/if}
|
||||
</div>
|
||||
<div class="desktop">
|
||||
<AppLockModal {app} />
|
||||
<AppLockModal {app} buttonSize="S" />
|
||||
</div>
|
||||
<div class="desktop">
|
||||
<div class="app-status">
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
import {
|
||||
Heading,
|
||||
Layout,
|
||||
Detail,
|
||||
Button,
|
||||
Input,
|
||||
Select,
|
||||
|
@ -312,7 +311,16 @@
|
|||
{welcomeBody}
|
||||
</Body>
|
||||
</Layout>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if !$apps?.length && $templates?.length}
|
||||
<TemplateDisplay templates={$templates} />
|
||||
{/if}
|
||||
|
||||
{#if enrichedApps.length}
|
||||
<Layout noPadding gap="S">
|
||||
<div class="title">
|
||||
<div class="buttons">
|
||||
<Button
|
||||
dataCy="create-app-btn"
|
||||
|
@ -347,23 +355,6 @@
|
|||
</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}
|
||||
<TemplateDisplay templates={$templates} />
|
||||
{/if}
|
||||
|
||||
{#if enrichedApps.length}
|
||||
<Layout noPadding gap="S">
|
||||
<div class="title">
|
||||
<Detail size="L">Apps</Detail>
|
||||
{#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;
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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 -->
|
||||
{#await userPromise}
|
||||
<Avatar size="M" initials={"-"} />
|
||||
{:then _}
|
||||
<div class="updated-by">
|
||||
<!-- Add a link to the user? new window? -->
|
||||
<Avatar size="M" initials={app.updatedBy.initials} />
|
||||
<div>{app.updatedBy.firstName}</div>
|
||||
{#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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue