Code review updates

This commit is contained in:
Dean 2022-05-18 17:22:58 +01:00
parent 346c5426eb
commit 3a0d1460b6
10 changed files with 426 additions and 234 deletions

View File

@ -40,6 +40,10 @@
padding-left: var(--spacing-xl); padding-left: var(--spacing-xl);
padding-right: 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 { .paddingY-S {
padding-top: var(--spacing-s); padding-top: var(--spacing-s);
padding-bottom: var(--spacing-s); padding-bottom: var(--spacing-s);
@ -56,6 +60,10 @@
padding-top: var(--spacing-xl); padding-top: var(--spacing-xl);
padding-bottom: 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 { .gap-XXS {
grid-gap: var(--spacing-xs); grid-gap: var(--spacing-xs);
} }

View File

@ -1,9 +1,10 @@
<script> <script>
export let wide = false export let wide = false
export let maxWidth = "80ch" export let maxWidth = "80ch"
export let noPadding = false
</script> </script>
<div style="--max-width: {maxWidth}" class:wide> <div style="--max-width: {maxWidth}" class:wide class:noPadding>
<slot /> <slot />
</div> </div>
@ -23,4 +24,9 @@
max-width: none; max-width: none;
margin: 0; margin: 0;
} }
.noPadding {
padding: 0px;
margin: 0px;
}
</style> </style>

View File

@ -28,8 +28,6 @@
$: lockedByHeading = $: lockedByHeading =
lockedBy && lockedByYou ? "Locked by you" : `Locked by ${lockIdentifer}` lockedBy && lockedByYou ? "Locked by you" : `Locked by ${lockIdentifer}`
$: lockExpiry = getExpiryDuration(app)
const getExpiryDuration = app => { const getExpiryDuration = app => {
if (!app?.lockedBy?.lockedAt) { if (!app?.lockedBy?.lockedAt) {
return -1 return -1
@ -86,12 +84,12 @@
between your team. between your team.
</p> </p>
{#if lockedByYou && lockExpiry > 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: lockExpiry, time: getExpiryDuration(app),
} }
)} )}
</span> </span>

View File

@ -10,20 +10,13 @@
</script> </script>
<div class="dash-card" data-cy={dataCy}> <div class="dash-card" data-cy={dataCy}>
<div <div class="dash-card-header" class:active={actionDefined} on:click={action}>
class={actionDefined ? "dash-card-header active" : "dash-card-header"}
on:click={() => {
if (actionDefined) {
action()
}
}}
>
<span class="dash-card-title"> <span class="dash-card-title">
<Detail size="M">{title}</Detail> <Detail size="M">{title}</Detail>
</span> </span>
<span class="dash-card-action"> <span class="dash-card-action">
{#if actionDefined} {#if actionDefined}
<Icon name={actionIcon ? actionIcon : "ChevronRight"} /> <Icon name={actionIcon || "ChevronRight"} />
{/if} {/if}
</span> </span>
</div> </div>
@ -36,7 +29,6 @@
.dash-card { .dash-card {
background: var(--spectrum-alias-background-color-primary); background: var(--spectrum-alias-background-color-primary);
border-radius: var(--border-radius-s); border-radius: var(--border-radius-s);
border: 1px solid var(--spectrum-global-color-gray-300);
overflow: hidden; overflow: hidden;
min-height: 150px; min-height: 150px;
} }

View File

@ -321,7 +321,7 @@
.mobile-toggle, .mobile-toggle,
.user-dropdown { .user-dropdown {
flex: 1 1 0; flex: 0 1 0;
} }
/* Reduce BBUI page padding */ /* Reduce BBUI page padding */

View File

@ -10,7 +10,6 @@
notifications, notifications,
Body, Body,
Search, Search,
Divider,
Helpers, Helpers,
} from "@budibase/bbui" } from "@budibase/bbui"
import TemplateDisplay from "components/common/TemplateDisplay.svelte" import TemplateDisplay from "components/common/TemplateDisplay.svelte"
@ -66,6 +65,9 @@
app?.name?.toLowerCase().includes(searchTerm.toLowerCase()) app?.name?.toLowerCase().includes(searchTerm.toLowerCase())
) )
$: lockedApps = filteredApps.filter(app => app?.lockedYou || app?.lockedOther)
$: unlocked = lockedApps?.length == 0
const enrichApps = (apps, user, sortBy) => { const enrichApps = (apps, user, sortBy) => {
const enrichedApps = apps.map(app => ({ const enrichedApps = apps.map(app => ({
...app, ...app,
@ -178,10 +180,6 @@
} }
} }
const previewApp = app => {
window.open(`/${app.devId}`)
}
const appOverview = app => { const appOverview = app => {
$goto(`../overview/${app.devId}`) $goto(`../overview/${app.devId}`)
} }
@ -348,7 +346,7 @@
{/if} {/if}
{#if enrichedApps.length} {#if enrichedApps.length}
<Layout noPadding gap="S"> <Layout noPadding gap="L">
<div class="title"> <div class="title">
<div class="buttons"> <div class="buttons">
<Button <Button
@ -414,8 +412,8 @@
</div> </div>
{/if} {/if}
</div> </div>
<Divider size="S" />
<div class="appTable"> <div class="appTable" class:unlocked>
{#each filteredApps as app (app.appId)} {#each filteredApps as app (app.appId)}
<AppRow <AppRow
{copyAppId} {copyAppId}
@ -428,7 +426,6 @@
{exportApp} {exportApp}
{deleteApp} {deleteApp}
{updateApp} {updateApp}
{previewApp}
{appOverview} {appOverview}
/> />
{/each} {/each}
@ -490,6 +487,9 @@
<ChooseIconModal app={selectedApp} bind:this={iconModal} /> <ChooseIconModal app={selectedApp} bind:this={iconModal} />
<style> <style>
.appTable {
border-top: var(--border-light);
}
.app-actions { .app-actions {
display: flex; display: flex;
} }
@ -533,6 +533,11 @@
grid-template-columns: 1fr 1fr 1fr 1fr auto; grid-template-columns: 1fr 1fr 1fr 1fr auto;
align-items: center; align-items: center;
} }
.appTable.unlocked {
grid-template-columns: 1fr 1fr auto 1fr auto;
}
.appTable :global(> div) { .appTable :global(> div) {
height: 70px; height: 70px;
display: grid; display: grid;

View File

@ -1,6 +1,6 @@
<script> <script>
import { goto } from "@roxi/routify" 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 TemplateDisplay from "components/common/TemplateDisplay.svelte"
import { onMount } from "svelte" import { onMount } from "svelte"
import { templates } from "stores/portal" import { templates } from "stores/portal"
@ -25,16 +25,15 @@
<Page wide> <Page wide>
<Layout noPadding gap="XL"> <Layout noPadding gap="XL">
<span> <span>
<Button <ActionButton
quiet
secondary secondary
icon={"ChevronLeft"} icon={"ArrowLeft"}
on:click={() => { on:click={() => {
$goto("../") $goto("../")
}} }}
> >
Back Back
</Button> </ActionButton>
</span> </span>
{#if loaded && $templates?.length} {#if loaded && $templates?.length}
<TemplateDisplay templates={$templates} /> <TemplateDisplay templates={$templates} />

View File

@ -4,12 +4,18 @@
Layout, Layout,
Page, Page,
Button, Button,
ActionButton,
ButtonGroup, ButtonGroup,
Heading, Heading,
Tab, Tab,
Tabs, Tabs,
notifications, notifications,
ProgressCircle, ProgressCircle,
Input,
ActionMenu,
MenuItem,
Icon,
Helpers,
} from "@budibase/bbui" } from "@budibase/bbui"
import OverviewTab from "../_components/OverviewTab.svelte" import OverviewTab from "../_components/OverviewTab.svelte"
import SettingsTab from "../_components/SettingsTab.svelte" import SettingsTab from "../_components/SettingsTab.svelte"
@ -20,12 +26,17 @@
import { AppStatus } from "constants" import { AppStatus } from "constants"
import AppLockModal from "components/common/AppLockModal.svelte" import AppLockModal from "components/common/AppLockModal.svelte"
import EditableIcon from "components/common/EditableIcon.svelte" import EditableIcon from "components/common/EditableIcon.svelte"
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
import { checkIncomingDeploymentStatus } from "components/deploy/utils" import { checkIncomingDeploymentStatus } from "components/deploy/utils"
import { onDestroy, onMount } from "svelte" import { onDestroy, onMount } from "svelte"
export let application export let application
let promise = getPackage() let promise = getPackage()
let loaded = false
let deletionModal
let unpublishModal
let appName = ""
// App // App
$: filteredApps = $apps.filter(app => app.devId === application) $: filteredApps = $apps.filter(app => app.devId === application)
@ -74,6 +85,7 @@
try { try {
const pkg = await API.fetchAppPackage(application) const pkg = await API.fetchAppPackage(application)
await store.actions.initialise(pkg) await store.actions.initialise(pkg)
loaded = true
return pkg return pkg
} catch (error) { } catch (error) {
notifications.error(`Error initialising app: ${error?.message}`) notifications.error(`Error initialising app: ${error?.message}`)
@ -121,6 +133,58 @@
$goto(`../../../app/${app.devId}`) $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(() => { onDestroy(() => {
store.actions.reset() store.actions.reset()
}) })
@ -130,6 +194,7 @@
if (!apps.length) { if (!apps.length) {
await apps.load() await apps.load()
} }
await API.syncApp(application)
deployments = await fetchDeployments() deployments = await fetchDeployments()
} catch (error) { } catch (error) {
notifications.error("Error initialising app overview") notifications.error("Error initialising app overview")
@ -137,18 +202,24 @@
}) })
</script> </script>
<Page wide> <span class="overview-wrap">
<Layout noPadding gap="XL"> <Page wide noPadding>
<span>
<Button quiet secondary icon={"ChevronLeft"} on:click={backToAppList}>
Back
</Button>
</span>
{#await promise} {#await promise}
<span class="page-header">
<ActionButton secondary icon={"ArrowLeft"} on:click={backToAppList}>
Back
</ActionButton>
</span>
<div class="loading"> <div class="loading">
<ProgressCircle size="XL" /> <ProgressCircle size="XL" />
</div> </div>
{:then _} {: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="overview-header">
<div class="app-title"> <div class="app-title">
<div class="app-logo"> <div class="app-logo">
@ -173,6 +244,7 @@
<ButtonGroup gap="XS"> <ButtonGroup gap="XS">
<Button <Button
size="M" size="M"
quiet
secondary secondary
icon="Globe" icon="Globe"
disabled={!isPublished} disabled={!isPublished}
@ -193,8 +265,34 @@
<span>Edit</span> <span>Edit</span>
</Button> </Button>
</ButtonGroup> </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>
</div> </div>
</Layout>
<div class="tab-wrap">
<Tabs <Tabs
selected={selectedTab} selected={selectedTab}
noPadding noPadding
@ -221,11 +319,38 @@
<SettingsTab app={selectedApp} /> <SettingsTab app={selectedApp} />
</Tab> </Tab>
</Tabs> </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} {:catch error}
<p>Something went wrong: {error.message}</p> <p>Something went wrong: {error.message}</p>
{/await} {/await}
</Layout>
</Page> </Page>
</span>
<style> <style>
.app-url { .app-url {
@ -234,11 +359,35 @@
.loading { .loading {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center;
flex: 1;
} }
.overview-header { .overview-header {
display: flex; display: flex;
justify-content: space-between; 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 { .app-title {
display: flex; display: flex;
gap: var(--spacing-m); gap: var(--spacing-m);
@ -252,4 +401,13 @@
line-height: 1em; line-height: 1em;
margin-bottom: var(--spacing-s); 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> </style>

View File

@ -1,7 +1,14 @@
<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, notifications } from "@budibase/bbui" import {
Icon,
Heading,
Link,
Avatar,
notifications,
Layout,
} 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"
@ -41,6 +48,7 @@
</script> </script>
<div class="overview-tab"> <div class="overview-tab">
<Layout paddingX="XXL" paddingY="XXL">
<div class="top"> <div class="top">
<DashCard title={"App Status"} dataCy={"app-status"}> <DashCard title={"App Status"} dataCy={"app-status"}>
<div class="status-content"> <div class="status-content">
@ -92,7 +100,8 @@
{processStringSync( {processStringSync(
"Last edited {{ duration time 'millisecond' }} ago", "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} {/if}
@ -111,7 +120,8 @@
<Heading size="XS">{$store.version}</Heading> <Heading size="XS">{$store.version}</Heading>
{#if updateAvailable} {#if updateAvailable}
<p class="version-status"> <p class="version-status">
New version <strong>{clientPackage.version}</strong> is available - New version <strong>{clientPackage.version}</strong> is available
-
<Link <Link
on:click={() => { on:click={() => {
if (typeof navigateTab === "function") { if (typeof navigateTab === "function") {
@ -167,6 +177,7 @@
</DashCard> </DashCard>
</div> </div>
{/if} {/if}
</Layout>
</div> </div>
<style> <style>
@ -180,6 +191,23 @@
grid-gap: var(--spacing-xl); grid-gap: var(--spacing-xl);
grid-template-columns: repeat(auto-fill, minmax(30%, 1fr)); 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, .overview-tab .bottom,
.automation-metrics { .automation-metrics {
display: grid; display: grid;

View File

@ -28,10 +28,10 @@
<div class="settings-tab"> <div class="settings-tab">
<Page wide={false}> <Page wide={false}>
<Layout gap="XL" noPadding> <Layout gap="XL" paddingY="XXL" paddingX="">
<span class="details-section"> <span class="details-section">
<Layout gap="XS" noPadding> <Layout gap="XS" noPadding>
<Heading size="S">Name and Url</Heading> <Heading size="S">Name and URL</Heading>
<Divider /> <Divider />
<Body> <Body>
<div class="app-details"> <div class="app-details">
@ -60,8 +60,8 @@
</Layout> </Layout>
</span> </span>
<span class="version-section"> <span class="version-section">
<Layout gap="XS" noPadding> <Layout gap="XS" paddingY="XXL" paddingX="">
<Heading size="S">App Version</Heading> <Heading size="S">App version</Heading>
<Divider /> <Divider />
<Body> <Body>
{#if updateAvailable} {#if updateAvailable}
@ -76,28 +76,26 @@
<strong>{$store.version}</strong>. You're running the latest! <strong>{$store.version}</strong>. You're running the latest!
</p> </p>
{/if} {/if}
<p>
Updates can contain new features, performance improvements and bug Updates can contain new features, performance improvements and bug
fixes. fixes.
</p>
<div class="page-action"> <div class="page-action">
<Button cta on:click={versionModal.show()}>Update App</Button> <Button cta on:click={versionModal.show()}>Update app</Button>
</div> </div>
</Body> </Body>
</Layout> </Layout>
</span> </span>
<span class="selfhost-section"> <span class="selfhost-section">
<Layout gap="XS" noPadding> <Layout gap="XS" paddingY="XXL" paddingX="">
<Heading size="S">Self-host Budibase</Heading> <Heading size="S">Self-host Budibase</Heading>
<Divider /> <Divider />
<Body> <Body>
<p> Self-host Budibase for free to get unlimited apps and more - and it
Self-host Budibase for free to get unlimited apps and more - and only takes a few minutes!
it only takes a few minutes!
</p>
<div class="page-action"> <div class="page-action">
<Button <Button
cta secondary
on:click={() => { on:click={() => {
window.open(selfHostPath, "_blank") window.open(selfHostPath, "_blank")
}}>Self-host Budibase</Button }}>Self-host Budibase</Button