Merge pull request #2606 from Budibase/responsive-portal
Responsive portal
This commit is contained in:
commit
00b12463f6
|
@ -21,8 +21,5 @@
|
||||||
.wide {
|
.wide {
|
||||||
max-width: none;
|
max-width: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: var(--spacing-xl) calc(var(--spacing-xl) * 2)
|
|
||||||
calc(var(--spacing-xl) * 2) calc(var(--spacing-xl) * 2);
|
|
||||||
min-height: calc(100% - var(--spacing-xl) * 3);
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export let value = false
|
export let value = null
|
||||||
export let minValue = 0
|
export let minValue = 0
|
||||||
export let maxValue = 100
|
export let maxValue = 100
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
class="spectrum-SideNav-item"
|
class="spectrum-SideNav-item"
|
||||||
class:is-selected={selected}
|
class:is-selected={selected}
|
||||||
class:is-disabled={disabled}
|
class:is-disabled={disabled}
|
||||||
|
on:click
|
||||||
>
|
>
|
||||||
{#if heading}
|
{#if heading}
|
||||||
<h2 class="spectrum-SideNav-heading" id="nav-heading-{heading}">
|
<h2 class="spectrum-SideNav-heading" id="nav-heading-{heading}">
|
||||||
|
|
|
@ -34,7 +34,7 @@ Cypress.Commands.add("createApp", name => {
|
||||||
cy.get(".spectrum-Modal")
|
cy.get(".spectrum-Modal")
|
||||||
.within(() => {
|
.within(() => {
|
||||||
cy.get("input").eq(0).type(name).should("have.value", name).blur()
|
cy.get("input").eq(0).type(name).should("have.value", name).blur()
|
||||||
cy.contains("Create app").click()
|
cy.get(".spectrum-ButtonGroup").contains("Create app").click()
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
cy.expandBudibaseConnection()
|
cy.expandBudibaseConnection()
|
||||||
|
|
|
@ -8,9 +8,28 @@
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { admin } from "stores/portal"
|
import { admin } from "stores/portal"
|
||||||
import { goto } from "@roxi/routify"
|
import { goto } from "@roxi/routify"
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
|
||||||
|
let width = window.innerWidth
|
||||||
|
$: side = width < 500 ? "right" : "left"
|
||||||
|
|
||||||
|
const resizeObserver = new ResizeObserver(entries => {
|
||||||
|
if (entries?.[0]) {
|
||||||
|
width = entries[0].contentRect?.width
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
const doc = document.documentElement
|
||||||
|
resizeObserver.observe(doc)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
resizeObserver.unobserve(doc)
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ActionMenu>
|
<ActionMenu align={side}>
|
||||||
<div slot="control" class="icon">
|
<div slot="control" class="icon">
|
||||||
<ProgressCircle size="S" value={$admin.onboardingProgress} />
|
<ProgressCircle size="S" value={$admin.onboardingProgress} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -37,7 +56,7 @@
|
||||||
.item {
|
.item {
|
||||||
display: grid;
|
display: grid;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
grid-template-columns: 200px 20px;
|
grid-template-columns: 175px 20px;
|
||||||
}
|
}
|
||||||
.icon {
|
.icon {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
|
@ -104,8 +104,10 @@
|
||||||
|
|
||||||
// remove all iframe event listeners on component destroy
|
// remove all iframe event listeners on component destroy
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
iframe.contentWindow.removeEventListener("bb-event", handleBudibaseEvent)
|
if (iframe.contentWindow) {
|
||||||
iframe.contentWindow.removeEventListener("keydown", handleKeydownEvent)
|
iframe.contentWindow.removeEventListener("bb-event", handleBudibaseEvent)
|
||||||
|
iframe.contentWindow.removeEventListener("keydown", handleKeydownEvent)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleBudibaseEvent = event => {
|
const handleBudibaseEvent = event => {
|
||||||
|
@ -123,17 +125,21 @@
|
||||||
} else {
|
} else {
|
||||||
console.warning(`Client sent unknown event type: ${type}`)
|
console.warning(`Client sent unknown event type: ${type}`)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleKeydownEvent = event => {
|
const handleKeydownEvent = event => {
|
||||||
if ((event.key === "Delete" || event.key === "Backspace") &&
|
if (
|
||||||
|
(event.key === "Delete" || event.key === "Backspace") &&
|
||||||
selectedComponentId &&
|
selectedComponentId &&
|
||||||
['input', 'textarea'].indexOf(iframe.contentWindow.document.activeElement?.tagName.toLowerCase()) === -1) {
|
["input", "textarea"].indexOf(
|
||||||
confirmDeleteComponent(selectedComponentId);
|
iframe.contentWindow.document.activeElement?.tagName.toLowerCase()
|
||||||
|
) === -1
|
||||||
|
) {
|
||||||
|
confirmDeleteComponent(selectedComponentId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirmDeleteComponent = (componentId) => {
|
const confirmDeleteComponent = componentId => {
|
||||||
idToDelete = componentId
|
idToDelete = componentId
|
||||||
confirmDeleteDialog.show()
|
confirmDeleteDialog.show()
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
</Heading>
|
</Heading>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="desktop">
|
||||||
{#if app.updatedAt}
|
{#if app.updatedAt}
|
||||||
{processStringSync("Updated {{ duration time 'millisecond' }} ago", {
|
{processStringSync("Updated {{ duration time 'millisecond' }} ago", {
|
||||||
time: new Date().getTime() - new Date(app.updatedAt).getTime(),
|
time: new Date().getTime() - new Date(app.updatedAt).getTime(),
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
Never updated
|
Never updated
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="desktop">
|
||||||
<StatusLight
|
<StatusLight
|
||||||
positive={!app.lockedYou && !app.lockedOther}
|
positive={!app.lockedYou && !app.lockedOther}
|
||||||
notice={app.lockedYou}
|
notice={app.lockedYou}
|
||||||
|
@ -52,7 +52,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
</StatusLight>
|
</StatusLight>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="desktop">
|
||||||
<StatusLight active={app.deployed} neutral={!app.deployed}>
|
<StatusLight active={app.deployed} neutral={!app.deployed}>
|
||||||
{#if app.deployed}Published{:else}Unpublished{/if}
|
{#if app.deployed}Published{:else}Unpublished{/if}
|
||||||
</StatusLight>
|
</StatusLight>
|
||||||
|
@ -109,4 +109,10 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: color 130ms ease;
|
transition: color 130ms ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.desktop {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -134,7 +134,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ModalContent
|
<ModalContent
|
||||||
title={template ? "Import app" : "Create new app"}
|
title={template ? "Import app" : "Create app"}
|
||||||
confirmText={template ? "Import app" : "Create app"}
|
confirmText={template ? "Import app" : "Create app"}
|
||||||
onConfirm={createNewApp}
|
onConfirm={createNewApp}
|
||||||
disabled={!valid}
|
disabled={!valid}
|
||||||
|
|
|
@ -41,17 +41,8 @@
|
||||||
<Page>
|
<Page>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<Layout noPadding>
|
<Layout noPadding>
|
||||||
<img alt="logo" src={$organisation.logoUrl || Logo} />
|
<div class="header">
|
||||||
<div class="info-title">
|
<img alt="logo" src={$organisation.logoUrl || Logo} />
|
||||||
<Layout noPadding gap="XS">
|
|
||||||
<Heading size="L">
|
|
||||||
Hey {$auth.user.firstName || $auth.user.email}
|
|
||||||
</Heading>
|
|
||||||
<Body>
|
|
||||||
Welcome to the {$organisation.company} portal. Below you'll find
|
|
||||||
the list of apps that you have access to.
|
|
||||||
</Body>
|
|
||||||
</Layout>
|
|
||||||
<ActionMenu align="right">
|
<ActionMenu align="right">
|
||||||
<div slot="control" class="avatar">
|
<div slot="control" class="avatar">
|
||||||
<Avatar
|
<Avatar
|
||||||
|
@ -81,6 +72,15 @@
|
||||||
<MenuItem icon="LogOut" on:click={auth.logout}>Log out</MenuItem>
|
<MenuItem icon="LogOut" on:click={auth.logout}>Log out</MenuItem>
|
||||||
</ActionMenu>
|
</ActionMenu>
|
||||||
</div>
|
</div>
|
||||||
|
<Layout noPadding gap="XS">
|
||||||
|
<Heading size="M">
|
||||||
|
Hey {$auth.user.firstName || $auth.user.email}
|
||||||
|
</Heading>
|
||||||
|
<Body>
|
||||||
|
Welcome to the {$organisation.company} portal. Below you'll find the
|
||||||
|
list of apps that you have access to.
|
||||||
|
</Body>
|
||||||
|
</Layout>
|
||||||
<Divider />
|
<Divider />
|
||||||
{#if publishedApps.length}
|
{#if publishedApps.length}
|
||||||
<Heading>Apps</Heading>
|
<Heading>Apps</Heading>
|
||||||
|
@ -137,18 +137,18 @@
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
.content {
|
.content {
|
||||||
padding: 60px 0;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
img {
|
img {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
margin-bottom: -12px;
|
margin-bottom: -12px;
|
||||||
}
|
}
|
||||||
.info-title {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr auto;
|
|
||||||
grid-gap: var(--spacing-xl);
|
|
||||||
}
|
|
||||||
.avatar {
|
.avatar {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: auto auto;
|
grid-template-columns: auto auto;
|
||||||
|
@ -160,7 +160,6 @@
|
||||||
filter: brightness(110%);
|
filter: brightness(110%);
|
||||||
}
|
}
|
||||||
.group {
|
.group {
|
||||||
margin-top: var(--spacing-s);
|
|
||||||
}
|
}
|
||||||
.app {
|
.app {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
ActionMenu,
|
ActionMenu,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
Modal,
|
Modal,
|
||||||
|
clickOutside,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import ConfigChecklist from "components/common/ConfigChecklist.svelte"
|
import ConfigChecklist from "components/common/ConfigChecklist.svelte"
|
||||||
import { organisation, auth } from "stores/portal"
|
import { organisation, auth } from "stores/portal"
|
||||||
|
@ -21,6 +22,7 @@
|
||||||
let loaded = false
|
let loaded = false
|
||||||
let userInfoModal
|
let userInfoModal
|
||||||
let changePasswordModal
|
let changePasswordModal
|
||||||
|
let mobileMenuVisible = false
|
||||||
|
|
||||||
$: menu = buildMenu($auth.isAdmin)
|
$: menu = buildMenu($auth.isAdmin)
|
||||||
|
|
||||||
|
@ -71,6 +73,9 @@
|
||||||
return menu
|
return menu
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const showMobileMenu = () => (mobileMenuVisible = true)
|
||||||
|
const hideMobileMenu = () => (mobileMenuVisible = false)
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
// Prevent non-builders from accessing the portal
|
// Prevent non-builders from accessing the portal
|
||||||
if ($auth.user) {
|
if ($auth.user) {
|
||||||
|
@ -86,7 +91,11 @@
|
||||||
|
|
||||||
{#if $auth.user && loaded}
|
{#if $auth.user && loaded}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="nav">
|
<div
|
||||||
|
class="nav"
|
||||||
|
class:visible={mobileMenuVisible}
|
||||||
|
use:clickOutside={hideMobileMenu}
|
||||||
|
>
|
||||||
<Layout paddingX="L" paddingY="L">
|
<Layout paddingX="L" paddingY="L">
|
||||||
<div class="branding">
|
<div class="branding">
|
||||||
<div class="name" on:click={() => $goto("./apps")}>
|
<div class="name" on:click={() => $goto("./apps")}>
|
||||||
|
@ -100,7 +109,12 @@
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
<Navigation>
|
<Navigation>
|
||||||
{#each menu as { title, href, heading }}
|
{#each menu as { title, href, heading }}
|
||||||
<Item selected={$isActive(href)} {href} {heading}>{title}</Item>
|
<Item
|
||||||
|
on:click={hideMobileMenu}
|
||||||
|
selected={$isActive(href)}
|
||||||
|
{href}
|
||||||
|
{heading}>{title}</Item
|
||||||
|
>
|
||||||
{/each}
|
{/each}
|
||||||
</Navigation>
|
</Navigation>
|
||||||
</div>
|
</div>
|
||||||
|
@ -108,30 +122,40 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
<div />
|
<div class="mobile-toggle">
|
||||||
<ActionMenu align="right">
|
<Icon hoverable name="ShowMenu" on:click={showMobileMenu} />
|
||||||
<div slot="control" class="avatar">
|
</div>
|
||||||
<Avatar
|
<div class="mobile-logo">
|
||||||
size="M"
|
<img
|
||||||
initials={$auth.initials}
|
src={$organisation?.logoUrl || Logo}
|
||||||
url={$auth.user.pictureUrl}
|
alt={$organisation?.company || "Budibase"}
|
||||||
/>
|
/>
|
||||||
<Icon size="XL" name="ChevronDown" />
|
</div>
|
||||||
</div>
|
<div class="user-dropdown">
|
||||||
<MenuItem icon="UserEdit" on:click={() => userInfoModal.show()}>
|
<ActionMenu align="right">
|
||||||
Update user information
|
<div slot="control" class="avatar">
|
||||||
</MenuItem>
|
<Avatar
|
||||||
<MenuItem
|
size="M"
|
||||||
icon="LockClosed"
|
initials={$auth.initials}
|
||||||
on:click={() => changePasswordModal.show()}
|
url={$auth.user.pictureUrl}
|
||||||
>
|
/>
|
||||||
Update password
|
<Icon size="XL" name="ChevronDown" />
|
||||||
</MenuItem>
|
</div>
|
||||||
<MenuItem icon="UserDeveloper" on:click={() => $goto("../apps")}>
|
<MenuItem icon="UserEdit" on:click={() => userInfoModal.show()}>
|
||||||
Close developer mode
|
Update user information
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem icon="LogOut" on:click={auth.logout}>Log out</MenuItem>
|
<MenuItem
|
||||||
</ActionMenu>
|
icon="LockClosed"
|
||||||
|
on:click={() => changePasswordModal.show()}
|
||||||
|
>
|
||||||
|
Update password
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem icon="UserDeveloper" on:click={() => $goto("../apps")}>
|
||||||
|
Close developer mode
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem icon="LogOut" on:click={auth.logout}>Log out</MenuItem>
|
||||||
|
</ActionMenu>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<slot />
|
<slot />
|
||||||
|
@ -149,16 +173,20 @@
|
||||||
<style>
|
<style>
|
||||||
.container {
|
.container {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: grid;
|
display: flex;
|
||||||
grid-template-columns: 250px 1fr;
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
.nav {
|
.nav {
|
||||||
background: var(--background);
|
background: var(--background);
|
||||||
border-right: var(--border-light);
|
border-right: var(--border-light);
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: 250px;
|
||||||
}
|
}
|
||||||
.main {
|
.main {
|
||||||
|
flex: 1 1 auto;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: auto 1fr;
|
grid-template-rows: auto 1fr;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -192,11 +220,21 @@
|
||||||
.toolbar {
|
.toolbar {
|
||||||
background: var(--background);
|
background: var(--background);
|
||||||
border-bottom: var(--border-light);
|
border-bottom: var(--border-light);
|
||||||
display: grid;
|
display: flex;
|
||||||
grid-template-columns: 250px auto;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: var(--spacing-m) calc(var(--spacing-xl) * 2);
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
padding: var(--spacing-m) calc(var(--spacing-xl) * 2);
|
||||||
|
}
|
||||||
|
.mobile-toggle,
|
||||||
|
.mobile-logo {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.user-dropdown {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
img {
|
img {
|
||||||
width: 28px;
|
width: 28px;
|
||||||
|
@ -210,4 +248,43 @@
|
||||||
.content {
|
.content {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.toolbar {
|
||||||
|
background: var(--background);
|
||||||
|
border-bottom: var(--border-light);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: var(--spacing-m) calc(var(--spacing-xl) * 1.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav {
|
||||||
|
position: absolute;
|
||||||
|
left: -250px;
|
||||||
|
height: 100%;
|
||||||
|
transition: left ease-in-out 230ms;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
.nav.visible {
|
||||||
|
left: 0;
|
||||||
|
box-shadow: 0 0 80px 20px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-toggle,
|
||||||
|
.mobile-logo {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-toggle,
|
||||||
|
.user-dropdown {
|
||||||
|
flex: 1 1 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reduce BBUI page padding */
|
||||||
|
.content :global(> *) {
|
||||||
|
padding: calc(var(--spacing-xl) * 1.5) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -201,7 +201,7 @@
|
||||||
<Heading>Apps</Heading>
|
<Heading>Apps</Heading>
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
<Button secondary on:click={initiateAppImport}>Import app</Button>
|
<Button secondary on:click={initiateAppImport}>Import app</Button>
|
||||||
<Button cta on:click={initiateAppCreation}>Create new app</Button>
|
<Button cta on:click={initiateAppCreation}>Create app</Button>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</div>
|
</div>
|
||||||
<div class="filter">
|
<div class="filter">
|
||||||
|
@ -347,4 +347,10 @@
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.appTable {
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -272,7 +272,7 @@
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Layout>
|
<Layout noPadding>
|
||||||
<Layout gap="XS" noPadding>
|
<Layout gap="XS" noPadding>
|
||||||
<Heading size="M">Authentication</Heading>
|
<Heading size="M">Authentication</Heading>
|
||||||
<Body>
|
<Body>
|
||||||
|
@ -285,19 +285,17 @@
|
||||||
<Divider />
|
<Divider />
|
||||||
<Layout gap="XS" noPadding>
|
<Layout gap="XS" noPadding>
|
||||||
<Heading size="S">
|
<Heading size="S">
|
||||||
<div>
|
<div class="provider-title">
|
||||||
<GoogleLogo />
|
<GoogleLogo />
|
||||||
Google
|
<span>Google</span>
|
||||||
<div class="google-save-button">
|
<Button
|
||||||
<div>
|
disabled={googleSaveButtonDisabled}
|
||||||
<Button
|
size="s"
|
||||||
disabled={googleSaveButtonDisabled}
|
cta
|
||||||
size="s"
|
on:click={() => save([providers.google])}
|
||||||
cta
|
>
|
||||||
on:click={() => save([providers.google])}>Save</Button
|
Save
|
||||||
>
|
</Button>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Heading>
|
</Heading>
|
||||||
<Body size="S">
|
<Body size="S">
|
||||||
|
@ -317,12 +315,8 @@
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="field">
|
<Label size="L">Activated</Label>
|
||||||
<Label size="L">Activated</Label>
|
<Toggle text="" bind:value={providers.google.config.activated} />
|
||||||
<span class="alignedToggle">
|
|
||||||
<Toggle text="" bind:value={providers.google.config.activated} />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -330,21 +324,19 @@
|
||||||
<Divider />
|
<Divider />
|
||||||
<Layout gap="XS" noPadding>
|
<Layout gap="XS" noPadding>
|
||||||
<Heading size="S">
|
<Heading size="S">
|
||||||
<div>
|
<div class="provider-title">
|
||||||
<OidcLogo />
|
<OidcLogo />
|
||||||
OpenID Connect
|
<span>OpenID Connect</span>
|
||||||
<div class="oidc-save-button">
|
<Button
|
||||||
<div>
|
disabled={oidcSaveButtonDisabled}
|
||||||
<Button
|
size="s"
|
||||||
disabled={oidcSaveButtonDisabled}
|
cta
|
||||||
size="s"
|
on:click={() => save([providers.oidc])}
|
||||||
cta
|
>
|
||||||
on:click={() => save([providers.oidc])}>Save</Button
|
Save
|
||||||
>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Heading>
|
||||||
</div></Heading
|
|
||||||
>
|
|
||||||
<Body size="S">
|
<Body size="S">
|
||||||
To allow users to authenticate using OIDC, fill out the fields below.
|
To allow users to authenticate using OIDC, fill out the fields below.
|
||||||
</Body>
|
</Body>
|
||||||
|
@ -360,7 +352,8 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
<br />
|
</Layout>
|
||||||
|
<Layout gap="XS" noPadding>
|
||||||
<Body size="S">
|
<Body size="S">
|
||||||
To customize your login button, fill out the fields below.
|
To customize your login button, fill out the fields below.
|
||||||
</Body>
|
</Body>
|
||||||
|
@ -383,54 +376,37 @@
|
||||||
on:change={e => onFileSelected(e)}
|
on:change={e => onFileSelected(e)}
|
||||||
bind:this={fileinput}
|
bind:this={fileinput}
|
||||||
/>
|
/>
|
||||||
</Layout>
|
<div class="form-row">
|
||||||
<div class="form-row">
|
|
||||||
<div class="field">
|
|
||||||
<Label size="L">Activated</Label>
|
<Label size="L">Activated</Label>
|
||||||
<span class="alignedToggle">
|
<Toggle
|
||||||
<Toggle
|
text=""
|
||||||
text=""
|
bind:value={providers.oidc.config.configs[0].activated}
|
||||||
bind:value={providers.oidc.config.configs[0].activated}
|
/>
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Layout>
|
||||||
{/if}
|
{/if}
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.field {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alignedToggle {
|
|
||||||
margin-left: 63%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-row {
|
.form-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 20% 1fr;
|
grid-template-columns: 100px 1fr;
|
||||||
grid-gap: var(--spacing-l);
|
grid-gap: var(--spacing-l);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
span {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--spacing-s);
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
input[type="file"] {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.google-save-button {
|
.provider-title {
|
||||||
display: inline-block;
|
display: flex;
|
||||||
margin-left: 400px;
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-m);
|
||||||
}
|
}
|
||||||
|
.provider-title span {
|
||||||
.oidc-save-button {
|
flex: 1 1 auto;
|
||||||
display: inline-block;
|
|
||||||
margin-left: 320px;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -71,90 +71,109 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Layout gap="XS" noPadding>
|
<Layout noPadding>
|
||||||
<div class="back">
|
<Layout gap="XS" noPadding>
|
||||||
<ActionButton
|
<div>
|
||||||
on:click={() => $goto("./")}
|
<ActionButton
|
||||||
quiet
|
on:click={() => $goto("./")}
|
||||||
size="S"
|
quiet
|
||||||
icon="BackAndroid"
|
size="S"
|
||||||
>
|
icon="BackAndroid"
|
||||||
Back to email settings
|
>
|
||||||
</ActionButton>
|
Back to email settings
|
||||||
|
</ActionButton>
|
||||||
|
</div>
|
||||||
|
<header>
|
||||||
|
<Heading>
|
||||||
|
Email Template: {name}
|
||||||
|
</Heading>
|
||||||
|
<Button cta on:click={saveTemplate}>Save</Button>
|
||||||
|
</header>
|
||||||
|
<Body>
|
||||||
|
{description}
|
||||||
|
<br />
|
||||||
|
Change the email template here. Add dynamic content by using the bindings menu
|
||||||
|
on the right.
|
||||||
|
</Body>
|
||||||
|
</Layout>
|
||||||
|
<div>
|
||||||
|
<Tabs selected="Edit" on:select={fixMountBug}>
|
||||||
|
<Tab title="Edit">
|
||||||
|
<div class="template-editor">
|
||||||
|
<div class="template-text-editor">
|
||||||
|
<Editor
|
||||||
|
editorHeight={640}
|
||||||
|
bind:this={htmlEditor}
|
||||||
|
mode="handlebars"
|
||||||
|
on:change={e => {
|
||||||
|
selectedTemplate.contents = e.detail.value
|
||||||
|
}}
|
||||||
|
value={selectedTemplate?.contents}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="bindings-editor">
|
||||||
|
<Detail size="L">Bindings</Detail>
|
||||||
|
{#if mounted}
|
||||||
|
<Tabs selected="Template">
|
||||||
|
<Tab title="Template">
|
||||||
|
<TemplateBindings
|
||||||
|
title="Template Bindings"
|
||||||
|
bindings={templateBindings}
|
||||||
|
onBindingClick={setTemplateBinding}
|
||||||
|
/>
|
||||||
|
</Tab>
|
||||||
|
<Tab title="Common">
|
||||||
|
<TemplateBindings
|
||||||
|
title="Common Bindings"
|
||||||
|
bindings={$email?.definitions?.bindings?.common}
|
||||||
|
onBindingClick={setTemplateBinding}
|
||||||
|
/>
|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Tab>
|
||||||
|
<Tab title="Preview">
|
||||||
|
<div class="preview">
|
||||||
|
<iframe title="preview" srcdoc={previewContent} />
|
||||||
|
</div>
|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
<header>
|
|
||||||
<Heading>
|
|
||||||
Email Template: {name}
|
|
||||||
</Heading>
|
|
||||||
<Button cta on:click={saveTemplate}>Save</Button>
|
|
||||||
</header>
|
|
||||||
<Detail>Description</Detail>
|
|
||||||
<Body>{description}</Body>
|
|
||||||
<Body
|
|
||||||
>Change the email template here. Add dynamic content by using the bindings
|
|
||||||
menu on the right.</Body
|
|
||||||
>
|
|
||||||
</Layout>
|
</Layout>
|
||||||
<Tabs selected="Edit" on:select={fixMountBug}>
|
|
||||||
<Tab title="Edit">
|
|
||||||
<div class="template-editor">
|
|
||||||
<Editor
|
|
||||||
editorHeight={800}
|
|
||||||
bind:this={htmlEditor}
|
|
||||||
mode="handlebars"
|
|
||||||
on:change={e => {
|
|
||||||
selectedTemplate.contents = e.detail.value
|
|
||||||
}}
|
|
||||||
value={selectedTemplate?.contents}
|
|
||||||
/>
|
|
||||||
<div class="bindings-editor">
|
|
||||||
<Detail size="L">Bindings</Detail>
|
|
||||||
{#if mounted}
|
|
||||||
<Tabs selected="Template">
|
|
||||||
<Tab title="Template">
|
|
||||||
<TemplateBindings
|
|
||||||
title="Template Bindings"
|
|
||||||
bindings={templateBindings}
|
|
||||||
onBindingClick={setTemplateBinding}
|
|
||||||
/>
|
|
||||||
</Tab>
|
|
||||||
<Tab title="Common">
|
|
||||||
<TemplateBindings
|
|
||||||
title="Common Bindings"
|
|
||||||
bindings={$email?.definitions?.bindings?.common}
|
|
||||||
onBindingClick={setTemplateBinding}
|
|
||||||
/>
|
|
||||||
</Tab>
|
|
||||||
</Tabs>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Tab>
|
|
||||||
<Tab title="Preview">
|
|
||||||
<div class="preview">
|
|
||||||
<iframe title="preview" srcdoc={previewContent} />
|
|
||||||
</div>
|
|
||||||
</Tab>
|
|
||||||
</Tabs>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.template-editor {
|
.template-editor {
|
||||||
display: grid;
|
display: flex;
|
||||||
grid-template-columns: 1fr minmax(250px, 20%);
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: stretch;
|
||||||
|
flex-wrap: wrap;
|
||||||
grid-gap: var(--spacing-xl);
|
grid-gap: var(--spacing-xl);
|
||||||
margin-top: var(--spacing-xl);
|
margin-top: var(--spacing-xl);
|
||||||
}
|
}
|
||||||
|
.template-text-editor {
|
||||||
|
flex: 1 1 0;
|
||||||
|
min-width: 250px;
|
||||||
|
}
|
||||||
|
.bindings-editor {
|
||||||
|
margin-top: var(--spacing-s);
|
||||||
|
max-height: 640px;
|
||||||
|
overflow: auto;
|
||||||
|
flex: 0 0 300px;
|
||||||
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-top: var(--spacing-l);
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview {
|
.preview {
|
||||||
height: 800px;
|
height: 640px;
|
||||||
padding: var(--spacing-xl) 0;
|
margin-top: calc(var(--spacing-xl) + var(--spacing-s));
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
iframe {
|
iframe {
|
||||||
|
|
|
@ -107,7 +107,7 @@
|
||||||
fetchSmtp()
|
fetchSmtp()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Layout>
|
<Layout noPadding>
|
||||||
<Layout noPadding gap="XS">
|
<Layout noPadding gap="XS">
|
||||||
<Heading size="M">Email</Heading>
|
<Heading size="M">Email</Heading>
|
||||||
<Body>
|
<Body>
|
||||||
|
@ -186,7 +186,7 @@
|
||||||
<style>
|
<style>
|
||||||
.form-row {
|
.form-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 25% 1fr;
|
grid-template-columns: 120px 1fr;
|
||||||
grid-gap: var(--spacing-l);
|
grid-gap: var(--spacing-l);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,7 @@
|
||||||
|
|
||||||
<Layout noPadding>
|
<Layout noPadding>
|
||||||
<Layout gap="XS" noPadding>
|
<Layout gap="XS" noPadding>
|
||||||
<div class="back">
|
<div>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
on:click={() => $goto("./")}
|
on:click={() => $goto("./")}
|
||||||
quiet
|
quiet
|
||||||
|
|
|
@ -49,7 +49,7 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Layout>
|
<Layout noPadding>
|
||||||
<Layout gap="XS" noPadding>
|
<Layout gap="XS" noPadding>
|
||||||
<Heading>Users</Heading>
|
<Heading>Users</Heading>
|
||||||
<Body>
|
<Body>
|
||||||
|
|
|
@ -83,7 +83,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $auth.isAdmin}
|
{#if $auth.isAdmin}
|
||||||
<Layout>
|
<Layout noPadding>
|
||||||
<Layout gap="XS" noPadding>
|
<Layout gap="XS" noPadding>
|
||||||
<Heading size="M">Organisation</Heading>
|
<Heading size="M">Organisation</Heading>
|
||||||
<Body>
|
<Body>
|
||||||
|
@ -99,7 +99,7 @@
|
||||||
</Layout>
|
</Layout>
|
||||||
<div class="fields">
|
<div class="fields">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<Label size="L">Organization name</Label>
|
<Label size="L">Org. name</Label>
|
||||||
<Input thin bind:value={$values.company} />
|
<Input thin bind:value={$values.company} />
|
||||||
</div>
|
</div>
|
||||||
<div class="field logo">
|
<div class="field logo">
|
||||||
|
@ -138,12 +138,10 @@
|
||||||
please let us know below.
|
please let us know below.
|
||||||
</Body>
|
</Body>
|
||||||
</Layout>
|
</Layout>
|
||||||
<div class="fields">
|
<Toggle
|
||||||
<div class="field">
|
text="Send Analytics to Budibase"
|
||||||
<Label size="L">Send Analytics to Budibase</Label>
|
bind:value={$values.analytics}
|
||||||
<Toggle text="" bind:value={$values.analytics} />
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<Button disabled={loading} on:click={saveConfig} cta>Save</Button>
|
<Button disabled={loading} on:click={saveConfig} cta>Save</Button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -158,7 +156,7 @@
|
||||||
}
|
}
|
||||||
.field {
|
.field {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 33% 1fr;
|
grid-template-columns: 100px 1fr;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
.file {
|
.file {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
import { capitalise } from "helpers"
|
import { capitalise } from "helpers"
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Layout>
|
<Layout noPadding>
|
||||||
<Layout gap="XS" noPadding>
|
<Layout gap="XS" noPadding>
|
||||||
<Heading size="M">Theming</Heading>
|
<Heading size="M">Theming</Heading>
|
||||||
<Body>Customize how Budibase looks and feels.</Body>
|
<Body>Customize how Budibase looks and feels.</Body>
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
}
|
}
|
||||||
.field {
|
.field {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 33% 1fr;
|
grid-template-columns: 120px 1fr;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
Button,
|
Button,
|
||||||
Divider,
|
Divider,
|
||||||
notifications,
|
notifications,
|
||||||
|
Label,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import api from "builderStore/api"
|
import api from "builderStore/api"
|
||||||
import { auth } from "stores/portal"
|
import { auth } from "stores/portal"
|
||||||
|
@ -47,36 +48,25 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $auth.isAdmin}
|
{#if $auth.isAdmin}
|
||||||
<Layout>
|
<Layout noPadding>
|
||||||
<Layout gap="XS" noPadding>
|
<Layout gap="XS" noPadding>
|
||||||
<Heading size="M">Update</Heading>
|
<Heading size="M">Updates</Heading>
|
||||||
<Body>
|
<Body>
|
||||||
Keep your budibase installation up to date to take advantage of the
|
Keep your budibase installation up to date to take advantage of the
|
||||||
latest features, security updates and much more.
|
latest features, security updates and much more.
|
||||||
</Body>
|
</Body>
|
||||||
</Layout>
|
</Layout>
|
||||||
<Divider size="S" />
|
<Divider size="S" />
|
||||||
<div class="fields">
|
{#if version}
|
||||||
<div class="field">
|
<div>
|
||||||
{#if version}
|
<Label size="L">Current version</Label>
|
||||||
Current Version: {version}
|
<Heading size="XS">
|
||||||
{/if}
|
{version}
|
||||||
</div>
|
</Heading>
|
||||||
<div class="field">
|
|
||||||
<Button cta on:click={updateBudibase}>Check For Updates</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
|
<div>
|
||||||
|
<Button cta on:click={updateBudibase}>Check for updates</Button>
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
|
||||||
.fields {
|
|
||||||
display: grid;
|
|
||||||
grid-gap: var(--spacing-m);
|
|
||||||
}
|
|
||||||
.field {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 33% 1fr;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue