Code review updates
This commit is contained in:
parent
5a6c727b0f
commit
11293997a9
10 changed files with 426 additions and 234 deletions
|
@ -40,6 +40,10 @@
|
|||
padding-left: var(--spacing-xl);
|
||||
padding-right: var(--spacing-xl);
|
||||
}
|
||||
.paddingX-XXL {
|
||||
padding-left: calc(var(--spacing-xl) * 2);
|
||||
padding-right: calc(var(--spacing-xl) * 2);
|
||||
}
|
||||
.paddingY-S {
|
||||
padding-top: var(--spacing-s);
|
||||
padding-bottom: var(--spacing-s);
|
||||
|
@ -56,6 +60,10 @@
|
|||
padding-top: var(--spacing-xl);
|
||||
padding-bottom: var(--spacing-xl);
|
||||
}
|
||||
.paddingY-XXL {
|
||||
padding-top: calc(var(--spacing-xl) * 2);
|
||||
padding-bottom: calc(var(--spacing-xl) * 2);
|
||||
}
|
||||
.gap-XXS {
|
||||
grid-gap: var(--spacing-xs);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
<script>
|
||||
export let wide = false
|
||||
export let maxWidth = "80ch"
|
||||
export let noPadding = false
|
||||
</script>
|
||||
|
||||
<div style="--max-width: {maxWidth}" class:wide>
|
||||
<div style="--max-width: {maxWidth}" class:wide class:noPadding>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
|
@ -23,4 +24,9 @@
|
|||
max-width: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.noPadding {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -28,8 +28,6 @@
|
|||
$: lockedByHeading =
|
||||
lockedBy && lockedByYou ? "Locked by you" : `Locked by ${lockIdentifer}`
|
||||
|
||||
$: lockExpiry = getExpiryDuration(app)
|
||||
|
||||
const getExpiryDuration = app => {
|
||||
if (!app?.lockedBy?.lockedAt) {
|
||||
return -1
|
||||
|
@ -86,12 +84,12 @@
|
|||
between your team.
|
||||
</p>
|
||||
|
||||
{#if lockedByYou && lockExpiry > 0}
|
||||
{#if lockedByYou && getExpiryDuration(app) > 0}
|
||||
<span class="lock-expiry-body">
|
||||
{processStringSync(
|
||||
"This lock will expire in {{ duration time 'millisecond' }} from now",
|
||||
{
|
||||
time: lockExpiry,
|
||||
time: getExpiryDuration(app),
|
||||
}
|
||||
)}
|
||||
</span>
|
||||
|
|
|
@ -10,20 +10,13 @@
|
|||
</script>
|
||||
|
||||
<div class="dash-card" data-cy={dataCy}>
|
||||
<div
|
||||
class={actionDefined ? "dash-card-header active" : "dash-card-header"}
|
||||
on:click={() => {
|
||||
if (actionDefined) {
|
||||
action()
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div class="dash-card-header" class:active={actionDefined} on:click={action}>
|
||||
<span class="dash-card-title">
|
||||
<Detail size="M">{title}</Detail>
|
||||
</span>
|
||||
<span class="dash-card-action">
|
||||
{#if actionDefined}
|
||||
<Icon name={actionIcon ? actionIcon : "ChevronRight"} />
|
||||
<Icon name={actionIcon || "ChevronRight"} />
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
|
@ -36,7 +29,6 @@
|
|||
.dash-card {
|
||||
background: var(--spectrum-alias-background-color-primary);
|
||||
border-radius: var(--border-radius-s);
|
||||
border: 1px solid var(--spectrum-global-color-gray-300);
|
||||
overflow: hidden;
|
||||
min-height: 150px;
|
||||
}
|
||||
|
|
|
@ -321,7 +321,7 @@
|
|||
|
||||
.mobile-toggle,
|
||||
.user-dropdown {
|
||||
flex: 1 1 0;
|
||||
flex: 0 1 0;
|
||||
}
|
||||
|
||||
/* Reduce BBUI page padding */
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
notifications,
|
||||
Body,
|
||||
Search,
|
||||
Divider,
|
||||
Helpers,
|
||||
} from "@budibase/bbui"
|
||||
import TemplateDisplay from "components/common/TemplateDisplay.svelte"
|
||||
|
@ -66,6 +65,9 @@
|
|||
app?.name?.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
)
|
||||
|
||||
$: lockedApps = filteredApps.filter(app => app?.lockedYou || app?.lockedOther)
|
||||
$: unlocked = lockedApps?.length == 0
|
||||
|
||||
const enrichApps = (apps, user, sortBy) => {
|
||||
const enrichedApps = apps.map(app => ({
|
||||
...app,
|
||||
|
@ -178,10 +180,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
const previewApp = app => {
|
||||
window.open(`/${app.devId}`)
|
||||
}
|
||||
|
||||
const appOverview = app => {
|
||||
$goto(`../overview/${app.devId}`)
|
||||
}
|
||||
|
@ -348,7 +346,7 @@
|
|||
{/if}
|
||||
|
||||
{#if enrichedApps.length}
|
||||
<Layout noPadding gap="S">
|
||||
<Layout noPadding gap="L">
|
||||
<div class="title">
|
||||
<div class="buttons">
|
||||
<Button
|
||||
|
@ -414,8 +412,8 @@
|
|||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<Divider size="S" />
|
||||
<div class="appTable">
|
||||
|
||||
<div class="appTable" class:unlocked>
|
||||
{#each filteredApps as app (app.appId)}
|
||||
<AppRow
|
||||
{copyAppId}
|
||||
|
@ -428,7 +426,6 @@
|
|||
{exportApp}
|
||||
{deleteApp}
|
||||
{updateApp}
|
||||
{previewApp}
|
||||
{appOverview}
|
||||
/>
|
||||
{/each}
|
||||
|
@ -490,6 +487,9 @@
|
|||
<ChooseIconModal app={selectedApp} bind:this={iconModal} />
|
||||
|
||||
<style>
|
||||
.appTable {
|
||||
border-top: var(--border-light);
|
||||
}
|
||||
.app-actions {
|
||||
display: flex;
|
||||
}
|
||||
|
@ -533,6 +533,11 @@
|
|||
grid-template-columns: 1fr 1fr 1fr 1fr auto;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.appTable.unlocked {
|
||||
grid-template-columns: 1fr 1fr auto 1fr auto;
|
||||
}
|
||||
|
||||
.appTable :global(> div) {
|
||||
height: 70px;
|
||||
display: grid;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { goto } from "@roxi/routify"
|
||||
import { Layout, Page, notifications, Button } from "@budibase/bbui"
|
||||
import { Layout, Page, notifications, ActionButton } from "@budibase/bbui"
|
||||
import TemplateDisplay from "components/common/TemplateDisplay.svelte"
|
||||
import { onMount } from "svelte"
|
||||
import { templates } from "stores/portal"
|
||||
|
@ -25,16 +25,15 @@
|
|||
<Page wide>
|
||||
<Layout noPadding gap="XL">
|
||||
<span>
|
||||
<Button
|
||||
quiet
|
||||
<ActionButton
|
||||
secondary
|
||||
icon={"ChevronLeft"}
|
||||
icon={"ArrowLeft"}
|
||||
on:click={() => {
|
||||
$goto("../")
|
||||
}}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
</ActionButton>
|
||||
</span>
|
||||
{#if loaded && $templates?.length}
|
||||
<TemplateDisplay templates={$templates} />
|
||||
|
|
|
@ -4,12 +4,18 @@
|
|||
Layout,
|
||||
Page,
|
||||
Button,
|
||||
ActionButton,
|
||||
ButtonGroup,
|
||||
Heading,
|
||||
Tab,
|
||||
Tabs,
|
||||
notifications,
|
||||
ProgressCircle,
|
||||
Input,
|
||||
ActionMenu,
|
||||
MenuItem,
|
||||
Icon,
|
||||
Helpers,
|
||||
} from "@budibase/bbui"
|
||||
import OverviewTab from "../_components/OverviewTab.svelte"
|
||||
import SettingsTab from "../_components/SettingsTab.svelte"
|
||||
|
@ -20,12 +26,17 @@
|
|||
import { AppStatus } from "constants"
|
||||
import AppLockModal from "components/common/AppLockModal.svelte"
|
||||
import EditableIcon from "components/common/EditableIcon.svelte"
|
||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||
import { checkIncomingDeploymentStatus } from "components/deploy/utils"
|
||||
import { onDestroy, onMount } from "svelte"
|
||||
|
||||
export let application
|
||||
|
||||
let promise = getPackage()
|
||||
let loaded = false
|
||||
let deletionModal
|
||||
let unpublishModal
|
||||
let appName = ""
|
||||
|
||||
// App
|
||||
$: filteredApps = $apps.filter(app => app.devId === application)
|
||||
|
@ -74,6 +85,7 @@
|
|||
try {
|
||||
const pkg = await API.fetchAppPackage(application)
|
||||
await store.actions.initialise(pkg)
|
||||
loaded = true
|
||||
return pkg
|
||||
} catch (error) {
|
||||
notifications.error(`Error initialising app: ${error?.message}`)
|
||||
|
@ -121,6 +133,58 @@
|
|||
$goto(`../../../app/${app.devId}`)
|
||||
}
|
||||
|
||||
const copyAppId = async app => {
|
||||
await Helpers.copyToClipboard(app.prodId)
|
||||
notifications.success("App ID copied to clipboard.")
|
||||
}
|
||||
|
||||
const exportApp = app => {
|
||||
const id = isPublished ? app.prodId : app.devId
|
||||
const appName = encodeURIComponent(app.name)
|
||||
window.location = `/api/backups/export?appId=${id}&appname=${appName}`
|
||||
}
|
||||
|
||||
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)
|
||||
backToAppList()
|
||||
notifications.success("App deleted successfully")
|
||||
} catch (err) {
|
||||
notifications.error("Error deleting app")
|
||||
}
|
||||
selectedApp = null
|
||||
appName = null
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
store.actions.reset()
|
||||
})
|
||||
|
@ -130,6 +194,7 @@
|
|||
if (!apps.length) {
|
||||
await apps.load()
|
||||
}
|
||||
await API.syncApp(application)
|
||||
deployments = await fetchDeployments()
|
||||
} catch (error) {
|
||||
notifications.error("Error initialising app overview")
|
||||
|
@ -137,18 +202,24 @@
|
|||
})
|
||||
</script>
|
||||
|
||||
<Page wide>
|
||||
<Layout noPadding gap="XL">
|
||||
<span>
|
||||
<Button quiet secondary icon={"ChevronLeft"} on:click={backToAppList}>
|
||||
Back
|
||||
</Button>
|
||||
</span>
|
||||
<span class="overview-wrap">
|
||||
<Page wide noPadding>
|
||||
{#await promise}
|
||||
<span class="page-header">
|
||||
<ActionButton secondary icon={"ArrowLeft"} on:click={backToAppList}>
|
||||
Back
|
||||
</ActionButton>
|
||||
</span>
|
||||
<div class="loading">
|
||||
<ProgressCircle size="XL" />
|
||||
</div>
|
||||
{:then _}
|
||||
<Layout paddingX="XXL" paddingY="XXL" gap="XL">
|
||||
<span class="page-header" class:loaded>
|
||||
<ActionButton secondary icon={"ArrowLeft"} on:click={backToAppList}>
|
||||
Back
|
||||
</ActionButton>
|
||||
</span>
|
||||
<div class="overview-header">
|
||||
<div class="app-title">
|
||||
<div class="app-logo">
|
||||
|
@ -173,6 +244,7 @@
|
|||
<ButtonGroup gap="XS">
|
||||
<Button
|
||||
size="M"
|
||||
quiet
|
||||
secondary
|
||||
icon="Globe"
|
||||
disabled={!isPublished}
|
||||
|
@ -193,8 +265,34 @@
|
|||
<span>Edit</span>
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
<ActionMenu align="right" dataCy="app-overview-menu-popover">
|
||||
<span slot="control" class="app-overview-actions-icon">
|
||||
<Icon hoverable name="More" />
|
||||
</span>
|
||||
<MenuItem on:click={() => exportApp(selectedApp)} icon="Download">
|
||||
Export
|
||||
</MenuItem>
|
||||
{#if isPublished}
|
||||
<MenuItem
|
||||
on:click={() => unpublishApp(selectedApp)}
|
||||
icon="GlobeRemove"
|
||||
>
|
||||
Unpublish
|
||||
</MenuItem>
|
||||
<MenuItem on:click={() => copyAppId(selectedApp)} icon="Copy">
|
||||
Copy App ID
|
||||
</MenuItem>
|
||||
{/if}
|
||||
{#if !isPublished}
|
||||
<MenuItem on:click={() => deleteApp(selectedApp)} icon="Delete">
|
||||
Delete
|
||||
</MenuItem>
|
||||
{/if}
|
||||
</ActionMenu>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
<div class="tab-wrap">
|
||||
<Tabs
|
||||
selected={selectedTab}
|
||||
noPadding
|
||||
|
@ -221,11 +319,38 @@
|
|||
<SettingsTab app={selectedApp} />
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</div>
|
||||
<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>
|
||||
{:catch error}
|
||||
<p>Something went wrong: {error.message}</p>
|
||||
{/await}
|
||||
</Layout>
|
||||
</Page>
|
||||
</Page>
|
||||
</span>
|
||||
|
||||
<style>
|
||||
.app-url {
|
||||
|
@ -234,11 +359,35 @@
|
|||
.loading {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
.overview-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.page-header.loaded {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.overview-wrap :global(> div > .container),
|
||||
.tab-wrap :global(.spectrum-Tabs) {
|
||||
background-color: var(--background);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
@media (max-width: 1000px) {
|
||||
.overview-header {
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-l);
|
||||
}
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.overview-wrap :global(.content > *) {
|
||||
padding: calc(var(--spacing-xl) * 1.5) !important;
|
||||
}
|
||||
}
|
||||
.app-title {
|
||||
display: flex;
|
||||
gap: var(--spacing-m);
|
||||
|
@ -252,4 +401,13 @@
|
|||
line-height: 1em;
|
||||
margin-bottom: var(--spacing-s);
|
||||
}
|
||||
.tab-wrap :global(.spectrum-Tabs) {
|
||||
padding-left: calc(var(--spacing-xl) * 2);
|
||||
padding-right: calc(var(--spacing-xl) * 2);
|
||||
}
|
||||
.page-header {
|
||||
padding-left: calc(var(--spacing-xl) * 2);
|
||||
padding-right: calc(var(--spacing-xl) * 2);
|
||||
padding-top: calc(var(--spacing-xl) * 2);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
<script>
|
||||
import DashCard from "components/common/DashCard.svelte"
|
||||
import { AppStatus } from "constants"
|
||||
import { Icon, Heading, Link, Avatar, notifications } from "@budibase/bbui"
|
||||
import {
|
||||
Icon,
|
||||
Heading,
|
||||
Link,
|
||||
Avatar,
|
||||
notifications,
|
||||
Layout,
|
||||
} from "@budibase/bbui"
|
||||
import { store } from "builderStore"
|
||||
import clientPackage from "@budibase/client/package.json"
|
||||
import { processStringSync } from "@budibase/string-templates"
|
||||
|
@ -41,6 +48,7 @@
|
|||
</script>
|
||||
|
||||
<div class="overview-tab">
|
||||
<Layout paddingX="XXL" paddingY="XXL">
|
||||
<div class="top">
|
||||
<DashCard title={"App Status"} dataCy={"app-status"}>
|
||||
<div class="status-content">
|
||||
|
@ -92,7 +100,8 @@
|
|||
{processStringSync(
|
||||
"Last edited {{ duration time 'millisecond' }} ago",
|
||||
{
|
||||
time: new Date().getTime() - new Date(app?.updatedAt).getTime(),
|
||||
time:
|
||||
new Date().getTime() - new Date(app?.updatedAt).getTime(),
|
||||
}
|
||||
)}
|
||||
{/if}
|
||||
|
@ -111,7 +120,8 @@
|
|||
<Heading size="XS">{$store.version}</Heading>
|
||||
{#if updateAvailable}
|
||||
<p class="version-status">
|
||||
New version <strong>{clientPackage.version}</strong> is available -
|
||||
New version <strong>{clientPackage.version}</strong> is available
|
||||
-
|
||||
<Link
|
||||
on:click={() => {
|
||||
if (typeof navigateTab === "function") {
|
||||
|
@ -167,6 +177,7 @@
|
|||
</DashCard>
|
||||
</div>
|
||||
{/if}
|
||||
</Layout>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
@ -180,6 +191,23 @@
|
|||
grid-gap: var(--spacing-xl);
|
||||
grid-template-columns: repeat(auto-fill, minmax(30%, 1fr));
|
||||
}
|
||||
|
||||
@media (max-width: 1000px) {
|
||||
.overview-tab .top {
|
||||
display: grid;
|
||||
grid-gap: var(--spacing-xl);
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
.overview-tab .top {
|
||||
display: grid;
|
||||
grid-gap: var(--spacing-xl);
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.overview-tab .bottom,
|
||||
.automation-metrics {
|
||||
display: grid;
|
||||
|
|
|
@ -28,10 +28,10 @@
|
|||
|
||||
<div class="settings-tab">
|
||||
<Page wide={false}>
|
||||
<Layout gap="XL" noPadding>
|
||||
<Layout gap="XL" paddingY="XXL" paddingX="">
|
||||
<span class="details-section">
|
||||
<Layout gap="XS" noPadding>
|
||||
<Heading size="S">Name and Url</Heading>
|
||||
<Heading size="S">Name and URL</Heading>
|
||||
<Divider />
|
||||
<Body>
|
||||
<div class="app-details">
|
||||
|
@ -60,8 +60,8 @@
|
|||
</Layout>
|
||||
</span>
|
||||
<span class="version-section">
|
||||
<Layout gap="XS" noPadding>
|
||||
<Heading size="S">App Version</Heading>
|
||||
<Layout gap="XS" paddingY="XXL" paddingX="">
|
||||
<Heading size="S">App version</Heading>
|
||||
<Divider />
|
||||
<Body>
|
||||
{#if updateAvailable}
|
||||
|
@ -76,28 +76,26 @@
|
|||
<strong>{$store.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()}>Update App</Button>
|
||||
<Button cta on:click={versionModal.show()}>Update app</Button>
|
||||
</div>
|
||||
</Body>
|
||||
</Layout>
|
||||
</span>
|
||||
<span class="selfhost-section">
|
||||
<Layout gap="XS" noPadding>
|
||||
<Layout gap="XS" paddingY="XXL" paddingX="">
|
||||
<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>
|
||||
Self-host Budibase for free to get unlimited apps and more - and it
|
||||
only takes a few minutes!
|
||||
<div class="page-action">
|
||||
<Button
|
||||
cta
|
||||
secondary
|
||||
on:click={() => {
|
||||
window.open(selfHostPath, "_blank")
|
||||
}}>Self-host Budibase</Button
|
||||
|
|
Loading…
Reference in a new issue