2022-05-05 23:52:17 +12:00
|
|
|
<script>
|
|
|
|
import { goto } from "@roxi/routify"
|
|
|
|
import {
|
|
|
|
Layout,
|
|
|
|
Page,
|
|
|
|
Button,
|
|
|
|
ButtonGroup,
|
|
|
|
Heading,
|
|
|
|
Tab,
|
|
|
|
Tabs,
|
|
|
|
notifications,
|
|
|
|
ProgressCircle,
|
|
|
|
} from "@budibase/bbui"
|
|
|
|
import OverviewTab from "../_components/OverviewTab.svelte"
|
2022-05-07 02:52:49 +12:00
|
|
|
import SettingsTab from "../_components/SettingsTab.svelte"
|
2022-05-05 23:52:17 +12:00
|
|
|
import { API } from "api"
|
|
|
|
import { store } from "builderStore"
|
|
|
|
import { apps, auth } from "stores/portal"
|
|
|
|
import analytics, { Events, EventSource } from "analytics"
|
|
|
|
import { AppStatus } from "constants"
|
2022-05-10 01:41:53 +12:00
|
|
|
import AppLockModal from "components/common/AppLockModal.svelte"
|
|
|
|
import EditableIcon from "components/common/EditableIcon.svelte"
|
2022-05-05 23:52:17 +12:00
|
|
|
import { checkIncomingDeploymentStatus } from "components/deploy/utils"
|
2022-05-10 01:41:53 +12:00
|
|
|
import { onDestroy, onMount } from "svelte"
|
2022-05-05 23:52:17 +12:00
|
|
|
|
|
|
|
export let application
|
|
|
|
|
|
|
|
let promise = getPackage()
|
|
|
|
|
|
|
|
// App
|
|
|
|
$: filteredApps = $apps.filter(app => app.devId === application)
|
|
|
|
$: selectedApp = filteredApps?.length ? filteredApps[0] : null
|
|
|
|
|
|
|
|
// Locking
|
|
|
|
$: lockedBy = selectedApp?.lockedBy
|
|
|
|
$: lockedByYou = $auth.user.email === lockedBy?.email
|
|
|
|
$: lockIdentifer = `${
|
2022-05-07 02:52:49 +12:00
|
|
|
lockedBy && Object.prototype.hasOwnProperty.call(lockedBy, "firstName")
|
2022-05-05 23:52:17 +12:00
|
|
|
? lockedBy?.firstName
|
|
|
|
: lockedBy?.email
|
|
|
|
}`
|
|
|
|
|
|
|
|
// App deployments
|
|
|
|
$: deployments = []
|
|
|
|
$: latestDeployments = deployments
|
|
|
|
.filter(
|
|
|
|
deployment =>
|
|
|
|
deployment.status === "SUCCESS" && application === deployment.appId
|
|
|
|
)
|
|
|
|
.sort((a, b) => a.updatedAt > b.updatedAt)
|
|
|
|
|
|
|
|
$: isPublished =
|
|
|
|
selectedApp?.status === AppStatus.DEPLOYED && latestDeployments?.length > 0
|
|
|
|
|
|
|
|
$: appUrl = `${window.origin}/app${selectedApp?.url}`
|
2022-05-07 02:52:49 +12:00
|
|
|
$: tabs = ["Overview", "Automation History", "Backups", "Settings"]
|
2022-05-05 23:52:17 +12:00
|
|
|
$: selectedTab = "Overview"
|
|
|
|
|
2022-05-10 01:41:53 +12:00
|
|
|
const backToAppList = () => {
|
|
|
|
$goto(`../../../portal/`)
|
|
|
|
}
|
|
|
|
|
2022-05-05 23:52:17 +12:00
|
|
|
const handleTabChange = tabKey => {
|
|
|
|
if (tabKey === selectedTab) {
|
|
|
|
return
|
|
|
|
} else if (tabKey && tabs.indexOf(tabKey) > -1) {
|
|
|
|
selectedTab = tabKey
|
|
|
|
} else {
|
|
|
|
notifications.error("Invalid tab key")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getPackage() {
|
|
|
|
try {
|
|
|
|
const pkg = await API.fetchAppPackage(application)
|
|
|
|
await store.actions.initialise(pkg)
|
|
|
|
return pkg
|
|
|
|
} catch (error) {
|
|
|
|
notifications.error(`Error initialising app: ${error?.message}`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const reviewPendingDeployments = (deployments, newDeployments) => {
|
|
|
|
if (deployments.length > 0) {
|
|
|
|
const pending = checkIncomingDeploymentStatus(deployments, newDeployments)
|
|
|
|
if (pending.length) {
|
|
|
|
notifications.warning(
|
|
|
|
"Deployment has been queued and will be processed shortly"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function fetchDeployments() {
|
|
|
|
try {
|
|
|
|
const newDeployments = await API.getAppDeployments()
|
|
|
|
reviewPendingDeployments(deployments, newDeployments)
|
|
|
|
return newDeployments
|
|
|
|
} catch (err) {
|
|
|
|
notifications.error("Error fetching deployment history")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const viewApp = () => {
|
|
|
|
if (isPublished) {
|
|
|
|
analytics.captureEvent(Events.APP.VIEW_PUBLISHED, {
|
|
|
|
appId: $store.appId,
|
|
|
|
eventSource: EventSource.PORTAL,
|
|
|
|
})
|
|
|
|
window.open(appUrl, "_blank")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const editApp = app => {
|
|
|
|
if (lockedBy && !lockedByYou) {
|
2022-05-10 01:41:53 +12:00
|
|
|
notifications.warning(
|
2022-05-05 23:52:17 +12:00
|
|
|
`App locked by ${lockIdentifer}. Please allow lock to expire or have them unlock this app.`
|
|
|
|
)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
$goto(`../../../app/${app.devId}`)
|
|
|
|
}
|
2022-05-10 01:41:53 +12:00
|
|
|
|
|
|
|
onDestroy(() => {
|
|
|
|
store.actions.reset()
|
|
|
|
})
|
|
|
|
|
|
|
|
onMount(async () => {
|
|
|
|
try {
|
|
|
|
if (!apps.length) {
|
|
|
|
await apps.load()
|
|
|
|
}
|
|
|
|
deployments = await fetchDeployments()
|
|
|
|
} catch (error) {
|
|
|
|
notifications.error("Error initialising app overview")
|
|
|
|
}
|
|
|
|
})
|
2022-05-05 23:52:17 +12:00
|
|
|
</script>
|
|
|
|
|
|
|
|
<Page wide>
|
|
|
|
<Layout noPadding gap="XL">
|
|
|
|
<span>
|
2022-05-10 01:41:53 +12:00
|
|
|
<Button quiet secondary icon={"ChevronLeft"} on:click={backToAppList}>
|
2022-05-05 23:52:17 +12:00
|
|
|
Back
|
|
|
|
</Button>
|
|
|
|
</span>
|
|
|
|
{#await promise}
|
|
|
|
<div class="loading">
|
|
|
|
<ProgressCircle size="XL" />
|
|
|
|
</div>
|
|
|
|
{:then _}
|
|
|
|
<div class="overview-header">
|
|
|
|
<div class="app-title">
|
|
|
|
<div class="app-logo">
|
|
|
|
<div
|
|
|
|
class="app-icon"
|
|
|
|
style="color: {selectedApp?.icon?.color || ''}"
|
|
|
|
>
|
2022-05-10 01:41:53 +12:00
|
|
|
<EditableIcon
|
|
|
|
app={selectedApp}
|
|
|
|
size="XL"
|
|
|
|
name={selectedApp?.icon?.name || "Apps"}
|
|
|
|
/>
|
2022-05-05 23:52:17 +12:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="app-details">
|
|
|
|
<Heading size="M">{selectedApp?.name}</Heading>
|
|
|
|
<div class="app-url">{appUrl}</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="header-right">
|
|
|
|
<AppLockModal app={selectedApp} />
|
|
|
|
<ButtonGroup gap="XS">
|
|
|
|
<Button
|
|
|
|
size="M"
|
|
|
|
secondary
|
|
|
|
icon="Globe"
|
|
|
|
disabled={!isPublished}
|
2022-05-11 03:58:55 +12:00
|
|
|
on:click={viewApp}
|
|
|
|
dataCy="view-app"
|
2022-05-05 23:52:17 +12:00
|
|
|
>
|
2022-05-11 03:58:55 +12:00
|
|
|
View app
|
|
|
|
</Button>
|
2022-05-07 02:52:49 +12:00
|
|
|
<Button
|
|
|
|
size="M"
|
|
|
|
cta
|
|
|
|
icon="Edit"
|
2022-05-10 01:41:53 +12:00
|
|
|
disabled={lockedBy && !lockedByYou}
|
2022-05-07 02:52:49 +12:00
|
|
|
on:click={() => {
|
|
|
|
editApp(selectedApp)
|
|
|
|
}}
|
|
|
|
>
|
2022-05-05 23:52:17 +12:00
|
|
|
<span>Edit</span>
|
|
|
|
</Button>
|
|
|
|
</ButtonGroup>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<Tabs
|
|
|
|
selected={selectedTab}
|
|
|
|
noPadding
|
|
|
|
on:select={e => {
|
|
|
|
selectedTab = e.detail
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Tab title="Overview">
|
|
|
|
<OverviewTab
|
|
|
|
app={selectedApp}
|
|
|
|
deployments={latestDeployments}
|
|
|
|
navigateTab={handleTabChange}
|
|
|
|
/>
|
|
|
|
</Tab>
|
2022-05-12 20:24:19 +12:00
|
|
|
{#if false}
|
|
|
|
<Tab title="Automation History">
|
|
|
|
<div class="container">Automation History contents</div>
|
|
|
|
</Tab>
|
|
|
|
<Tab title="Backups">
|
|
|
|
<div class="container">Backups contents</div>
|
|
|
|
</Tab>
|
|
|
|
{/if}
|
2022-05-07 02:52:49 +12:00
|
|
|
<Tab title="Settings">
|
|
|
|
<SettingsTab app={selectedApp} />
|
2022-05-05 23:52:17 +12:00
|
|
|
</Tab>
|
|
|
|
</Tabs>
|
|
|
|
{:catch error}
|
|
|
|
<p>Something went wrong: {error.message}</p>
|
|
|
|
{/await}
|
|
|
|
</Layout>
|
|
|
|
</Page>
|
|
|
|
|
|
|
|
<style>
|
|
|
|
.app-url {
|
|
|
|
color: var(--spectrum-global-color-gray-600);
|
|
|
|
}
|
|
|
|
.loading {
|
|
|
|
display: flex;
|
|
|
|
justify-content: center;
|
|
|
|
}
|
|
|
|
.overview-header {
|
|
|
|
display: flex;
|
|
|
|
justify-content: space-between;
|
|
|
|
}
|
|
|
|
.app-title {
|
|
|
|
display: flex;
|
|
|
|
gap: var(--spacing-m);
|
|
|
|
}
|
|
|
|
.header-right {
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
gap: var(--spacing-xl);
|
|
|
|
}
|
|
|
|
.app-details :global(.spectrum-Heading) {
|
|
|
|
line-height: 1em;
|
|
|
|
margin-bottom: var(--spacing-s);
|
|
|
|
}
|
|
|
|
</style>
|