Overview Tab refactoring and general updates to the homepage
This commit is contained in:
parent
1c5990b9d7
commit
fffb4b0174
|
@ -5,20 +5,23 @@
|
||||||
ModalContent,
|
ModalContent,
|
||||||
Modal,
|
Modal,
|
||||||
notifications,
|
notifications,
|
||||||
|
ProgressCircle,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { auth, apps } from "stores/portal"
|
import { auth, apps } from "stores/portal"
|
||||||
import { processStringSync } from "@budibase/string-templates"
|
import { processStringSync } from "@budibase/string-templates"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
|
|
||||||
export let app
|
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 appLockModal
|
||||||
|
let processing = false
|
||||||
|
|
||||||
$: lockedBy = app?.lockedBy
|
$: lockedBy = app?.lockedBy
|
||||||
$: lockedByYou = $auth.user.email === lockedBy?.email
|
$: lockedByYou = $auth.user.email === lockedBy?.email
|
||||||
$: lockIdentifer = `${
|
$: lockIdentifer = `${
|
||||||
Object.prototype.hasOwnProperty.call(lockedBy, "firstName")
|
lockedBy && Object.prototype.hasOwnProperty.call(lockedBy, "firstName")
|
||||||
? lockedBy?.firstName
|
? lockedBy?.firstName
|
||||||
: lockedBy?.email
|
: lockedBy?.email
|
||||||
}`
|
}`
|
||||||
|
@ -28,7 +31,7 @@
|
||||||
$: lockExpiry = getExpiryDuration(app)
|
$: lockExpiry = getExpiryDuration(app)
|
||||||
|
|
||||||
const getExpiryDuration = app => {
|
const getExpiryDuration = app => {
|
||||||
if (!app.lockedBy) {
|
if (!app?.lockedBy?.lockedAt) {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
let expiry =
|
let expiry =
|
||||||
|
@ -37,6 +40,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const releaseLock = async () => {
|
const releaseLock = async () => {
|
||||||
|
processing = true
|
||||||
if (app) {
|
if (app) {
|
||||||
try {
|
try {
|
||||||
await API.releaseAppLock(app.devId)
|
await API.releaseAppLock(app.devId)
|
||||||
|
@ -48,6 +52,7 @@
|
||||||
} else {
|
} else {
|
||||||
notifications.error("No application is selected")
|
notifications.error("No application is selected")
|
||||||
}
|
}
|
||||||
|
processing = false
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -57,6 +62,7 @@
|
||||||
quiet
|
quiet
|
||||||
secondary
|
secondary
|
||||||
icon="LockClosed"
|
icon="LockClosed"
|
||||||
|
size={buttonSize}
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
appLockModal.show()
|
appLockModal.show()
|
||||||
}}
|
}}
|
||||||
|
@ -93,6 +99,7 @@
|
||||||
<Button
|
<Button
|
||||||
secondary
|
secondary
|
||||||
quiet={lockedBy && lockedByYou}
|
quiet={lockedBy && lockedByYou}
|
||||||
|
disabled={processing}
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
appLockModal.hide()
|
appLockModal.hide()
|
||||||
}}
|
}}
|
||||||
|
@ -104,12 +111,17 @@
|
||||||
{#if lockedByYou && lockExpiry > 0}
|
{#if lockedByYou && lockExpiry > 0}
|
||||||
<Button
|
<Button
|
||||||
secondary
|
secondary
|
||||||
|
disabled={processing}
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
releaseLock()
|
releaseLock()
|
||||||
appLockModal.hide()
|
appLockModal.hide()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
{#if processing}
|
||||||
|
<ProgressCircle overBackground={true} size="S" />
|
||||||
|
{:else}
|
||||||
<span class="unlock">Release Lock</span>
|
<span class="unlock">Release Lock</span>
|
||||||
|
{/if}
|
||||||
</Button>
|
</Button>
|
||||||
{/if}
|
{/if}
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
|
|
|
@ -11,6 +11,16 @@
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
import clientPackage from "@budibase/client/package.json"
|
import clientPackage from "@budibase/client/package.json"
|
||||||
|
|
||||||
|
export function show() {
|
||||||
|
updateModal.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hide() {
|
||||||
|
updateModal.hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
export let hideIcon = false
|
||||||
|
|
||||||
let updateModal
|
let updateModal
|
||||||
|
|
||||||
$: appId = $store.appId
|
$: appId = $store.appId
|
||||||
|
@ -57,9 +67,11 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
{#if !hideIcon}
|
||||||
<div class="icon-wrapper" class:highlight={updateAvailable}>
|
<div class="icon-wrapper" class:highlight={updateAvailable}>
|
||||||
<Icon name="Refresh" hoverable on:click={updateModal.show} />
|
<Icon name="Refresh" hoverable on:click={updateModal.show} />
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
<Modal bind:this={updateModal}>
|
<Modal bind:this={updateModal}>
|
||||||
<ModalContent
|
<ModalContent
|
||||||
title="App version"
|
title="App version"
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="desktop">
|
<div class="desktop">
|
||||||
<AppLockModal {app} />
|
<AppLockModal {app} buttonSize="S" />
|
||||||
</div>
|
</div>
|
||||||
<div class="desktop">
|
<div class="desktop">
|
||||||
<div class="app-status">
|
<div class="app-status">
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
import {
|
import {
|
||||||
Heading,
|
Heading,
|
||||||
Layout,
|
Layout,
|
||||||
Detail,
|
|
||||||
Button,
|
Button,
|
||||||
Input,
|
Input,
|
||||||
Select,
|
Select,
|
||||||
|
@ -312,7 +311,16 @@
|
||||||
{welcomeBody}
|
{welcomeBody}
|
||||||
</Body>
|
</Body>
|
||||||
</Layout>
|
</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">
|
<div class="buttons">
|
||||||
<Button
|
<Button
|
||||||
dataCy="create-app-btn"
|
dataCy="create-app-btn"
|
||||||
|
@ -347,23 +355,6 @@
|
||||||
</Button>
|
</Button>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</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}
|
{#if enrichedApps.length > 1}
|
||||||
<div class="app-actions">
|
<div class="app-actions">
|
||||||
{#if cloud}
|
{#if cloud}
|
||||||
|
@ -394,7 +385,7 @@
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
<Divider size="S" />
|
||||||
<div class="appTable">
|
<div class="appTable">
|
||||||
{#each filteredApps as app (app.appId)}
|
{#each filteredApps as app (app.appId)}
|
||||||
<AppRow
|
<AppRow
|
||||||
|
@ -475,9 +466,6 @@
|
||||||
.app-actions :global(> button) {
|
.app-actions :global(> button) {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
.title .welcome > .buttons {
|
|
||||||
padding-top: 30px;
|
|
||||||
}
|
|
||||||
.title {
|
.title {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
|
@ -13,12 +13,9 @@
|
||||||
ProgressCircle,
|
ProgressCircle,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import OverviewTab from "../_components/OverviewTab.svelte"
|
import OverviewTab from "../_components/OverviewTab.svelte"
|
||||||
import VersionTab from "../_components/VersionTab.svelte"
|
import SettingsTab from "../_components/SettingsTab.svelte"
|
||||||
import SelfHostTab from "../_components/SelfHostTab.svelte"
|
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
import { onMount } from "svelte"
|
|
||||||
import { store } from "builderStore"
|
import { store } from "builderStore"
|
||||||
import { roles, flags } from "stores/backend"
|
|
||||||
import { apps, auth } from "stores/portal"
|
import { apps, auth } from "stores/portal"
|
||||||
import analytics, { Events, EventSource } from "analytics"
|
import analytics, { Events, EventSource } from "analytics"
|
||||||
import { AppStatus } from "constants"
|
import { AppStatus } from "constants"
|
||||||
|
@ -37,7 +34,7 @@
|
||||||
$: lockedBy = selectedApp?.lockedBy
|
$: lockedBy = selectedApp?.lockedBy
|
||||||
$: lockedByYou = $auth.user.email === lockedBy?.email
|
$: lockedByYou = $auth.user.email === lockedBy?.email
|
||||||
$: lockIdentifer = `${
|
$: lockIdentifer = `${
|
||||||
Object.prototype.hasOwnProperty.call(lockedBy, "firstName")
|
lockedBy && Object.prototype.hasOwnProperty.call(lockedBy, "firstName")
|
||||||
? lockedBy?.firstName
|
? lockedBy?.firstName
|
||||||
: lockedBy?.email
|
: lockedBy?.email
|
||||||
}`
|
}`
|
||||||
|
@ -55,13 +52,7 @@
|
||||||
selectedApp?.status === AppStatus.DEPLOYED && latestDeployments?.length > 0
|
selectedApp?.status === AppStatus.DEPLOYED && latestDeployments?.length > 0
|
||||||
|
|
||||||
$: appUrl = `${window.origin}/app${selectedApp?.url}`
|
$: appUrl = `${window.origin}/app${selectedApp?.url}`
|
||||||
$: tabs = [
|
$: tabs = ["Overview", "Automation History", "Backups", "Settings"]
|
||||||
"Overview",
|
|
||||||
"Automation History",
|
|
||||||
"Backups",
|
|
||||||
"App Version",
|
|
||||||
"Self-host",
|
|
||||||
]
|
|
||||||
$: selectedTab = "Overview"
|
$: selectedTab = "Overview"
|
||||||
|
|
||||||
const handleTabChange = tabKey => {
|
const handleTabChange = tabKey => {
|
||||||
|
@ -78,9 +69,8 @@
|
||||||
try {
|
try {
|
||||||
const pkg = await API.fetchAppPackage(application)
|
const pkg = await API.fetchAppPackage(application)
|
||||||
await store.actions.initialise(pkg)
|
await store.actions.initialise(pkg)
|
||||||
// await automationStore.actions.fetch()
|
await apps.load()
|
||||||
await roles.fetch()
|
deployments = await fetchDeployments()
|
||||||
await flags.fetch()
|
|
||||||
return pkg
|
return pkg
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error(`Error initialising app: ${error?.message}`)
|
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 = () => {
|
const viewApp = () => {
|
||||||
if (isPublished) {
|
if (isPublished) {
|
||||||
analytics.captureEvent(Events.APP.VIEW_PUBLISHED, {
|
analytics.captureEvent(Events.APP.VIEW_PUBLISHED, {
|
||||||
|
@ -129,18 +117,6 @@
|
||||||
}
|
}
|
||||||
$goto(`../../../app/${app.devId}`)
|
$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>
|
</script>
|
||||||
|
|
||||||
<Page wide>
|
<Page wide>
|
||||||
|
@ -187,7 +163,14 @@
|
||||||
disabled={!isPublished}
|
disabled={!isPublished}
|
||||||
on:click={viewApp}>View app</Button
|
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>
|
<span>Edit</span>
|
||||||
</Button>
|
</Button>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
|
@ -213,11 +196,8 @@
|
||||||
<Tab title="Backups">
|
<Tab title="Backups">
|
||||||
<div class="container">Backups contents</div>
|
<div class="container">Backups contents</div>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab title="App Version">
|
<Tab title="Settings">
|
||||||
<VersionTab app={selectedApp} />
|
<SettingsTab app={selectedApp} />
|
||||||
</Tab>
|
|
||||||
<Tab title="Self-host">
|
|
||||||
<SelfHostTab />
|
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
{:catch error}
|
{:catch error}
|
||||||
|
|
|
@ -1,17 +1,43 @@
|
||||||
<script>
|
<script>
|
||||||
import DashCard from "../../../../../components/common/DashCard.svelte"
|
import DashCard from "components/common/DashCard.svelte"
|
||||||
import { AppStatus } from "constants"
|
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 { store } from "builderStore"
|
||||||
import clientPackage from "@budibase/client/package.json"
|
import clientPackage from "@budibase/client/package.json"
|
||||||
import { processStringSync } from "@budibase/string-templates"
|
import { processStringSync } from "@budibase/string-templates"
|
||||||
|
import { users, auth } from "stores/portal"
|
||||||
|
|
||||||
export let app
|
export let app
|
||||||
export let deployments
|
export let deployments
|
||||||
export let navigateTab
|
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
|
$: 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>
|
</script>
|
||||||
|
|
||||||
<div class="overview-tab">
|
<div class="overview-tab">
|
||||||
|
@ -46,14 +72,21 @@
|
||||||
</div>
|
</div>
|
||||||
</DashCard>
|
</DashCard>
|
||||||
<DashCard title={"Last Edited"}>
|
<DashCard title={"Last Edited"}>
|
||||||
{app.updatedAt}
|
|
||||||
<div class="last-edited-content">
|
<div class="last-edited-content">
|
||||||
<!-- Where is this information sourced? auditLog > Placeholder, metadata -->
|
{#await userPromise}
|
||||||
|
<Avatar size="M" initials={"-"} />
|
||||||
|
{:then _}
|
||||||
<div class="updated-by">
|
<div class="updated-by">
|
||||||
<!-- Add a link to the user? new window? -->
|
{#if appEditor}
|
||||||
<Avatar size="M" initials={app.updatedBy.initials} />
|
<Avatar size="M" initials={getInitials(appEditor)} />
|
||||||
<div>{app.updatedBy.firstName}</div>
|
<div>
|
||||||
|
{appEditor._id === $auth.user._id ? "You" : appEditorText}
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{:catch error}
|
||||||
|
<p>Could not fetch user: {error.message}</p>
|
||||||
|
{/await}
|
||||||
<p class="last-edit-text">
|
<p class="last-edit-text">
|
||||||
{#if app}
|
{#if app}
|
||||||
{processStringSync(
|
{processStringSync(
|
||||||
|
@ -70,7 +103,7 @@
|
||||||
title={"App Version"}
|
title={"App Version"}
|
||||||
showIcon={true}
|
showIcon={true}
|
||||||
action={() => {
|
action={() => {
|
||||||
navigateTab("App Version")
|
navigateTab("Settings")
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="version-content">
|
<div class="version-content">
|
||||||
|
@ -81,7 +114,7 @@
|
||||||
<Link
|
<Link
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
if (typeof navigateTab === "function") {
|
if (typeof navigateTab === "function") {
|
||||||
navigateTab("App Version")
|
navigateTab("Settings")
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -120,7 +153,12 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</DashCard>
|
</DashCard>
|
||||||
<DashCard title={"Backups"}>
|
<DashCard
|
||||||
|
title={"Backups"}
|
||||||
|
action={() => {
|
||||||
|
navigateTab("Backups")
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div class="backups-content">test</div>
|
<div class="backups-content">test</div>
|
||||||
</DashCard>
|
</DashCard>
|
||||||
</div>
|
</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)
|
const metadata = await db.get(DocumentTypes.APP_METADATA)
|
||||||
metadata.updatedAt = new Date().toISOString()
|
metadata.updatedAt = new Date().toISOString()
|
||||||
|
|
||||||
const getInitials = user => {
|
metadata.updatedBy = getGlobalIDFromUserMetadataID(ctx.user.userId)
|
||||||
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)
|
const response = await db.put(metadata)
|
||||||
metadata._rev = response.rev
|
metadata._rev = response.rev
|
||||||
await appCache.invalidateAppMetadata(appId, metadata)
|
await appCache.invalidateAppMetadata(appId, metadata)
|
||||||
|
|
Loading…
Reference in New Issue