Update apps list page and app rows to match new styles and behaviours
This commit is contained in:
parent
eb2fd47e44
commit
9d38fa938f
|
@ -87,7 +87,7 @@
|
||||||
{#if lockedByYou && getExpiryDuration(app) > 0}
|
{#if lockedByYou && getExpiryDuration(app) > 0}
|
||||||
<span class="lock-expiry-body">
|
<span class="lock-expiry-body">
|
||||||
{processStringSync(
|
{processStringSync(
|
||||||
"This lock will expire in {{ duration time 'millisecond' }} from now",
|
"This lock will expire in {{ duration time 'millisecond' }} from now.",
|
||||||
{
|
{
|
||||||
time: getExpiryDuration(app),
|
time: getExpiryDuration(app),
|
||||||
}
|
}
|
||||||
|
@ -141,4 +141,8 @@
|
||||||
gap: var(--spacing-s);
|
gap: var(--spacing-s);
|
||||||
max-width: 175px;
|
max-width: 175px;
|
||||||
}
|
}
|
||||||
|
.lock-status-text {
|
||||||
|
font-weight: 400;
|
||||||
|
color: var(--spectrum-global-color-gray-800);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,18 +1,11 @@
|
||||||
<script>
|
<script>
|
||||||
import { Heading, Button, Icon, ActionMenu, MenuItem } from "@budibase/bbui"
|
import { Heading, Button, Icon } from "@budibase/bbui"
|
||||||
import AppLockModal from "../common/AppLockModal.svelte"
|
import AppLockModal from "../common/AppLockModal.svelte"
|
||||||
import { processStringSync } from "@budibase/string-templates"
|
import { processStringSync } from "@budibase/string-templates"
|
||||||
|
|
||||||
export let app
|
export let app
|
||||||
export let exportApp
|
|
||||||
export let editApp
|
export let editApp
|
||||||
export let updateApp
|
|
||||||
export let deleteApp
|
|
||||||
export let unpublishApp
|
|
||||||
export let appOverview
|
export let appOverview
|
||||||
export let releaseLock
|
|
||||||
export let editIcon
|
|
||||||
export let copyAppId
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="title" data-cy={`${app.devId}`}>
|
<div class="title" data-cy={`${app.devId}`}>
|
||||||
|
@ -20,7 +13,7 @@
|
||||||
<div class="app-icon" style="color: {app.icon?.color || ''}">
|
<div class="app-icon" style="color: {app.icon?.color || ''}">
|
||||||
<Icon size="XL" name={app.icon?.name || "Apps"} />
|
<Icon size="XL" name={app.icon?.name || "Apps"} />
|
||||||
</div>
|
</div>
|
||||||
<div class="name" data-cy="app-name-link" on:click={() => appOverview(app)}>
|
<div class="name" data-cy="app-name-link" on:click={() => editApp(app)}>
|
||||||
<Heading size="XS">
|
<Heading size="XS">
|
||||||
{app.name}
|
{app.name}
|
||||||
</Heading>
|
</Heading>
|
||||||
|
@ -37,7 +30,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="desktop">
|
<div class="desktop">
|
||||||
<AppLockModal {app} buttonSize="S" />
|
<AppLockModal {app} buttonSize="M" />
|
||||||
</div>
|
</div>
|
||||||
<div class="desktop">
|
<div class="desktop">
|
||||||
<div class="app-status">
|
<div class="app-status">
|
||||||
|
@ -52,47 +45,27 @@
|
||||||
</div>
|
</div>
|
||||||
<div data-cy={`row_actions_${app.appId}`}>
|
<div data-cy={`row_actions_${app.appId}`}>
|
||||||
<div class="app-row-actions">
|
<div class="app-row-actions">
|
||||||
|
<Button size="S" secondary newStyles on:click={() => appOverview(app)}>
|
||||||
|
Manage
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
size="S"
|
size="S"
|
||||||
secondary
|
primary
|
||||||
quiet
|
newStyles
|
||||||
disabled={app.lockedOther}
|
disabled={app.lockedOther}
|
||||||
on:click={() => editApp(app)}
|
on:click={() => editApp(app)}
|
||||||
>Edit
|
>
|
||||||
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
<Button size="S" cta on:click={() => appOverview(app)}>View</Button>
|
|
||||||
</div>
|
</div>
|
||||||
<ActionMenu align="right" dataCy="app-row-actions-menu-popover">
|
|
||||||
<span slot="control" class="app-row-actions-icon">
|
|
||||||
<Icon hoverable name="More" />
|
|
||||||
</span>
|
|
||||||
{#if app.lockedYou}
|
|
||||||
<MenuItem on:click={() => releaseLock(app)} icon="LockOpen">
|
|
||||||
Release lock
|
|
||||||
</MenuItem>
|
|
||||||
{/if}
|
|
||||||
<MenuItem on:click={() => exportApp(app)} icon="Download">Export</MenuItem>
|
|
||||||
{#if app.deployed}
|
|
||||||
<MenuItem on:click={() => unpublishApp(app)} icon="GlobeRemove">
|
|
||||||
Unpublish
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem on:click={() => copyAppId(app)} icon="Copy">
|
|
||||||
Copy App ID
|
|
||||||
</MenuItem>
|
|
||||||
{/if}
|
|
||||||
{#if !app.deployed}
|
|
||||||
<MenuItem on:click={() => updateApp(app)} icon="Edit">Edit</MenuItem>
|
|
||||||
<MenuItem on:click={() => deleteApp(app)} icon="Delete">Delete</MenuItem>
|
|
||||||
{/if}
|
|
||||||
<MenuItem on:click={() => editIcon(app)} icon="Brush">Edit icon</MenuItem>
|
|
||||||
</ActionMenu>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.app-row-actions {
|
.app-row-actions {
|
||||||
grid-gap: var(--spacing-s);
|
grid-gap: var(--spacing-s);
|
||||||
display: grid;
|
display: flex;
|
||||||
grid-template-columns: 75px 75px;
|
flex-direction: row;
|
||||||
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
.app-status {
|
.app-status {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|
|
@ -3,20 +3,17 @@
|
||||||
Heading,
|
Heading,
|
||||||
Layout,
|
Layout,
|
||||||
Button,
|
Button,
|
||||||
Input,
|
|
||||||
Select,
|
Select,
|
||||||
Modal,
|
Modal,
|
||||||
Page,
|
Page,
|
||||||
notifications,
|
notifications,
|
||||||
Body,
|
Body,
|
||||||
Search,
|
Search,
|
||||||
Helpers,
|
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import TemplateDisplay from "components/common/TemplateDisplay.svelte"
|
import TemplateDisplay from "components/common/TemplateDisplay.svelte"
|
||||||
import Spinner from "components/common/Spinner.svelte"
|
import Spinner from "components/common/Spinner.svelte"
|
||||||
import CreateAppModal from "components/start/CreateAppModal.svelte"
|
import CreateAppModal from "components/start/CreateAppModal.svelte"
|
||||||
import UpdateAppModal from "components/start/UpdateAppModal.svelte"
|
import UpdateAppModal from "components/start/UpdateAppModal.svelte"
|
||||||
import ChooseIconModal from "components/start/ChooseIconModal.svelte"
|
|
||||||
import ExportAppModal from "components/start/ExportAppModal.svelte"
|
import ExportAppModal from "components/start/ExportAppModal.svelte"
|
||||||
|
|
||||||
import { store, automationStore } from "builderStore"
|
import { store, automationStore } from "builderStore"
|
||||||
|
@ -25,10 +22,9 @@
|
||||||
import { apps, auth, admin, templates } from "stores/portal"
|
import { apps, auth, admin, templates } from "stores/portal"
|
||||||
import download from "downloadjs"
|
import download from "downloadjs"
|
||||||
import { goto } from "@roxi/routify"
|
import { goto } from "@roxi/routify"
|
||||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
|
||||||
import AppRow from "components/start/AppRow.svelte"
|
import AppRow from "components/start/AppRow.svelte"
|
||||||
import { AppStatus } from "constants"
|
import { AppStatus } from "constants"
|
||||||
import analytics, { Events, EventSource } from "analytics"
|
import analytics, { Events } from "analytics"
|
||||||
import Logo from "assets/bb-space-man.svg"
|
import Logo from "assets/bb-space-man.svg"
|
||||||
|
|
||||||
let sortBy = "name"
|
let sortBy = "name"
|
||||||
|
@ -36,15 +32,11 @@
|
||||||
let selectedApp
|
let selectedApp
|
||||||
let creationModal
|
let creationModal
|
||||||
let updatingModal
|
let updatingModal
|
||||||
let deletionModal
|
|
||||||
let unpublishModal
|
|
||||||
let exportModal
|
let exportModal
|
||||||
let iconModal
|
|
||||||
let creatingApp = false
|
let creatingApp = false
|
||||||
let loaded = $apps?.length || $templates?.length
|
let loaded = $apps?.length || $templates?.length
|
||||||
let searchTerm = ""
|
let searchTerm = ""
|
||||||
let cloud = $admin.cloud
|
let cloud = $admin.cloud
|
||||||
let appName = ""
|
|
||||||
let creatingFromTemplate = false
|
let creatingFromTemplate = false
|
||||||
|
|
||||||
const resolveWelcomeMessage = (auth, apps) => {
|
const resolveWelcomeMessage = (auth, apps) => {
|
||||||
|
@ -170,18 +162,6 @@
|
||||||
creatingApp = false
|
creatingApp = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const viewApp = app => {
|
|
||||||
analytics.captureEvent(Events.APP.VIEW_PUBLISHED, {
|
|
||||||
appId: app.appId,
|
|
||||||
eventSource: EventSource.PORTAL,
|
|
||||||
})
|
|
||||||
if (app.url) {
|
|
||||||
window.open(`/app${app.url}`)
|
|
||||||
} else {
|
|
||||||
window.open(`/${app.prodId}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const appOverview = app => {
|
const appOverview = app => {
|
||||||
$goto(`../overview/${app.devId}`)
|
$goto(`../overview/${app.devId}`)
|
||||||
}
|
}
|
||||||
|
@ -196,79 +176,6 @@
|
||||||
$goto(`../../app/${app.devId}`)
|
$goto(`../../app/${app.devId}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const editIcon = app => {
|
|
||||||
selectedApp = app
|
|
||||||
iconModal.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
const exportApp = app => {
|
|
||||||
exportModal.show()
|
|
||||||
selectedApp = app
|
|
||||||
}
|
|
||||||
|
|
||||||
const unpublishApp = app => {
|
|
||||||
selectedApp = app
|
|
||||||
unpublishModal.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
const confirmUnpublishApp = async () => {
|
|
||||||
if (!selectedApp) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
analytics.captureEvent(Events.APP.UNPUBLISHED, {
|
|
||||||
appId: selectedApp.appId,
|
|
||||||
})
|
|
||||||
await API.unpublishApp(selectedApp.prodId)
|
|
||||||
await apps.load()
|
|
||||||
notifications.success("App unpublished successfully")
|
|
||||||
} catch (err) {
|
|
||||||
notifications.error("Error unpublishing app")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteApp = app => {
|
|
||||||
selectedApp = app
|
|
||||||
deletionModal.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
const confirmDeleteApp = async () => {
|
|
||||||
if (!selectedApp) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
await API.deleteApp(selectedApp?.devId)
|
|
||||||
await apps.load()
|
|
||||||
// Get checklist, just in case that was the last app
|
|
||||||
await admin.init()
|
|
||||||
notifications.success("App deleted successfully")
|
|
||||||
} catch (err) {
|
|
||||||
notifications.error("Error deleting app")
|
|
||||||
}
|
|
||||||
selectedApp = null
|
|
||||||
appName = null
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateApp = async app => {
|
|
||||||
selectedApp = app
|
|
||||||
updatingModal.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
const releaseLock = async app => {
|
|
||||||
try {
|
|
||||||
await API.releaseAppLock(app.devId)
|
|
||||||
await apps.load()
|
|
||||||
notifications.success("Lock released successfully")
|
|
||||||
} catch (err) {
|
|
||||||
notifications.error("Error releasing lock")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const copyAppId = async app => {
|
|
||||||
await Helpers.copyToClipboard(app.prodId)
|
|
||||||
notifications.success("App ID copied to clipboard.")
|
|
||||||
}
|
|
||||||
|
|
||||||
function createAppFromTemplateUrl(templateKey) {
|
function createAppFromTemplateUrl(templateKey) {
|
||||||
// validate the template key just to make sure
|
// validate the template key just to make sure
|
||||||
const templateParts = templateKey.split("/")
|
const templateParts = templateKey.split("/")
|
||||||
|
@ -416,19 +323,7 @@
|
||||||
|
|
||||||
<div class="appTable" class:unlocked>
|
<div class="appTable" class:unlocked>
|
||||||
{#each filteredApps as app (app.appId)}
|
{#each filteredApps as app (app.appId)}
|
||||||
<AppRow
|
<AppRow {app} {editApp} {appOverview} />
|
||||||
{copyAppId}
|
|
||||||
{releaseLock}
|
|
||||||
{editIcon}
|
|
||||||
{app}
|
|
||||||
{unpublishApp}
|
|
||||||
{viewApp}
|
|
||||||
{editApp}
|
|
||||||
{exportApp}
|
|
||||||
{deleteApp}
|
|
||||||
{updateApp}
|
|
||||||
{appOverview}
|
|
||||||
/>
|
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
@ -462,35 +357,6 @@
|
||||||
<ExportAppModal app={selectedApp} />
|
<ExportAppModal app={selectedApp} />
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<ConfirmDialog
|
|
||||||
bind:this={deletionModal}
|
|
||||||
title="Confirm deletion"
|
|
||||||
okText="Delete app"
|
|
||||||
onOk={confirmDeleteApp}
|
|
||||||
onCancel={() => (appName = null)}
|
|
||||||
disabled={appName !== selectedApp?.name}
|
|
||||||
>
|
|
||||||
Are you sure you want to delete the app <b>{selectedApp?.name}</b>?
|
|
||||||
|
|
||||||
<p>Please enter the app name below to confirm.</p>
|
|
||||||
<Input
|
|
||||||
bind:value={appName}
|
|
||||||
data-cy="delete-app-confirmation"
|
|
||||||
placeholder={selectedApp?.name}
|
|
||||||
/>
|
|
||||||
</ConfirmDialog>
|
|
||||||
<ConfirmDialog
|
|
||||||
bind:this={unpublishModal}
|
|
||||||
title="Confirm unpublish"
|
|
||||||
okText="Unpublish app"
|
|
||||||
onOk={confirmUnpublishApp}
|
|
||||||
dataCy={"unpublish-modal"}
|
|
||||||
>
|
|
||||||
Are you sure you want to unpublish the app <b>{selectedApp?.name}</b>?
|
|
||||||
</ConfirmDialog>
|
|
||||||
|
|
||||||
<ChooseIconModal app={selectedApp} bind:this={iconModal} />
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.appTable {
|
.appTable {
|
||||||
border-top: var(--border-light);
|
border-top: var(--border-light);
|
||||||
|
@ -547,12 +413,9 @@
|
||||||
height: 70px;
|
height: 70px;
|
||||||
display: grid;
|
display: grid;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
grid-gap: var(--spacing-xl);
|
|
||||||
grid-template-columns: auto 1fr;
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
padding: 0 var(--spacing-s);
|
|
||||||
}
|
}
|
||||||
.appTable :global(> div) {
|
.appTable :global(> div) {
|
||||||
border-bottom: var(--border-light);
|
border-bottom: var(--border-light);
|
||||||
|
|
Loading…
Reference in New Issue