Merge pull request #1455 from Budibase/app-list
Portal as default home screen and app management
This commit is contained in:
commit
7481f129aa
|
@ -16,6 +16,11 @@ static_resources:
|
|||
- name: local_services
|
||||
domains: ["*"]
|
||||
routes:
|
||||
# special case to redirect specifically the route path
|
||||
# to the builder, if this were a prefix then it would break minio
|
||||
- match: { path: "/" }
|
||||
redirect: { path_redirect: "/builder/" }
|
||||
|
||||
- match: { prefix: "/db/" }
|
||||
route:
|
||||
cluster: couchdb-service
|
||||
|
@ -33,7 +38,11 @@ static_resources:
|
|||
route:
|
||||
cluster: server-dev
|
||||
|
||||
- match: { prefix: "/" }
|
||||
- match: { path: "/" }
|
||||
route:
|
||||
cluster: builder-dev
|
||||
|
||||
- match: { prefix: "/builder/" }
|
||||
route:
|
||||
cluster: builder-dev
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ static_resources:
|
|||
cluster: app-service
|
||||
prefix_rewrite: "/"
|
||||
|
||||
# special case for presenting our static self hosting page
|
||||
- match: { path: "/" }
|
||||
route:
|
||||
cluster: app-service
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
import Menu from "../Menu/Menu.svelte"
|
||||
|
||||
export let disabled = false
|
||||
export let align = "left"
|
||||
|
||||
let anchor
|
||||
let dropdown
|
||||
|
@ -31,7 +32,7 @@
|
|||
<div use:getAnchor on:click={openMenu}>
|
||||
<slot name="control" />
|
||||
</div>
|
||||
<Popover bind:this={dropdown} {anchor} align="left">
|
||||
<Popover bind:this={dropdown} {anchor} {align}>
|
||||
<Menu>
|
||||
<slot />
|
||||
</Menu>
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
export let getOptionValue = option => option
|
||||
export let open = false
|
||||
export let readonly = false
|
||||
export let quiet = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const onClick = e => {
|
||||
|
@ -33,6 +34,7 @@
|
|||
<button
|
||||
{id}
|
||||
class="spectrum-Picker spectrum-Picker--sizeM"
|
||||
class:spectrum-Picker--quiet={quiet}
|
||||
{disabled}
|
||||
class:is-invalid={!!error}
|
||||
class:is-open={open}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
export let getOptionLabel = option => option
|
||||
export let getOptionValue = option => option
|
||||
export let readonly = false
|
||||
export let quiet = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
let open = false
|
||||
|
@ -43,6 +44,7 @@
|
|||
<Picker
|
||||
on:click
|
||||
bind:open
|
||||
{quiet}
|
||||
{id}
|
||||
{error}
|
||||
{disabled}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
export let options = []
|
||||
export let getOptionLabel = option => extractProperty(option, "label")
|
||||
export let getOptionValue = option => extractProperty(option, "value")
|
||||
export let quiet = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const onChange = e => {
|
||||
|
@ -29,6 +30,7 @@
|
|||
|
||||
<Field {label} {labelPosition} {disabled} {error}>
|
||||
<Select
|
||||
{quiet}
|
||||
{error}
|
||||
{disabled}
|
||||
{readonly}
|
||||
|
|
|
@ -1,62 +1,16 @@
|
|||
<script>
|
||||
export let forAttr = "",
|
||||
extraSmall = false,
|
||||
small = false,
|
||||
medium = false,
|
||||
large = false,
|
||||
extraLarge = false,
|
||||
white = false,
|
||||
grey = false,
|
||||
black = false
|
||||
import "@spectrum-css/fieldlabel/dist/index-vars.css"
|
||||
|
||||
export let size = "M"
|
||||
</script>
|
||||
|
||||
<label
|
||||
class="bb-label"
|
||||
class:extraSmall
|
||||
class:small
|
||||
class:medium
|
||||
class:large
|
||||
class:extraLarge
|
||||
class:white
|
||||
class:grey
|
||||
class:black
|
||||
for={forAttr}
|
||||
>
|
||||
<label class={`spectrum-FieldLabel spectrum-FieldLabel--size${size}`}>
|
||||
<slot />
|
||||
</label>
|
||||
|
||||
<style>
|
||||
.bb-label {
|
||||
font-family: var(--font-sans);
|
||||
font-weight: 500;
|
||||
text-rendering: var(--text-render);
|
||||
color: var(--ink);
|
||||
font-size: var(--font-size-s);
|
||||
margin-bottom: var(--spacing-s);
|
||||
display: block;
|
||||
}
|
||||
.extraSmall {
|
||||
font-size: var(--font-size-xs);
|
||||
}
|
||||
.small {
|
||||
font-size: var(--font-size-s);
|
||||
}
|
||||
.medium {
|
||||
font-size: var(--font-size-m);
|
||||
}
|
||||
.large {
|
||||
font-size: var(--font-size-l);
|
||||
}
|
||||
.extraLarge {
|
||||
font-size: var(--font-size-xl);
|
||||
}
|
||||
.white {
|
||||
color: white;
|
||||
}
|
||||
.grey {
|
||||
color: var(--grey-6);
|
||||
}
|
||||
.black {
|
||||
color: var(--ink);
|
||||
label {
|
||||
padding: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -44,6 +44,9 @@
|
|||
padding-top: var(--spacing-l);
|
||||
padding-bottom: var(--spacing-l);
|
||||
}
|
||||
.gap-XS {
|
||||
grid-gap: var(--spacing-s);
|
||||
}
|
||||
.gap-S {
|
||||
grid-gap: var(--spectrum-alias-grid-gutter-xsmall);
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
>
|
||||
{#if icon}
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Menu-itemIcon"
|
||||
class="spectrum-Icon spectrum-Icon--sizeS spectrum-Menu-itemIcon"
|
||||
focusable="false"
|
||||
aria-hidden="true"
|
||||
aria-label={icon}
|
||||
|
@ -37,3 +37,9 @@
|
|||
{/if}
|
||||
<span class="spectrum-Menu-itemLabel"><slot /></span>
|
||||
</li>
|
||||
|
||||
<style>
|
||||
.spectrum-Menu-itemIcon {
|
||||
align-self: center;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -3,11 +3,20 @@
|
|||
|
||||
export let size = "M"
|
||||
export let serif = false
|
||||
export let noPadding = false
|
||||
</script>
|
||||
|
||||
<p
|
||||
class="spectrum-Body class:spectrum-Body--size{size}"
|
||||
class:noPadding
|
||||
class="spectrum-Body spectrum-Body--size{size}"
|
||||
class:spectrum-Body--serif={serif}
|
||||
>
|
||||
<slot />
|
||||
</p>
|
||||
|
||||
<style>
|
||||
.noPadding {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
export const gradient = (node, config = {}) => {
|
||||
const defaultConfig = {
|
||||
points: 10,
|
||||
saturation: 0.8,
|
||||
lightness: 0.75,
|
||||
softness: 0.8,
|
||||
}
|
||||
|
||||
// Applies a gradient background
|
||||
const createGradient = config => {
|
||||
config = {
|
||||
...defaultConfig,
|
||||
...config,
|
||||
}
|
||||
const { saturation, lightness, softness, points } = config
|
||||
|
||||
// Generates a random number between min and max
|
||||
const rand = (min, max) => {
|
||||
return Math.round(min + Math.random() * (max - min))
|
||||
}
|
||||
|
||||
// Generates a random HSL colour using the options specified
|
||||
const randomHSL = () => {
|
||||
const lowerSaturation = Math.min(100, saturation * 100)
|
||||
const upperSaturation = Math.min(100, (saturation + 0.2) * 100)
|
||||
const lowerLightness = Math.min(100, lightness * 100)
|
||||
const upperLightness = Math.min(100, (lightness + 0.2) * 100)
|
||||
const hue = rand(0, 360)
|
||||
const sat = `${rand(lowerSaturation, upperSaturation)}%`
|
||||
const light = `${rand(lowerLightness, upperLightness)}%`
|
||||
return `hsl(${hue},${sat},${light})`
|
||||
}
|
||||
|
||||
// Generates a radial gradient stop point
|
||||
const randomGradientPoint = () => {
|
||||
const lowerTransparency = Math.min(100, softness * 100)
|
||||
const upperTransparency = Math.min(100, (softness + 0.2) * 100)
|
||||
const transparency = rand(lowerTransparency, upperTransparency)
|
||||
return (
|
||||
`radial-gradient(` +
|
||||
`at ${rand(10, 90)}% ${rand(10, 90)}%,` +
|
||||
`${randomHSL()} 0,` +
|
||||
`transparent ${transparency}%)`
|
||||
)
|
||||
}
|
||||
|
||||
let css = `opacity:0.9;background-color:${randomHSL()};background-image:`
|
||||
for (let i = 0; i < points - 1; i++) {
|
||||
css += `${randomGradientPoint()},`
|
||||
}
|
||||
css += `${randomGradientPoint()};`
|
||||
node.style = css
|
||||
}
|
||||
|
||||
// Apply the initial gradient
|
||||
createGradient(config)
|
||||
|
||||
return {
|
||||
// Apply a new gradient
|
||||
update: config => {
|
||||
createGradient(config)
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { Input, Select, DatePicker, Toggle, TextArea } from "@budibase/bbui"
|
||||
import Dropzone from "components/common/Dropzone.svelte"
|
||||
import { capitalise } from "../../../helpers"
|
||||
import { capitalise } from "helpers"
|
||||
import LinkedRowSelector from "components/common/LinkedRowSelector.svelte"
|
||||
|
||||
export let defaultValue
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
|
||||
const selectRelationship = ({ tableId, rowId, fieldName }) => {
|
||||
$goto(
|
||||
`/builder/${$params.application}/data/table/${tableId}/relationship/${rowId}/${fieldName}`
|
||||
`/builder/app/${$params.application}/data/table/${tableId}/relationship/${rowId}/${fieldName}`
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
Body,
|
||||
ModalContent,
|
||||
} from "@budibase/bbui"
|
||||
import { capitalise } from "../../../../helpers"
|
||||
import { capitalise } from "helpers"
|
||||
|
||||
export let resourceId
|
||||
export let permissions
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { Label, Input, Layout } from "@budibase/bbui"
|
||||
import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte"
|
||||
import { capitalise } from "../../../../helpers"
|
||||
import { capitalise } from "helpers"
|
||||
|
||||
export let integration
|
||||
export let schema
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
getBindableProperties,
|
||||
readableToRuntimeBinding,
|
||||
} from "builderStore/dataBinding"
|
||||
import { currentAsset, store } from "../../../builderStore"
|
||||
import { currentAsset, store } from "builderStore"
|
||||
import { handlebarsCompletions } from "constants/completions"
|
||||
import { addToText } from "./utils"
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
import { createEventDispatcher } from "svelte"
|
||||
import { isValid } from "@budibase/string-templates"
|
||||
import { handlebarsCompletions } from "constants/completions"
|
||||
import { readableToRuntimeBinding } from "../../../builderStore/dataBinding"
|
||||
import { readableToRuntimeBinding } from "builderStore/dataBinding"
|
||||
import { addToText } from "./utils"
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import { store, currentAsset, selectedComponent } from "builderStore"
|
||||
import iframeTemplate from "./iframeTemplate"
|
||||
import { Screen } from "builderStore/store/screenTemplates/utils/Screen"
|
||||
import { FrontendTypes } from "../../../constants"
|
||||
import { FrontendTypes } from "constants"
|
||||
|
||||
let iframe
|
||||
let layout
|
||||
|
@ -82,7 +82,8 @@
|
|||
style="height: 100%; width: 100%"
|
||||
title="componentPreview"
|
||||
bind:this={iframe}
|
||||
srcdoc={template} />
|
||||
srcdoc={template}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
runtimeToReadableBinding,
|
||||
} from "builderStore/dataBinding"
|
||||
import BindingPanel from "components/common/bindings/BindingPanel.svelte"
|
||||
import { capitalise } from "../../../../helpers"
|
||||
import { capitalise } from "helpers"
|
||||
|
||||
export let label = ""
|
||||
export let bindable = true
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { goto } from "@roxi/routify"
|
||||
import {
|
||||
notifications,
|
||||
Button,
|
||||
Link,
|
||||
Input,
|
||||
Modal,
|
||||
|
@ -18,27 +18,18 @@
|
|||
username,
|
||||
password,
|
||||
})
|
||||
notifications.success("Logged in successfully.")
|
||||
notifications.success("Logged in successfully")
|
||||
$goto("../portal")
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
notifications.error("Invalid credentials")
|
||||
}
|
||||
}
|
||||
|
||||
async function createTestUser() {
|
||||
try {
|
||||
await auth.firstUser()
|
||||
notifications.success("Test user created")
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
notifications.error("Could not create test user")
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal fixed>
|
||||
<ModalContent
|
||||
size="L"
|
||||
size="M"
|
||||
title="Log In"
|
||||
onConfirm={login}
|
||||
confirmText="Log In"
|
||||
|
@ -51,7 +42,6 @@
|
|||
<Link target="_blank" href="/api/admin/auth/google">
|
||||
Sign In With Google
|
||||
</Link>
|
||||
<Button secondary on:click={createTestUser}>Create Test User</Button>
|
||||
</div>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
const id = $params.application
|
||||
await del(`/api/applications/${id}`)
|
||||
loading = false
|
||||
$goto("/builder/")
|
||||
$goto("/builder")
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,11 +1,19 @@
|
|||
<script>
|
||||
import { goto } from "@roxi/routify"
|
||||
import { ActionButton, Heading } from "@budibase/bbui"
|
||||
import { notifications } from "@budibase/bbui"
|
||||
import Spinner from "components/common/Spinner.svelte"
|
||||
import {
|
||||
Heading,
|
||||
Icon,
|
||||
Body,
|
||||
Layout,
|
||||
ActionMenu,
|
||||
MenuItem,
|
||||
Link,
|
||||
notifications,
|
||||
} from "@budibase/bbui"
|
||||
import download from "downloadjs"
|
||||
import { gradient } from "actions"
|
||||
|
||||
export let name, _id
|
||||
export let name
|
||||
export let _id
|
||||
|
||||
let appExportLoading = false
|
||||
|
||||
|
@ -15,58 +23,60 @@
|
|||
download(
|
||||
`/api/backups/export?appId=${_id}&appname=${encodeURIComponent(name)}`
|
||||
)
|
||||
notifications.success("App Export Complete.")
|
||||
notifications.success("App export complete")
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
notifications.error("App Export Failed.")
|
||||
notifications.error("App export failed")
|
||||
} finally {
|
||||
appExportLoading = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="apps-card">
|
||||
<Heading size="S">{name}</Heading>
|
||||
<div class="card-footer" data-cy={`app-${name}`}>
|
||||
<ActionButton on:click={() => $goto(`/builder/${_id}`)}>
|
||||
Open
|
||||
{name}
|
||||
→
|
||||
</ActionButton>
|
||||
{#if appExportLoading}
|
||||
<Spinner size="10" />
|
||||
{:else}
|
||||
<ActionButton icon="Download" quiet />
|
||||
<Layout noPadding gap="XS">
|
||||
<div class="preview" use:gradient />
|
||||
<div class="title">
|
||||
<Link href={`/builder/app/${_id}`}>
|
||||
<Heading size="XS">
|
||||
{name}
|
||||
</Heading>
|
||||
</Link>
|
||||
<ActionMenu>
|
||||
<Icon slot="control" name="More" hoverable />
|
||||
<MenuItem on:click={exportApp} icon="Download">Export</MenuItem>
|
||||
</ActionMenu>
|
||||
</div>
|
||||
<div class="status">
|
||||
<Body noPadding size="S">
|
||||
Edited {Math.floor(1 + Math.random() * 10)} months ago
|
||||
</Body>
|
||||
{#if Math.random() > 0.5}
|
||||
<Icon name="LockClosed" />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
.apps-card {
|
||||
background-color: var(--background);
|
||||
padding: var(--spacing-xl) var(--spacing-xl) var(--spacing-xl)
|
||||
var(--spacing-xl);
|
||||
max-width: 300px;
|
||||
max-height: 150px;
|
||||
border-radius: var(--border-radius-m);
|
||||
border: var(--border-dark);
|
||||
.preview {
|
||||
height: 135px;
|
||||
border-radius: var(--border-radius-s);
|
||||
margin-bottom: var(--spacing-s);
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
.title,
|
||||
.status {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: var(--spacing-m);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: var(--font-size-l);
|
||||
.title :global(a) {
|
||||
text-decoration: none;
|
||||
}
|
||||
.title :global(h1:hover) {
|
||||
color: var(--spectrum-global-color-blue-600);
|
||||
cursor: pointer;
|
||||
transition: 0.2s all;
|
||||
}
|
||||
|
||||
i:hover {
|
||||
color: var(--blue);
|
||||
transition: color 130ms ease;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,50 +1,25 @@
|
|||
<script>
|
||||
import { onMount } from "svelte"
|
||||
import AppCard from "./AppCard.svelte"
|
||||
import { Heading, Divider } from "@budibase/bbui"
|
||||
import Spinner from "components/common/Spinner.svelte"
|
||||
import { get } from "builderStore/api"
|
||||
import { apps } from "stores/portal"
|
||||
|
||||
let promise = getApps()
|
||||
|
||||
async function getApps() {
|
||||
const res = await get("/api/applications")
|
||||
const json = await res.json()
|
||||
|
||||
if (res.ok) {
|
||||
return json
|
||||
} else {
|
||||
throw new Error(json)
|
||||
}
|
||||
}
|
||||
onMount(apps.load)
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
<Heading size="M">Your Apps</Heading>
|
||||
<Divider size="M" />
|
||||
{#await promise}
|
||||
<div class="spinner-container">
|
||||
<Spinner size="30" />
|
||||
</div>
|
||||
{:then apps}
|
||||
<div class="apps">
|
||||
{#each apps as app}
|
||||
<AppCard {...app} />
|
||||
{/each}
|
||||
</div>
|
||||
{:catch err}
|
||||
<h1 style="color:red">{err}</h1>
|
||||
{/await}
|
||||
</div>
|
||||
{#if $apps.length}
|
||||
<div class="appList">
|
||||
{#each $apps as app}
|
||||
<AppCard {...app} />
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<div>No apps found.</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.root {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.apps {
|
||||
margin-top: var(--layout-m);
|
||||
.appList {
|
||||
display: grid;
|
||||
grid-gap: 50px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
grid-gap: var(--layout-s);
|
||||
justify-content: start;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
<script>
|
||||
import { Button, Modal } from "@budibase/bbui"
|
||||
import BuilderSettingsModal from "./BuilderSettingsModal.svelte"
|
||||
|
||||
let modal
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<Button primary quiet icon="Settings" text on:click={modal.show}>
|
||||
Settings
|
||||
</Button>
|
||||
</div>
|
||||
<Modal bind:this={modal} width="30%">
|
||||
<BuilderSettingsModal />
|
||||
</Modal>
|
|
@ -112,7 +112,7 @@
|
|||
}
|
||||
const userResp = await api.post(`/api/users/metadata/self`, user)
|
||||
await userResp.json()
|
||||
$goto(`./${appJson._id}`)
|
||||
window.location = `/builder/app/${appJson._id}`
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
notifications.error(error)
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
<script>
|
||||
import { Button } from "@budibase/bbui"
|
||||
import { auth } from "stores/backend"
|
||||
</script>
|
||||
|
||||
<Button primary quiet text icon="LogOut" on:click={auth.logout}>Log Out</Button>
|
|
@ -1,29 +0,0 @@
|
|||
<script>
|
||||
import { onMount } from "svelte"
|
||||
import { goto } from "@roxi/routify"
|
||||
import {
|
||||
SideNavigation as Navigation,
|
||||
SideNavigationItem as Item,
|
||||
} from "@budibase/bbui"
|
||||
import { admin } from "stores/portal"
|
||||
import LoginForm from "components/login/LoginForm.svelte"
|
||||
import BuilderSettingsButton from "components/start/BuilderSettingsButton.svelte"
|
||||
import LogoutButton from "components/start/LogoutButton.svelte"
|
||||
import Logo from "/assets/budibase-logo.svg"
|
||||
import api from "builderStore/api"
|
||||
|
||||
let checklist
|
||||
|
||||
onMount(async () => {
|
||||
await admin.init()
|
||||
if (!$admin?.checklist?.adminUser) {
|
||||
$goto("./admin")
|
||||
} else {
|
||||
$goto("./portal")
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if $admin.checklist}
|
||||
<slot />
|
||||
{/if}
|
|
@ -1,69 +0,0 @@
|
|||
<script>
|
||||
import {
|
||||
Button,
|
||||
Heading,
|
||||
Label,
|
||||
notifications,
|
||||
Layout,
|
||||
Input,
|
||||
Body,
|
||||
} from "@budibase/bbui"
|
||||
import { goto } from "@roxi/routify"
|
||||
import { onMount } from "svelte"
|
||||
import api from "builderStore/api"
|
||||
|
||||
let adminUser = {}
|
||||
|
||||
async function save() {
|
||||
try {
|
||||
// Save the admin user
|
||||
const response = await api.post(`/api/admin/users/init`, adminUser)
|
||||
|
||||
const json = await response.json()
|
||||
if (response.status !== 200) throw new Error(json.message)
|
||||
notifications.success(`Admin user created.`)
|
||||
$goto("../portal")
|
||||
} catch (err) {
|
||||
notifications.error(`Failed to create admin user.`)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
<header>
|
||||
<Heading size="M">Create an admin user</Heading>
|
||||
<Body size="S">The admin user has access to everything in budibase.</Body>
|
||||
</header>
|
||||
<div class="config-form">
|
||||
<Layout gap="S">
|
||||
<Input label="email" bind:value={adminUser.email} />
|
||||
<Input
|
||||
label="password"
|
||||
type="password"
|
||||
bind:value={adminUser.password}
|
||||
/>
|
||||
<Button cta on:click={save}>Create super admin user</Button>
|
||||
</Layout>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
header {
|
||||
text-align: center;
|
||||
width: 80%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.config-form {
|
||||
margin-bottom: 42px;
|
||||
}
|
||||
</style>
|
|
@ -1,116 +1,33 @@
|
|||
<script>
|
||||
import {
|
||||
SideNavigation as Navigation,
|
||||
SideNavigationItem as Item,
|
||||
} from "@budibase/bbui"
|
||||
import { onMount } from "svelte"
|
||||
import { goto } from "@roxi/routify"
|
||||
import { auth } from "stores/backend"
|
||||
import LoginForm from "components/login/LoginForm.svelte"
|
||||
import BuilderSettingsButton from "components/start/BuilderSettingsButton.svelte"
|
||||
import LogoutButton from "components/start/LogoutButton.svelte"
|
||||
import Logo from "/assets/budibase-logo.svg"
|
||||
import { admin } from "stores/portal"
|
||||
|
||||
let modal
|
||||
let loaded = false
|
||||
$: hasAdminUser = !!$admin?.checklist?.adminUser
|
||||
|
||||
onMount(async () => {
|
||||
await admin.init()
|
||||
await auth.checkAuth()
|
||||
loaded = true
|
||||
})
|
||||
|
||||
// Force creation of an admin user if one doesn't exist
|
||||
$: {
|
||||
if (loaded && !hasAdminUser) {
|
||||
$goto("./admin")
|
||||
}
|
||||
}
|
||||
|
||||
// Redirect to log in at any time if the user isn't authenticated
|
||||
$: {
|
||||
if (loaded && hasAdminUser && !$auth.user) {
|
||||
$goto("./auth/login")
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if $auth}
|
||||
{#if $auth.user}
|
||||
<div class="root">
|
||||
<div class="ui-nav">
|
||||
<div class="home-logo">
|
||||
<img src={Logo} alt="Budibase icon" />
|
||||
</div>
|
||||
<div class="nav-section">
|
||||
<div class="nav-top">
|
||||
<Navigation>
|
||||
<Item href="/builder/" icon="Apps" selected>Apps</Item>
|
||||
<Item external href="https://portal.budi.live/" icon="Servers">
|
||||
Hosting
|
||||
</Item>
|
||||
<Item external href="https://docs.budibase.com/" icon="Book">
|
||||
Documentation
|
||||
</Item>
|
||||
<Item
|
||||
external
|
||||
href="https://github.com/Budibase/budibase/discussions"
|
||||
icon="PeopleGroup"
|
||||
>
|
||||
Community
|
||||
</Item>
|
||||
<Item
|
||||
external
|
||||
href="https://github.com/Budibase/budibase/issues/new/choose"
|
||||
icon="Bug"
|
||||
>
|
||||
Raise an issue
|
||||
</Item>
|
||||
</Navigation>
|
||||
</div>
|
||||
<div class="nav-bottom">
|
||||
<BuilderSettingsButton />
|
||||
<LogoutButton />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<section class="login">
|
||||
<LoginForm />
|
||||
</section>
|
||||
{/if}
|
||||
{#if loaded}
|
||||
<slot />
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.root {
|
||||
display: grid;
|
||||
grid-template-columns: 260px 1fr;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.login {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.main {
|
||||
grid-column: 2;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.ui-nav {
|
||||
grid-column: 1;
|
||||
background-color: var(--background);
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-right: var(--border-light);
|
||||
}
|
||||
|
||||
.home-logo {
|
||||
cursor: pointer;
|
||||
height: 40px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.home-logo img {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.nav-section {
|
||||
margin: 20px 0 0 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.nav-bottom :global(> *) {
|
||||
margin-top: 5px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
<script>
|
||||
import {
|
||||
Button,
|
||||
Heading,
|
||||
notifications,
|
||||
Layout,
|
||||
Input,
|
||||
Body,
|
||||
} from "@budibase/bbui"
|
||||
import { goto } from "@roxi/routify"
|
||||
import api from "builderStore/api"
|
||||
import { admin } from "stores/portal"
|
||||
|
||||
let adminUser = {}
|
||||
|
||||
async function save() {
|
||||
try {
|
||||
// Save the admin user
|
||||
const response = await api.post(`/api/admin/users/init`, adminUser)
|
||||
const json = await response.json()
|
||||
if (response.status !== 200) {
|
||||
throw new Error(json.message)
|
||||
}
|
||||
notifications.success(`Admin user created`)
|
||||
await admin.init()
|
||||
$goto("../portal")
|
||||
} catch (err) {
|
||||
notifications.error(`Failed to create admin user`)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
<Layout gap="XS">
|
||||
<img src="https://i.imgur.com/ZKyklgF.png" />
|
||||
</Layout>
|
||||
<div class="center">
|
||||
<Layout gap="XS">
|
||||
<Heading size="M">Create an admin user</Heading>
|
||||
<Body size="M"
|
||||
>The admin user has access to everything in Budibase.</Body
|
||||
>
|
||||
</Layout>
|
||||
</div>
|
||||
<Layout gap="XS">
|
||||
<Input label="Email" bind:value={adminUser.email} />
|
||||
<Input label="Password" type="password" bind:value={adminUser.password} />
|
||||
</Layout>
|
||||
<Layout gap="S">
|
||||
<Button cta on:click={save}>Create super admin user</Button>
|
||||
</Layout>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
.container {
|
||||
margin: 0 auto;
|
||||
width: 260px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: stretch;
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
img {
|
||||
width: 40px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
|
@ -6,9 +6,9 @@
|
|||
import ThemeEditorDropdown from "components/settings/ThemeEditorDropdown.svelte"
|
||||
import FeedbackNavLink from "components/feedback/FeedbackNavLink.svelte"
|
||||
import { get } from "builderStore/api"
|
||||
import { isActive, goto, layout, params } from "@roxi/routify"
|
||||
import { isActive, goto, layout } from "@roxi/routify"
|
||||
import Logo from "/assets/bb-logo.svg"
|
||||
import { capitalise } from "../../../helpers"
|
||||
import { capitalise } from "helpers"
|
||||
|
||||
// Get Package and set store
|
||||
export let application
|
||||
|
@ -60,7 +60,7 @@
|
|||
<img
|
||||
src={Logo}
|
||||
alt="budibase icon"
|
||||
on:click={() => $goto(`/builder/`)}
|
||||
on:click={() => $goto(`../../portal/`)}
|
||||
/>
|
||||
</button>
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
import { notifications } from "@budibase/bbui"
|
||||
import IntegrationConfigForm from "components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte"
|
||||
import ICONS from "components/backend/DatasourceNavigator/icons"
|
||||
import { capitalise } from "../../../../../../helpers"
|
||||
import { capitalise } from "helpers"
|
||||
|
||||
let unsaved = false
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<script>
|
||||
import { goto } from "@roxi/routify"
|
||||
$goto("../portal")
|
||||
</script>
|
|
@ -0,0 +1,4 @@
|
|||
<script>
|
||||
import { goto } from "@roxi/routify"
|
||||
$goto("./login")
|
||||
</script>
|
|
@ -0,0 +1,5 @@
|
|||
<script>
|
||||
import LoginForm from "components/login/LoginForm.svelte"
|
||||
</script>
|
||||
|
||||
<LoginForm />
|
|
@ -1,123 +1,4 @@
|
|||
<script>
|
||||
import api from "builderStore/api"
|
||||
import AppList from "components/start/AppList.svelte"
|
||||
import { get } from "builderStore/api"
|
||||
import CreateAppModal from "components/start/CreateAppModal.svelte"
|
||||
import { Button, Heading, Modal, ButtonGroup } from "@budibase/bbui"
|
||||
import TemplateList from "components/start/TemplateList.svelte"
|
||||
import analytics from "analytics"
|
||||
import Banner from "/assets/orange-landscape.png"
|
||||
|
||||
let hasKey
|
||||
let template
|
||||
let modal
|
||||
|
||||
async function getApps() {
|
||||
const res = await get("/api/applications")
|
||||
const json = await res.json()
|
||||
|
||||
if (res.ok) {
|
||||
return json
|
||||
} else {
|
||||
throw new Error(json)
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchKeys() {
|
||||
const response = await api.get(`/api/keys/`)
|
||||
return await response.json()
|
||||
}
|
||||
|
||||
async function checkIfKeysAndApps() {
|
||||
const keys = await fetchKeys()
|
||||
const apps = await getApps()
|
||||
if (keys.userId) {
|
||||
hasKey = true
|
||||
analytics.identify(keys.userId)
|
||||
}
|
||||
}
|
||||
|
||||
function selectTemplate(newTemplate) {
|
||||
template = newTemplate
|
||||
modal.show()
|
||||
}
|
||||
|
||||
function initiateAppImport() {
|
||||
template = { fromFile: true }
|
||||
modal.show()
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
template = null
|
||||
modal.hide()
|
||||
}
|
||||
|
||||
checkIfKeysAndApps()
|
||||
import { goto } from "@roxi/routify"
|
||||
$goto("./portal")
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<Heading size="M">Welcome to the Budibase Beta</Heading>
|
||||
<ButtonGroup>
|
||||
<Button secondary on:click={initiateAppImport}>Import Web App</Button>
|
||||
<Button cta on:click={modal.show}>Create New Web App</Button>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
|
||||
<div class="banner">
|
||||
<img src={Banner} alt="rocket" />
|
||||
<div class="banner-content">
|
||||
Every accomplishment starts with a decision to try.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <TemplateList onSelect={selectTemplate} /> -->
|
||||
|
||||
<AppList />
|
||||
</div>
|
||||
|
||||
<Modal bind:this={modal} padding={false} width="600px" on:hide={closeModal}>
|
||||
<CreateAppModal {hasKey} {template} />
|
||||
</Modal>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
display: grid;
|
||||
gap: var(--spacing-xl);
|
||||
margin: 40px 80px;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
color: white;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.banner img {
|
||||
height: 250px;
|
||||
width: 100%;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.banner-content {
|
||||
position: absolute;
|
||||
font-size: 24px;
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,49 +1,53 @@
|
|||
<script>
|
||||
import { isActive, url, goto } from "@roxi/routify"
|
||||
import { isActive, goto } from "@roxi/routify"
|
||||
import { onMount } from "svelte"
|
||||
import {
|
||||
ActionMenu,
|
||||
Checkbox,
|
||||
MenuItem,
|
||||
Icon,
|
||||
Heading,
|
||||
Avatar,
|
||||
Search,
|
||||
Layout,
|
||||
ProgressCircle,
|
||||
SideNavigation as Navigation,
|
||||
SideNavigationItem as Item,
|
||||
ActionMenu,
|
||||
MenuItem,
|
||||
Modal,
|
||||
} from "@budibase/bbui"
|
||||
import api from "builderStore/api"
|
||||
import ConfigChecklist from "components/common/ConfigChecklist.svelte"
|
||||
import { organisation, admin } from "stores/portal"
|
||||
import { organisation, apps } from "stores/portal"
|
||||
import { auth } from "stores/backend"
|
||||
import BuilderSettingsModal from "components/start/BuilderSettingsModal.svelte"
|
||||
|
||||
organisation.init()
|
||||
apps.load()
|
||||
|
||||
let orgName
|
||||
let orgLogo
|
||||
let user
|
||||
let oldSettingsModal
|
||||
|
||||
async function getInfo() {
|
||||
// fetch orgInfo
|
||||
orgName = "ACME Inc."
|
||||
orgLogo = "https://via.placeholder.com/150"
|
||||
|
||||
user = { name: "John Doe" }
|
||||
}
|
||||
|
||||
onMount(getInfo)
|
||||
|
||||
let menu = [
|
||||
{ title: "Apps", href: "/portal/apps" },
|
||||
{ title: "Drafts", href: "/portal/drafts" },
|
||||
{ title: "Users", href: "/portal/users", heading: "Manage" },
|
||||
{ title: "Groups", href: "/portal/groups" },
|
||||
{ title: "Auth", href: "/portal/oauth" },
|
||||
{ title: "Email", href: "/portal/email" },
|
||||
{ title: "General", href: "/portal/settings/general", heading: "Settings" },
|
||||
{ title: "Theming", href: "/portal/theming" },
|
||||
{ title: "Account", href: "/portal/account" },
|
||||
{ title: "Apps", href: "/builder/portal/apps" },
|
||||
{ title: "Drafts", href: "/builder/portal/drafts" },
|
||||
{ title: "Users", href: "/builder/portal/users", heading: "Manage" },
|
||||
{ title: "Groups", href: "/builder/portal/groups" },
|
||||
{ title: "Auth", href: "/builder/portal/oauth" },
|
||||
{ title: "Email", href: "/builder/portal/email" },
|
||||
{
|
||||
title: "General",
|
||||
href: "/builder/portal/settings/general",
|
||||
heading: "Settings",
|
||||
},
|
||||
{ title: "Theming", href: "/builder/portal/theming" },
|
||||
{ title: "Account", href: "/builder/portal/account" },
|
||||
]
|
||||
</script>
|
||||
|
||||
|
@ -51,7 +55,7 @@
|
|||
<div class="nav">
|
||||
<Layout paddingX="L" paddingY="L">
|
||||
<div class="branding">
|
||||
<div class="name">
|
||||
<div class="name" on:click={() => $goto("./apps")}>
|
||||
<img
|
||||
src={$organisation?.logoUrl || "https://i.imgur.com/ZKyklgF.png"}
|
||||
alt="Logotype"
|
||||
|
@ -74,30 +78,42 @@
|
|||
<div class="main">
|
||||
<div class="toolbar">
|
||||
<Search placeholder="Global search" />
|
||||
<div class="avatar">
|
||||
<Avatar size="M" name="John Doe" />
|
||||
<Icon size="XL" name="ChevronDown" />
|
||||
</div>
|
||||
<ActionMenu align="right">
|
||||
<div slot="control" class="avatar">
|
||||
<Avatar size="M" name="John Doe" />
|
||||
<Icon size="XL" name="ChevronDown" />
|
||||
</div>
|
||||
<MenuItem icon="Settings" on:click={oldSettingsModal.show}>
|
||||
Old settings
|
||||
</MenuItem>
|
||||
<MenuItem icon="LogOut" on:click={auth.logout}>Log out</MenuItem>
|
||||
</ActionMenu>
|
||||
</div>
|
||||
<div>
|
||||
<div class="content">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Modal bind:this={oldSettingsModal} width="30%">
|
||||
<BuilderSettingsModal />
|
||||
</Modal>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
height: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: 250px 1fr;
|
||||
align-items: stretch;
|
||||
}
|
||||
.nav {
|
||||
background: var(--background);
|
||||
border-right: var(--border-light);
|
||||
overflow: auto;
|
||||
}
|
||||
.main {
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
overflow: hidden;
|
||||
}
|
||||
.branding {
|
||||
display: grid;
|
||||
|
@ -112,6 +128,9 @@
|
|||
grid-gap: var(--spacing-m);
|
||||
align-items: center;
|
||||
}
|
||||
.name:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
.avatar {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto;
|
||||
|
@ -129,6 +148,7 @@
|
|||
grid-template-columns: 250px auto;
|
||||
justify-content: space-between;
|
||||
padding: var(--spacing-m) calc(var(--spacing-xl) * 2);
|
||||
align-items: center;
|
||||
}
|
||||
img {
|
||||
width: 28px;
|
||||
|
@ -139,4 +159,7 @@
|
|||
text-overflow: ellipsis;
|
||||
font-weight: 500;
|
||||
}
|
||||
.content {
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,92 @@
|
|||
<script>
|
||||
import {
|
||||
Heading,
|
||||
Layout,
|
||||
Button,
|
||||
ActionButton,
|
||||
ActionGroup,
|
||||
ButtonGroup,
|
||||
Select,
|
||||
Modal,
|
||||
} from "@budibase/bbui"
|
||||
import AppList from "components/start/AppList.svelte"
|
||||
import CreateAppModal from "components/start/CreateAppModal.svelte"
|
||||
import api from "builderStore/api"
|
||||
import analytics from "analytics"
|
||||
import { onMount } from "svelte"
|
||||
|
||||
let layout = "grid"
|
||||
let modal
|
||||
let template
|
||||
|
||||
async function checkKeys() {
|
||||
const response = await api.get(`/api/keys/`)
|
||||
const keys = await response.json()
|
||||
if (keys.userId) {
|
||||
analytics.identify(keys.userId)
|
||||
}
|
||||
}
|
||||
|
||||
function initiateAppImport() {
|
||||
template = { fromFile: true }
|
||||
modal.show()
|
||||
}
|
||||
|
||||
onMount(checkKeys)
|
||||
</script>
|
||||
|
||||
<Layout noPadding>
|
||||
<div class="title">
|
||||
<Heading>Apps</Heading>
|
||||
<ButtonGroup>
|
||||
<Button secondary on:click={initiateAppImport}>Import app</Button>
|
||||
<Button cta on:click={modal.show}>Create new app</Button>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
<div class="filter">
|
||||
<div class="select">
|
||||
<Select quiet placeholder="Filter by groups" />
|
||||
</div>
|
||||
<ActionGroup>
|
||||
<ActionButton
|
||||
on:click={() => (layout = "grid")}
|
||||
selected={layout === "grid"}
|
||||
quiet
|
||||
icon="ClassicGridView"
|
||||
/>
|
||||
<ActionButton
|
||||
on:click={() => (layout = "table")}
|
||||
selected={layout === "table"}
|
||||
quiet
|
||||
icon="ViewRow"
|
||||
/>
|
||||
</ActionGroup>
|
||||
</div>
|
||||
{#if layout === "grid"}
|
||||
<AppList />
|
||||
{:else}
|
||||
Table view.
|
||||
{/if}
|
||||
</Layout>
|
||||
<Modal
|
||||
bind:this={modal}
|
||||
padding={false}
|
||||
width="600px"
|
||||
on:hide={() => (template = null)}
|
||||
>
|
||||
<CreateAppModal {template} />
|
||||
</Modal>
|
||||
|
||||
<style>
|
||||
.title,
|
||||
.filter {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.select {
|
||||
width: 110px;
|
||||
}
|
||||
</style>
|
|
@ -60,47 +60,50 @@
|
|||
</script>
|
||||
|
||||
<Page>
|
||||
<header>
|
||||
<Heading size="M">OAuth</Heading>
|
||||
<Body size="S">
|
||||
Every budibase app comes with basic authentication (email/password)
|
||||
included. You can add additional authentication methods from the options
|
||||
below.
|
||||
</Body>
|
||||
</header>
|
||||
<Divider />
|
||||
{#if google}
|
||||
<div class="config-form">
|
||||
<Layout gap="S">
|
||||
<Layout noPadding>
|
||||
<div>
|
||||
<Heading size="M">OAuth</Heading>
|
||||
<Body>
|
||||
Every budibase app comes with basic authentication (email/password)
|
||||
included. You can add additional authentication methods from the options
|
||||
below.
|
||||
</Body>
|
||||
</div>
|
||||
<Divider />
|
||||
{#if google}
|
||||
<div>
|
||||
<Heading size="S">
|
||||
<span>
|
||||
<GoogleLogo />
|
||||
Google
|
||||
</span>
|
||||
</Heading>
|
||||
{#each ConfigFields.Google as field}
|
||||
<div class="form-row">
|
||||
<Label>{field}</Label>
|
||||
<Input bind:value={google.config[field]} />
|
||||
</div>
|
||||
{/each}
|
||||
</Layout>
|
||||
<Button primary on:click={() => save(google)}>Save</Button>
|
||||
</div>
|
||||
<Divider />
|
||||
{/if}
|
||||
<Body>
|
||||
To allow users to authenticate using their Google accounts, fill out
|
||||
the fields below.
|
||||
</Body>
|
||||
</div>
|
||||
|
||||
{#each ConfigFields.Google as field}
|
||||
<div class="form-row">
|
||||
<Label size="L">{field}</Label>
|
||||
<Input bind:value={google.config[field]} />
|
||||
</div>
|
||||
{/each}
|
||||
<div>
|
||||
<Button primary on:click={() => save(google)}>Save</Button>
|
||||
</div>
|
||||
<Divider />
|
||||
{/if}
|
||||
</Layout>
|
||||
</Page>
|
||||
|
||||
<style>
|
||||
.config-form {
|
||||
margin-top: 42px;
|
||||
margin-bottom: 42px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: 20% 1fr;
|
||||
grid-gap: var(--spacing-l);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
span {
|
||||
|
@ -108,8 +111,4 @@
|
|||
align-items: center;
|
||||
gap: var(--spacing-s);
|
||||
}
|
||||
|
||||
header {
|
||||
margin-bottom: 42px;
|
||||
}
|
||||
</style>
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
@ -45,12 +45,12 @@
|
|||
<Layout noPadding>
|
||||
<div class="intro">
|
||||
<Heading size="M">General</Heading>
|
||||
<Body
|
||||
>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Hic vero, aut
|
||||
<Body>
|
||||
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Hic vero, aut
|
||||
culpa provident sunt ratione! Voluptas doloremque, dicta nisi velit
|
||||
perspiciatis, ratione vel blanditiis totam, nam voluptate repellat
|
||||
aperiam fuga!</Body
|
||||
>
|
||||
aperiam fuga!
|
||||
</Body>
|
||||
</div>
|
||||
<Divider size="S" />
|
||||
<div class="information">
|
||||
|
@ -58,7 +58,7 @@
|
|||
<Body>Here you can update your logo and organization name.</Body>
|
||||
<div class="fields">
|
||||
<div class="field">
|
||||
<Label>Organization name</Label>
|
||||
<Label size="L">Organization name</Label>
|
||||
<Input thin bind:value={company} />
|
||||
</div>
|
||||
<!-- <div class="field">
|
||||
|
@ -72,13 +72,13 @@
|
|||
<Divider size="S" />
|
||||
<div class="analytics">
|
||||
<Heading size="S">Analytics</Heading>
|
||||
<Body
|
||||
>If you would like to send analytics that help us make Budibase better,
|
||||
please let us know below.</Body
|
||||
>
|
||||
<Body>
|
||||
If you would like to send analytics that help us make Budibase better,
|
||||
please let us know below.
|
||||
</Body>
|
||||
<div class="fields">
|
||||
<div class="field">
|
||||
<Label>Send Analytics to Budibase</Label>
|
||||
<Label size="L">Send Analytics to Budibase</Label>
|
||||
<Toggle text="" value={!analyticsDisabled} />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -97,7 +97,8 @@
|
|||
}
|
||||
.field {
|
||||
display: grid;
|
||||
grid-template-columns: 30% 1fr;
|
||||
grid-template-columns: 32% 1fr;
|
||||
align-items: center;
|
||||
}
|
||||
.file {
|
||||
max-width: 30ch;
|
|
@ -1 +1,4 @@
|
|||
Index route
|
||||
<script>
|
||||
import { goto } from "@roxi/routify"
|
||||
$goto("./builder")
|
||||
</script>
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
<script>
|
||||
import { Heading, Layout } from "@budibase/bbui"
|
||||
</script>
|
||||
|
||||
<Layout noPadding>
|
||||
<div>
|
||||
<Heading>Apps</Heading>
|
||||
</div>
|
||||
<div class="appList">
|
||||
{#each new Array(10) as _}
|
||||
<div class="app" />
|
||||
{/each}
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
.appList {
|
||||
display: grid;
|
||||
grid-gap: 50px;
|
||||
grid-template-columns: repeat(auto-fill, 300px);
|
||||
}
|
||||
.app {
|
||||
height: 130px;
|
||||
border-radius: 4px;
|
||||
background-color: var(--spectrum-global-color-gray-200);
|
||||
}
|
||||
</style>
|
|
@ -1,28 +1,25 @@
|
|||
import { writable } from "svelte/store"
|
||||
import api from "../../builderStore/api"
|
||||
|
||||
async function checkAuth() {
|
||||
const response = await api.get("/api/self")
|
||||
const user = await response.json()
|
||||
if (response.status === 200) return user
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export function createAuthStore() {
|
||||
const { subscribe, set } = writable(null)
|
||||
|
||||
checkAuth()
|
||||
.then(user => set({ user }))
|
||||
.catch(() => set({ user: null }))
|
||||
const store = writable({ user: null })
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
subscribe: store.subscribe,
|
||||
checkAuth: async () => {
|
||||
const response = await api.get("/api/self")
|
||||
const user = await response.json()
|
||||
if (response.status === 200) {
|
||||
store.update(state => ({ ...state, user }))
|
||||
} else {
|
||||
store.update(state => ({ ...state, user: null }))
|
||||
}
|
||||
},
|
||||
login: async creds => {
|
||||
const response = await api.post(`/api/admin/auth`, creds)
|
||||
const json = await response.json()
|
||||
if (response.status === 200) {
|
||||
set({ user: json.user })
|
||||
store.update(state => ({ ...state, user: json.user }))
|
||||
} else {
|
||||
throw "Invalid credentials"
|
||||
}
|
||||
|
@ -34,7 +31,7 @@ export function createAuthStore() {
|
|||
throw "Unable to create logout"
|
||||
}
|
||||
await response.json()
|
||||
set({ user: null })
|
||||
store.update(state => ({ ...state, user: null }))
|
||||
},
|
||||
createUser: async user => {
|
||||
const response = await api.post(`/api/admin/users`, user)
|
||||
|
@ -43,13 +40,6 @@ export function createAuthStore() {
|
|||
}
|
||||
await response.json()
|
||||
},
|
||||
firstUser: async () => {
|
||||
const response = await api.post(`/api/admin/users/first`)
|
||||
if (response.status !== 200) {
|
||||
throw "Unable to create test user"
|
||||
}
|
||||
await response.json()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
import { writable } from "svelte/store"
|
||||
import { get } from "builderStore/api"
|
||||
|
||||
export function createAppStore() {
|
||||
const store = writable([])
|
||||
|
||||
async function load() {
|
||||
try {
|
||||
const res = await get("/api/applications")
|
||||
const json = await res.json()
|
||||
if (res.ok && Array.isArray(json)) {
|
||||
store.set(json)
|
||||
} else {
|
||||
store.set([])
|
||||
}
|
||||
} catch (error) {
|
||||
store.set([])
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
subscribe: store.subscribe,
|
||||
load,
|
||||
}
|
||||
}
|
||||
|
||||
export const apps = createAppStore()
|
|
@ -1,2 +1,3 @@
|
|||
export { organisation } from "./organisation"
|
||||
export { admin } from "./admin"
|
||||
export { apps } from "./apps"
|
||||
|
|
|
@ -6,7 +6,7 @@ import path from "path"
|
|||
export default ({ mode }) => {
|
||||
const isProduction = mode === "production"
|
||||
return {
|
||||
base: "/",
|
||||
base: "/builder/",
|
||||
build: {
|
||||
minify: isProduction,
|
||||
outDir: "../server/builder",
|
||||
|
@ -52,6 +52,14 @@ export default ({ mode }) => {
|
|||
find: "analytics",
|
||||
replacement: path.resolve("./src/analytics"),
|
||||
},
|
||||
{
|
||||
find: "actions",
|
||||
replacement: path.resolve("./src/actions"),
|
||||
},
|
||||
{
|
||||
find: "helpers",
|
||||
replacement: path.resolve("./src/helpers"),
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { getContext } from "svelte"
|
||||
|
||||
const { styleable } = getContext("sdk")
|
||||
const { styleable, linkable } = getContext("sdk")
|
||||
const component = getContext("component")
|
||||
|
||||
export const className = ""
|
||||
|
@ -41,6 +41,7 @@
|
|||
<footer>
|
||||
<p class="subtext">{subtext}</p>
|
||||
<a
|
||||
use:linkable
|
||||
style="--linkColor: {linkColor}; --linkHoverColor: {linkHoverColor}"
|
||||
href={linkUrl || "/"}>{linkText}</a
|
||||
>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { getContext } from "svelte"
|
||||
|
||||
const { styleable } = getContext("sdk")
|
||||
const { styleable, linkable } = getContext("sdk")
|
||||
const component = getContext("component")
|
||||
|
||||
export let imageUrl = ""
|
||||
|
@ -13,7 +13,7 @@
|
|||
</script>
|
||||
|
||||
<div class="container" use:styleable={$component.styles}>
|
||||
<a href={destinationUrl}>
|
||||
<a use:linkable href={destinationUrl}>
|
||||
<div class="stackedlist">
|
||||
{#if showImage}
|
||||
<div class="image-block">
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue