More WIP portal redesign!

This commit is contained in:
Andrew Kingston 2022-11-09 09:45:37 +00:00
parent 16bfe97015
commit 98ce87e8f7
28 changed files with 295 additions and 243 deletions

View File

@ -25,7 +25,7 @@
}
.narrow {
max-width: 800px;
max-width: 840px;
margin: 0;
}
</style>

View File

@ -12,7 +12,7 @@
dummy.select()
document.execCommand("copy")
document.body.removeChild(dummy)
notifications.success(`URL copied to clipboard`)
notifications.success(`Copied to clipboard`)
}
</script>

View File

@ -15,6 +15,7 @@
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
gap: 4px;
}
.title {
margin-left: var(--spacing-m);

View File

@ -1,41 +0,0 @@
<script>
import { ModalContent, Body, notifications } from "@budibase/bbui"
import { auth } from "stores/portal"
import { onMount } from "svelte"
import CopyInput from "components/common/inputs/CopyInput.svelte"
let apiKey = null
async function generateAPIKey() {
try {
apiKey = await auth.generateAPIKey()
notifications.success("New API key generated")
} catch (err) {
notifications.error("Unable to generate new API key")
}
// need to return false to keep modal open
return false
}
onMount(async () => {
try {
apiKey = await auth.fetchAPIKey()
} catch (err) {
notifications.error("Unable to fetch API key")
}
})
</script>
<ModalContent
title="Developer information"
showConfirmButton={false}
showSecondaryButton={true}
secondaryButtonText="Re-generate key"
secondaryAction={generateAPIKey}
>
<Body size="S">
You can find information about your developer account here, such as the API
key used to access the Budibase API.
</Body>
<CopyInput bind:value={apiKey} label="API key" />
</ModalContent>

View File

@ -11,8 +11,12 @@
<div class="app-row">
<div class="header">
<div class="title" data-cy={`${app.devId}`}>
<div class="app-icon" style="color: {app.icon?.color || ''}">
<Icon size="L" name={app.icon?.name || "Apps"} />
<div class="app-icon">
<Icon
size="L"
name={app.icon?.name || "Apps"}
color={app.icon?.color}
/>
</div>
<div class="name" data-cy="app-name-link" on:click={() => editApp(app)}>
<Heading size="S">
@ -96,7 +100,7 @@
.app-status:not(.deployed) :global(.spectrum-Icon),
.app-status:not(.deployed) :global(.spectrum-Body) {
color: var(--spectrum-global-color-gray-700);
color: var(--spectrum-global-color-gray-600);
}
.app-row-actions {

View File

@ -89,7 +89,7 @@
{#if $auth.user && loaded}
<div class="container">
<Page>
<Page narrow>
<div class="content">
<Layout noPadding>
<div class="header">
@ -196,6 +196,11 @@
.container {
height: 100%;
overflow: auto;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
padding: 80px;
}
.content {
width: 100%;

View File

@ -15,14 +15,12 @@
import { onMount } from "svelte"
import UpdateUserInfoModal from "components/settings/UpdateUserInfoModal.svelte"
import ChangePasswordModal from "components/settings/ChangePasswordModal.svelte"
import UpdateAPIKeyModal from "components/settings/UpdateAPIKeyModal.svelte"
import Logo from "assets/bb-emblem.svg"
import { isEnabled, TENANT_FEATURE_FLAGS } from "helpers/featureFlags"
let loaded = false
let userInfoModal
let changePasswordModal
let apiKeyModal
let mobileMenuVisible = false
let activeTab = "Apps"
@ -42,92 +40,79 @@
title: "Users",
href: "/builder/portal/users/users",
},
{ title: "Auth", href: "/builder/portal/manage/auth" },
{ title: "Email", href: "/builder/portal/manage/email" },
{
title: "Plugins",
href: "/builder/portal/manage/plugins",
href: "/builder/portal/plugins",
badge: "New",
},
{
title: "Organisation",
href: "/builder/portal/settings/organisation",
heading: "Settings",
title: "Usage",
href: "/builder/portal/usage",
},
{
title: "Theming",
href: "/builder/portal/settings/theming",
},
])
if (!$adminStore.cloud) {
menu = menu.concat([
{
title: "Update",
href: "/builder/portal/settings/update",
},
])
}
} else {
menu = menu.concat([
{
title: "Theming",
href: "/builder/portal/settings/theming",
heading: "Settings",
title: "Settings",
href: "/builder/portal/settings",
},
])
// if (!$adminStore.cloud) {
// menu = menu.concat([
// {
// title: "Update",
// href: "/builder/portal/settings/update",
// },
// ])
// }
}
// add link to account portal if the user has access
let accountSectionAdded = false
// link out to account-portal if account holder in cloud or always in self-host
if ($auth?.user?.accountPortalAccess || (!$adminStore.cloud && admin)) {
accountSectionAdded = true
menu = menu.concat([
{
title: "Account",
href: $adminStore.accountPortalUrl,
heading: "Account",
},
])
}
if (isEnabled(TENANT_FEATURE_FLAGS.LICENSING)) {
// always show usage in self-host or cloud if licensing enabled
menu = menu.concat([
{
title: "Usage",
href: "/builder/portal/settings/usage",
heading: accountSectionAdded ? "" : "Account",
},
])
// show the relevant hosting upgrade page
if ($adminStore.cloud && $auth?.user?.accountPortalAccess) {
menu = menu.concat([
{
title: "Upgrade",
href: $adminStore.accountPortalUrl + "/portal/upgrade",
badge: "New",
},
])
}
// show the billing page to licensed account holders in cloud
if (
$auth?.user?.accountPortalAccess &&
$auth.user.account.stripeCustomerId
) {
menu = menu.concat([
{
title: "Billing",
href: $adminStore.accountPortalUrl + "/portal/billing",
},
])
}
}
// if ($auth?.user?.accountPortalAccess || (!$adminStore.cloud && admin)) {
// accountSectionAdded = true
// menu = menu.concat([
// {
// title: "Account",
// href: $adminStore.accountPortalUrl,
// heading: "Account",
// },
// ])
// }
//
// if (isEnabled(TENANT_FEATURE_FLAGS.LICENSING)) {
// // always show usage in self-host or cloud if licensing enabled
// menu = menu.concat([
// {
// title: "Usage",
// href: "/builder/portal/settings/usage",
// heading: accountSectionAdded ? "" : "Account",
// },
// ])
//
// // show the relevant hosting upgrade page
// if ($adminStore.cloud && $auth?.user?.accountPortalAccess) {
// menu = menu.concat([
// {
// title: "Upgrade",
// href: $adminStore.accountPortalUrl + "/portal/upgrade",
// badge: "New",
// },
// ])
// }
//
// // show the billing page to licensed account holders in cloud
// if (
// $auth?.user?.accountPortalAccess &&
// $auth.user.account.stripeCustomerId
// ) {
// menu = menu.concat([
// {
// title: "Billing",
// href: $adminStore.accountPortalUrl + "/portal/billing",
// },
// ])
// }
// }
menu = menu.filter(item => !!item)
return menu
@ -216,11 +201,6 @@
>
Update user information
</MenuItem>
{#if $auth.isBuilder}
<MenuItem icon="Key" on:click={() => apiKeyModal.show()}>
View API key
</MenuItem>
{/if}
<MenuItem
icon="LockClosed"
on:click={() => changePasswordModal.show()}
@ -247,9 +227,6 @@
<Modal bind:this={changePasswordModal}>
<ChangePasswordModal />
</Modal>
<Modal bind:this={apiKeyModal}>
<UpdateAPIKeyModal />
</Modal>
{/if}
<style>

View File

@ -39,7 +39,7 @@
let automationErrors
let accessFilterList = null
$: welcomeHeader = `Welcome ${auth?.user?.firstName || "back"}`
$: welcomeHeader = `Welcome ${$auth?.user?.firstName || "back"}`
$: enrichedApps = enrichApps($apps, $auth.user, sortBy)
$: filteredApps = enrichedApps.filter(
app =>

View File

@ -1,20 +0,0 @@
<script>
import { Page } from "@budibase/bbui"
import { auth } from "stores/portal"
import { page, redirect } from "@roxi/routify"
// Only admins allowed here
$: {
if (!$auth.isAdmin) {
$redirect("../")
}
}
$: wide = $page.path.includes("email/:template")
</script>
{#if $auth.isAdmin}
<Page maxWidth="90ch" {wide}>
<slot />
</Page>
{/if}

View File

@ -8,6 +8,7 @@
Divider,
Modal,
Search,
Page,
} from "@budibase/bbui"
import { onMount } from "svelte"
import { plugins, admin } from "stores/portal"
@ -42,43 +43,42 @@
})
</script>
<Layout noPadding>
<Layout gap="XS" noPadding>
<Heading size="M">Plugins</Heading>
<Body>Add your own custom datasources and components.</Body>
</Layout>
<Divider size="S" />
<Page narrow>
<Layout noPadding>
<div class="controls">
<div>
<Button on:click={modal.show} newStyles cta icon={"Add"}>
Add plugin
</Button>
</div>
{#if $plugins?.length}
<div class="filters">
<div class="select">
<Select
bind:value={filter}
placeholder={null}
options={filterOptions}
autoWidth
quiet
/>
</div>
<Search bind:value={searchTerm} placeholder="Search plugins" />
<Layout gap="XS" noPadding>
<Heading size="M">Plugins</Heading>
<Body>Add your own custom datasources and components.</Body>
</Layout>
<Divider />
<Layout noPadding>
<div class="controls">
<div>
<Button on:click={modal.show} newStyles cta>Add plugin</Button>
</div>
{#if $plugins?.length}
<div class="filters">
<div class="select">
<Select
bind:value={filter}
placeholder={null}
options={filterOptions}
autoWidth
/>
</div>
<Search bind:value={searchTerm} placeholder="Search plugins" />
</div>
{/if}
</div>
{#if filteredPlugins?.length}
<Layout noPadding gap="S">
{#each filteredPlugins as plugin (plugin._id)}
<PluginRow {plugin} />
{/each}
</Layout>
{/if}
</div>
{#if filteredPlugins?.length}
<Layout noPadding gap="S">
{#each filteredPlugins as plugin (plugin._id)}
<PluginRow {plugin} />
{/each}
</Layout>
{/if}
</Layout>
</Layout>
</Layout>
</Page>
<Modal bind:this={modal}>
<AddPluginModal />

View File

@ -1,7 +1,60 @@
<script>
import { url, isActive } from "@roxi/routify"
import { Page } from "@budibase/bbui"
import { Content, SideNav, SideNavItem } from "components/portal/page"
</script>
<Page maxWidth="90ch">
<slot />
<Page narrow>
<Content>
<div slot="side-nav">
<SideNav title="Settings">
<SideNavItem
text="API Key"
url={$url("./api")}
active={$isActive("./api")}
/>
<SideNavItem
text="Auth"
url={$url("./auth")}
active={$isActive("./auth")}
/>
<SideNavItem
text="Email"
url={$url("./email")}
active={$isActive("./email")}
/>
<SideNavItem
text="Organisation"
url={$url("./organisation")}
active={$isActive("./organisation")}
/>
<SideNavItem
text="Version"
url={$url("./version")}
active={$isActive("./version")}
/>
<SideNavItem
text="Theme"
url={$url("./theme")}
active={$isActive("./theme")}
/>
<SideNavItem
text="Upgrade"
url={$url("./upgrade")}
active={$isActive("./upgrade")}
/>
<SideNavItem
text="Secrets"
url={$url("./secrets")}
active={$isActive("./secrets")}
/>
<SideNavItem
text="Scheduled Backups"
url={$url("./backups")}
active={$isActive("./backups")}
/>
</SideNav>
</div>
<slot />
</Content>
</Page>

View File

@ -0,0 +1,65 @@
<script>
import {
Layout,
Heading,
Body,
Divider,
Button,
Label,
notifications,
} from "@budibase/bbui"
import { auth } from "stores/portal"
import { onMount } from "svelte"
import CopyInput from "components/common/inputs/CopyInput.svelte"
let apiKey = null
async function generateAPIKey() {
try {
apiKey = await auth.generateAPIKey()
notifications.success("New API key generated")
} catch (err) {
notifications.error("Unable to generate new API key")
}
// need to return false to keep modal open
return false
}
onMount(async () => {
try {
apiKey = await auth.fetchAPIKey()
} catch (err) {
notifications.error("Unable to fetch API key")
}
})
</script>
<Layout noPadding>
<Layout gap="XS" noPadding>
<Heading size="M">API Key</Heading>
<Body>You API key to access the Budibase public API</Body>
</Layout>
<Divider />
<div class="fields">
<div class="field">
<Label size="L">API key</Label>
<CopyInput bind:value={apiKey} />
</div>
</div>
<div>
<Button newStyles secondary on:click={generateAPIKey}>Regenerate key</Button
>
</div>
</Layout>
<style>
.fields {
display: grid;
grid-gap: var(--spacing-m);
}
.field {
display: grid;
grid-template-columns: 120px 1fr;
align-items: center;
}
</style>

View File

@ -1,4 +1,4 @@
<script>
import { goto } from "@roxi/routify"
$goto("./organisation")
$goto("./api")
</script>

View File

@ -6,13 +6,13 @@
<Layout noPadding>
<Layout gap="XS" noPadding>
<Heading size="M">Theming</Heading>
<Heading size="M">Theme</Heading>
<Body>Customize how Budibase looks and feels.</Body>
</Layout>
<Divider />
<div class="fields">
<div class="field">
<Label size="L">Builder theme</Label>
<Label size="L">Theme</Label>
<Select
options={Constants.Themes}
bind:value={$themeStore.theme}

View File

@ -8,6 +8,7 @@
Detail,
Link,
TooltipWrapper,
Page,
} from "@budibase/bbui"
import { onMount } from "svelte"
import { admin, auth, licensing } from "../../../../stores/portal"
@ -176,32 +177,32 @@
</script>
{#if loaded}
<Layout noPadding>
<Layout noPadding gap="XS">
<Heading>Usage</Heading>
<Body>
Get information about your current usage within Budibase.
{#if accountPortalAccess}
To upgrade your plan and usage limits visit your <Link
on:click={goToAccountPortal}
size="L">Account</Link
>
{:else}
To upgrade your plan and usage limits contact your account holder.
{/if}
</Body>
</Layout>
<Divider />
<DashCard
description="YOUR CURRENT PLAN"
title={planTitle()}
{primaryActionText}
primaryAction={accountPortalAccess ? goToAccountPortal : undefined}
{textRows}
>
<Layout gap="S" noPadding>
<Layout gap="S">
<div class="usages">
<Page narrow>
<Layout noPadding>
<Layout noPadding gap="XS">
<Heading>Usage</Heading>
<Body>
<div>Get information about your current usage within Budibase.</div>
{#if accountPortalAccess}
<div>
To upgrade your plan and usage limits visit your <Link
on:click={goToAccountPortal}
size="L">Account</Link
>
</div>
{/if}
</Body>
</Layout>
<Divider />
<DashCard
description="YOUR CURRENT PLAN"
title={planTitle()}
{primaryActionText}
primaryAction={accountPortalAccess ? goToAccountPortal : undefined}
{textRows}
>
<div class="content">
<div class="column">
<Layout noPadding>
{#each staticUsage as usage}
<div class="usage">
@ -213,44 +214,51 @@
{/each}
</Layout>
</div>
</Layout>
{#if monthlyUsage.length}
<div class="monthly-container">
<Layout gap="S">
<Heading size="S" weight="light">Monthly</Heading>
<div class="detail">
<TooltipWrapper tooltip={new Date(quotaReset)}>
<Detail size="M">Resets in {daysRemainingInMonth} days</Detail
>
</TooltipWrapper>
</div>
<div class="usages">
<Layout noPadding>
{#if monthlyUsage.length}
<div class="column">
<Layout noPadding gap="M">
<Layout gap="XS" noPadding>
<Heading size="S">Monthly limits</Heading>
<div class="detail">
<TooltipWrapper tooltip={new Date(quotaReset)}>
<Detail size="M">
Resets in {daysRemainingInMonth} days
</Detail>
</TooltipWrapper>
</div>
</Layout>
<Layout noPadding gap="M">
{#each monthlyUsage as usage}
<div class="usage">
<Usage
{usage}
warnWhenFull={WARN_USAGE.includes(usage.name)}
/>
</div>
<Usage
{usage}
warnWhenFull={WARN_USAGE.includes(usage.name)}
/>
{/each}
</Layout>
</div>
</Layout>
</div>
{/if}
</Layout>
</DashCard>
</Layout>
</Layout>
</div>
{/if}
</div>
</DashCard>
</Layout>
</Page>
{/if}
<style>
.usages {
.content {
display: flex;
flex-direction: column;
flex-direction: row;
justify-content: flex-start;
align-items: flex-start;
gap: 40px;
}
.column {
flex: 1 1 0;
}
.detail :global(.spectrum-Detail) {
color: var(--spectrum-global-color-gray-700);
margin-bottom: 5px;
}
.detail :global(.icon) {
margin-bottom: 0;

View File

@ -104,7 +104,7 @@
</script>
{#if loaded}
<Layout noPadding gap="XL">
<Layout noPadding gap="L">
<Breadcrumbs>
<Breadcrumb url={$url("./")} text="Groups" />
<Breadcrumb text={group?.name} />

View File

@ -189,7 +189,7 @@
</script>
{#if loaded}
<Layout gap="XL" noPadding>
<Layout gap="L" noPadding>
<Breadcrumbs>
<Breadcrumb url={$url("./")} text="Users" />
<Breadcrumb text={user?.email} />