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
|
- name: local_services
|
||||||
domains: ["*"]
|
domains: ["*"]
|
||||||
routes:
|
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/" }
|
- match: { prefix: "/db/" }
|
||||||
route:
|
route:
|
||||||
cluster: couchdb-service
|
cluster: couchdb-service
|
||||||
|
@ -33,7 +38,11 @@ static_resources:
|
||||||
route:
|
route:
|
||||||
cluster: server-dev
|
cluster: server-dev
|
||||||
|
|
||||||
- match: { prefix: "/" }
|
- match: { path: "/" }
|
||||||
|
route:
|
||||||
|
cluster: builder-dev
|
||||||
|
|
||||||
|
- match: { prefix: "/builder/" }
|
||||||
route:
|
route:
|
||||||
cluster: builder-dev
|
cluster: builder-dev
|
||||||
|
|
||||||
|
@ -41,7 +50,7 @@ static_resources:
|
||||||
route:
|
route:
|
||||||
cluster: builder-dev
|
cluster: builder-dev
|
||||||
prefix_rewrite: "/builder/"
|
prefix_rewrite: "/builder/"
|
||||||
|
|
||||||
# minio is on the default route because this works
|
# minio is on the default route because this works
|
||||||
# best, minio + AWS SDK doesn't handle path proxy
|
# best, minio + AWS SDK doesn't handle path proxy
|
||||||
- match: { prefix: "/" }
|
- match: { prefix: "/" }
|
||||||
|
|
|
@ -21,7 +21,6 @@ static_resources:
|
||||||
cluster: app-service
|
cluster: app-service
|
||||||
prefix_rewrite: "/"
|
prefix_rewrite: "/"
|
||||||
|
|
||||||
# special case for presenting our static self hosting page
|
|
||||||
- match: { path: "/" }
|
- match: { path: "/" }
|
||||||
route:
|
route:
|
||||||
cluster: app-service
|
cluster: app-service
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import Menu from "../Menu/Menu.svelte"
|
import Menu from "../Menu/Menu.svelte"
|
||||||
|
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
export let align = "left"
|
||||||
|
|
||||||
let anchor
|
let anchor
|
||||||
let dropdown
|
let dropdown
|
||||||
|
@ -31,7 +32,7 @@
|
||||||
<div use:getAnchor on:click={openMenu}>
|
<div use:getAnchor on:click={openMenu}>
|
||||||
<slot name="control" />
|
<slot name="control" />
|
||||||
</div>
|
</div>
|
||||||
<Popover bind:this={dropdown} {anchor} align="left">
|
<Popover bind:this={dropdown} {anchor} {align}>
|
||||||
<Menu>
|
<Menu>
|
||||||
<slot />
|
<slot />
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
export let getOptionValue = option => option
|
export let getOptionValue = option => option
|
||||||
export let open = false
|
export let open = false
|
||||||
export let readonly = false
|
export let readonly = false
|
||||||
|
export let quiet = false
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const onClick = e => {
|
const onClick = e => {
|
||||||
|
@ -33,6 +34,7 @@
|
||||||
<button
|
<button
|
||||||
{id}
|
{id}
|
||||||
class="spectrum-Picker spectrum-Picker--sizeM"
|
class="spectrum-Picker spectrum-Picker--sizeM"
|
||||||
|
class:spectrum-Picker--quiet={quiet}
|
||||||
{disabled}
|
{disabled}
|
||||||
class:is-invalid={!!error}
|
class:is-invalid={!!error}
|
||||||
class:is-open={open}
|
class:is-open={open}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
export let getOptionLabel = option => option
|
export let getOptionLabel = option => option
|
||||||
export let getOptionValue = option => option
|
export let getOptionValue = option => option
|
||||||
export let readonly = false
|
export let readonly = false
|
||||||
|
export let quiet = false
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
let open = false
|
let open = false
|
||||||
|
@ -43,6 +44,7 @@
|
||||||
<Picker
|
<Picker
|
||||||
on:click
|
on:click
|
||||||
bind:open
|
bind:open
|
||||||
|
{quiet}
|
||||||
{id}
|
{id}
|
||||||
{error}
|
{error}
|
||||||
{disabled}
|
{disabled}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
export let options = []
|
export let options = []
|
||||||
export let getOptionLabel = option => extractProperty(option, "label")
|
export let getOptionLabel = option => extractProperty(option, "label")
|
||||||
export let getOptionValue = option => extractProperty(option, "value")
|
export let getOptionValue = option => extractProperty(option, "value")
|
||||||
|
export let quiet = false
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const onChange = e => {
|
const onChange = e => {
|
||||||
|
@ -29,6 +30,7 @@
|
||||||
|
|
||||||
<Field {label} {labelPosition} {disabled} {error}>
|
<Field {label} {labelPosition} {disabled} {error}>
|
||||||
<Select
|
<Select
|
||||||
|
{quiet}
|
||||||
{error}
|
{error}
|
||||||
{disabled}
|
{disabled}
|
||||||
{readonly}
|
{readonly}
|
||||||
|
|
|
@ -1,62 +1,16 @@
|
||||||
<script>
|
<script>
|
||||||
export let forAttr = "",
|
import "@spectrum-css/fieldlabel/dist/index-vars.css"
|
||||||
extraSmall = false,
|
|
||||||
small = false,
|
export let size = "M"
|
||||||
medium = false,
|
|
||||||
large = false,
|
|
||||||
extraLarge = false,
|
|
||||||
white = false,
|
|
||||||
grey = false,
|
|
||||||
black = false
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<label
|
<label class={`spectrum-FieldLabel spectrum-FieldLabel--size${size}`}>
|
||||||
class="bb-label"
|
|
||||||
class:extraSmall
|
|
||||||
class:small
|
|
||||||
class:medium
|
|
||||||
class:large
|
|
||||||
class:extraLarge
|
|
||||||
class:white
|
|
||||||
class:grey
|
|
||||||
class:black
|
|
||||||
for={forAttr}
|
|
||||||
>
|
|
||||||
<slot />
|
<slot />
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.bb-label {
|
label {
|
||||||
font-family: var(--font-sans);
|
padding: 0;
|
||||||
font-weight: 500;
|
white-space: nowrap;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -44,6 +44,9 @@
|
||||||
padding-top: var(--spacing-l);
|
padding-top: var(--spacing-l);
|
||||||
padding-bottom: var(--spacing-l);
|
padding-bottom: var(--spacing-l);
|
||||||
}
|
}
|
||||||
|
.gap-XS {
|
||||||
|
grid-gap: var(--spacing-s);
|
||||||
|
}
|
||||||
.gap-S {
|
.gap-S {
|
||||||
grid-gap: var(--spectrum-alias-grid-gutter-xsmall);
|
grid-gap: var(--spectrum-alias-grid-gutter-xsmall);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
>
|
>
|
||||||
{#if icon}
|
{#if icon}
|
||||||
<svg
|
<svg
|
||||||
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Menu-itemIcon"
|
class="spectrum-Icon spectrum-Icon--sizeS spectrum-Menu-itemIcon"
|
||||||
focusable="false"
|
focusable="false"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
aria-label={icon}
|
aria-label={icon}
|
||||||
|
@ -37,3 +37,9 @@
|
||||||
{/if}
|
{/if}
|
||||||
<span class="spectrum-Menu-itemLabel"><slot /></span>
|
<span class="spectrum-Menu-itemLabel"><slot /></span>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.spectrum-Menu-itemIcon {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -3,11 +3,20 @@
|
||||||
|
|
||||||
export let size = "M"
|
export let size = "M"
|
||||||
export let serif = false
|
export let serif = false
|
||||||
|
export let noPadding = false
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<p
|
<p
|
||||||
class="spectrum-Body class:spectrum-Body--size{size}"
|
class:noPadding
|
||||||
|
class="spectrum-Body spectrum-Body--size{size}"
|
||||||
class:spectrum-Body--serif={serif}
|
class:spectrum-Body--serif={serif}
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</p>
|
</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>
|
<script>
|
||||||
import { Input, Select, DatePicker, Toggle, TextArea } from "@budibase/bbui"
|
import { Input, Select, DatePicker, Toggle, TextArea } from "@budibase/bbui"
|
||||||
import Dropzone from "components/common/Dropzone.svelte"
|
import Dropzone from "components/common/Dropzone.svelte"
|
||||||
import { capitalise } from "../../../helpers"
|
import { capitalise } from "helpers"
|
||||||
import LinkedRowSelector from "components/common/LinkedRowSelector.svelte"
|
import LinkedRowSelector from "components/common/LinkedRowSelector.svelte"
|
||||||
|
|
||||||
export let defaultValue
|
export let defaultValue
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
|
|
||||||
const selectRelationship = ({ tableId, rowId, fieldName }) => {
|
const selectRelationship = ({ tableId, rowId, fieldName }) => {
|
||||||
$goto(
|
$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,
|
Body,
|
||||||
ModalContent,
|
ModalContent,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { capitalise } from "../../../../helpers"
|
import { capitalise } from "helpers"
|
||||||
|
|
||||||
export let resourceId
|
export let resourceId
|
||||||
export let permissions
|
export let permissions
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { Label, Input, Layout } from "@budibase/bbui"
|
import { Label, Input, Layout } from "@budibase/bbui"
|
||||||
import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte"
|
import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte"
|
||||||
import { capitalise } from "../../../../helpers"
|
import { capitalise } from "helpers"
|
||||||
|
|
||||||
export let integration
|
export let integration
|
||||||
export let schema
|
export let schema
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
getBindableProperties,
|
getBindableProperties,
|
||||||
readableToRuntimeBinding,
|
readableToRuntimeBinding,
|
||||||
} from "builderStore/dataBinding"
|
} from "builderStore/dataBinding"
|
||||||
import { currentAsset, store } from "../../../builderStore"
|
import { currentAsset, store } from "builderStore"
|
||||||
import { handlebarsCompletions } from "constants/completions"
|
import { handlebarsCompletions } from "constants/completions"
|
||||||
import { addToText } from "./utils"
|
import { addToText } from "./utils"
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import { isValid } from "@budibase/string-templates"
|
import { isValid } from "@budibase/string-templates"
|
||||||
import { handlebarsCompletions } from "constants/completions"
|
import { handlebarsCompletions } from "constants/completions"
|
||||||
import { readableToRuntimeBinding } from "../../../builderStore/dataBinding"
|
import { readableToRuntimeBinding } from "builderStore/dataBinding"
|
||||||
import { addToText } from "./utils"
|
import { addToText } from "./utils"
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import { store, currentAsset, selectedComponent } from "builderStore"
|
import { store, currentAsset, selectedComponent } from "builderStore"
|
||||||
import iframeTemplate from "./iframeTemplate"
|
import iframeTemplate from "./iframeTemplate"
|
||||||
import { Screen } from "builderStore/store/screenTemplates/utils/Screen"
|
import { Screen } from "builderStore/store/screenTemplates/utils/Screen"
|
||||||
import { FrontendTypes } from "../../../constants"
|
import { FrontendTypes } from "constants"
|
||||||
|
|
||||||
let iframe
|
let iframe
|
||||||
let layout
|
let layout
|
||||||
|
@ -82,7 +82,8 @@
|
||||||
style="height: 100%; width: 100%"
|
style="height: 100%; width: 100%"
|
||||||
title="componentPreview"
|
title="componentPreview"
|
||||||
bind:this={iframe}
|
bind:this={iframe}
|
||||||
srcdoc={template} />
|
srcdoc={template}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
runtimeToReadableBinding,
|
runtimeToReadableBinding,
|
||||||
} from "builderStore/dataBinding"
|
} from "builderStore/dataBinding"
|
||||||
import BindingPanel from "components/common/bindings/BindingPanel.svelte"
|
import BindingPanel from "components/common/bindings/BindingPanel.svelte"
|
||||||
import { capitalise } from "../../../../helpers"
|
import { capitalise } from "helpers"
|
||||||
|
|
||||||
export let label = ""
|
export let label = ""
|
||||||
export let bindable = true
|
export let bindable = true
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { goto } from "@roxi/routify"
|
||||||
import {
|
import {
|
||||||
notifications,
|
notifications,
|
||||||
Button,
|
|
||||||
Link,
|
Link,
|
||||||
Input,
|
Input,
|
||||||
Modal,
|
Modal,
|
||||||
|
@ -18,27 +18,18 @@
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
})
|
})
|
||||||
notifications.success("Logged in successfully.")
|
notifications.success("Logged in successfully")
|
||||||
|
$goto("../portal")
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
notifications.error("Invalid credentials")
|
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>
|
</script>
|
||||||
|
|
||||||
<Modal fixed>
|
<Modal fixed>
|
||||||
<ModalContent
|
<ModalContent
|
||||||
size="L"
|
size="M"
|
||||||
title="Log In"
|
title="Log In"
|
||||||
onConfirm={login}
|
onConfirm={login}
|
||||||
confirmText="Log In"
|
confirmText="Log In"
|
||||||
|
@ -51,7 +42,6 @@
|
||||||
<Link target="_blank" href="/api/admin/auth/google">
|
<Link target="_blank" href="/api/admin/auth/google">
|
||||||
Sign In With Google
|
Sign In With Google
|
||||||
</Link>
|
</Link>
|
||||||
<Button secondary on:click={createTestUser}>Create Test User</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
const id = $params.application
|
const id = $params.application
|
||||||
await del(`/api/applications/${id}`)
|
await del(`/api/applications/${id}`)
|
||||||
loading = false
|
loading = false
|
||||||
$goto("/builder/")
|
$goto("/builder")
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,19 @@
|
||||||
<script>
|
<script>
|
||||||
import { goto } from "@roxi/routify"
|
import {
|
||||||
import { ActionButton, Heading } from "@budibase/bbui"
|
Heading,
|
||||||
import { notifications } from "@budibase/bbui"
|
Icon,
|
||||||
import Spinner from "components/common/Spinner.svelte"
|
Body,
|
||||||
|
Layout,
|
||||||
|
ActionMenu,
|
||||||
|
MenuItem,
|
||||||
|
Link,
|
||||||
|
notifications,
|
||||||
|
} from "@budibase/bbui"
|
||||||
import download from "downloadjs"
|
import download from "downloadjs"
|
||||||
|
import { gradient } from "actions"
|
||||||
|
|
||||||
export let name, _id
|
export let name
|
||||||
|
export let _id
|
||||||
|
|
||||||
let appExportLoading = false
|
let appExportLoading = false
|
||||||
|
|
||||||
|
@ -15,58 +23,60 @@
|
||||||
download(
|
download(
|
||||||
`/api/backups/export?appId=${_id}&appname=${encodeURIComponent(name)}`
|
`/api/backups/export?appId=${_id}&appname=${encodeURIComponent(name)}`
|
||||||
)
|
)
|
||||||
notifications.success("App Export Complete.")
|
notifications.success("App export complete")
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
notifications.error("App Export Failed.")
|
notifications.error("App export failed")
|
||||||
} finally {
|
} finally {
|
||||||
appExportLoading = false
|
appExportLoading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="apps-card">
|
<Layout noPadding gap="XS">
|
||||||
<Heading size="S">{name}</Heading>
|
<div class="preview" use:gradient />
|
||||||
<div class="card-footer" data-cy={`app-${name}`}>
|
<div class="title">
|
||||||
<ActionButton on:click={() => $goto(`/builder/${_id}`)}>
|
<Link href={`/builder/app/${_id}`}>
|
||||||
Open
|
<Heading size="XS">
|
||||||
{name}
|
{name}
|
||||||
→
|
</Heading>
|
||||||
</ActionButton>
|
</Link>
|
||||||
{#if appExportLoading}
|
<ActionMenu>
|
||||||
<Spinner size="10" />
|
<Icon slot="control" name="More" hoverable />
|
||||||
{:else}
|
<MenuItem on:click={exportApp} icon="Download">Export</MenuItem>
|
||||||
<ActionButton icon="Download" quiet />
|
</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}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Layout>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.apps-card {
|
.preview {
|
||||||
background-color: var(--background);
|
height: 135px;
|
||||||
padding: var(--spacing-xl) var(--spacing-xl) var(--spacing-xl)
|
border-radius: var(--border-radius-s);
|
||||||
var(--spacing-xl);
|
margin-bottom: var(--spacing-s);
|
||||||
max-width: 300px;
|
|
||||||
max-height: 150px;
|
|
||||||
border-radius: var(--border-radius-m);
|
|
||||||
border: var(--border-dark);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-footer {
|
.title,
|
||||||
|
.status {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-top: var(--spacing-m);
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
i {
|
.title :global(a) {
|
||||||
font-size: var(--font-size-l);
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.title :global(h1:hover) {
|
||||||
|
color: var(--spectrum-global-color-blue-600);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: 0.2s all;
|
transition: color 130ms ease;
|
||||||
}
|
|
||||||
|
|
||||||
i:hover {
|
|
||||||
color: var(--blue);
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,50 +1,25 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { onMount } from "svelte"
|
||||||
import AppCard from "./AppCard.svelte"
|
import AppCard from "./AppCard.svelte"
|
||||||
import { Heading, Divider } from "@budibase/bbui"
|
import { apps } from "stores/portal"
|
||||||
import Spinner from "components/common/Spinner.svelte"
|
|
||||||
import { get } from "builderStore/api"
|
|
||||||
|
|
||||||
let promise = getApps()
|
onMount(apps.load)
|
||||||
|
|
||||||
async function getApps() {
|
|
||||||
const res = await get("/api/applications")
|
|
||||||
const json = await res.json()
|
|
||||||
|
|
||||||
if (res.ok) {
|
|
||||||
return json
|
|
||||||
} else {
|
|
||||||
throw new Error(json)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="root">
|
{#if $apps.length}
|
||||||
<Heading size="M">Your Apps</Heading>
|
<div class="appList">
|
||||||
<Divider size="M" />
|
{#each $apps as app}
|
||||||
{#await promise}
|
<AppCard {...app} />
|
||||||
<div class="spinner-container">
|
{/each}
|
||||||
<Spinner size="30" />
|
</div>
|
||||||
</div>
|
{:else}
|
||||||
{:then apps}
|
<div>No apps found.</div>
|
||||||
<div class="apps">
|
{/if}
|
||||||
{#each apps as app}
|
|
||||||
<AppCard {...app} />
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
{:catch err}
|
|
||||||
<h1 style="color:red">{err}</h1>
|
|
||||||
{/await}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.root {
|
.appList {
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
.apps {
|
|
||||||
margin-top: var(--layout-m);
|
|
||||||
display: grid;
|
display: grid;
|
||||||
|
grid-gap: 50px;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||||
grid-gap: var(--layout-s);
|
|
||||||
justify-content: start;
|
|
||||||
}
|
}
|
||||||
</style>
|
</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)
|
const userResp = await api.post(`/api/users/metadata/self`, user)
|
||||||
await userResp.json()
|
await userResp.json()
|
||||||
$goto(`./${appJson._id}`)
|
window.location = `/builder/app/${appJson._id}`
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
notifications.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>
|
<script>
|
||||||
import {
|
import { onMount } from "svelte"
|
||||||
SideNavigation as Navigation,
|
import { goto } from "@roxi/routify"
|
||||||
SideNavigationItem as Item,
|
|
||||||
} from "@budibase/bbui"
|
|
||||||
import { auth } from "stores/backend"
|
import { auth } from "stores/backend"
|
||||||
import LoginForm from "components/login/LoginForm.svelte"
|
import { admin } from "stores/portal"
|
||||||
import BuilderSettingsButton from "components/start/BuilderSettingsButton.svelte"
|
|
||||||
import LogoutButton from "components/start/LogoutButton.svelte"
|
|
||||||
import Logo from "/assets/budibase-logo.svg"
|
|
||||||
|
|
||||||
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>
|
</script>
|
||||||
|
|
||||||
{#if $auth}
|
{#if loaded}
|
||||||
{#if $auth.user}
|
<slot />
|
||||||
<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}
|
{/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 ThemeEditorDropdown from "components/settings/ThemeEditorDropdown.svelte"
|
||||||
import FeedbackNavLink from "components/feedback/FeedbackNavLink.svelte"
|
import FeedbackNavLink from "components/feedback/FeedbackNavLink.svelte"
|
||||||
import { get } from "builderStore/api"
|
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 Logo from "/assets/bb-logo.svg"
|
||||||
import { capitalise } from "../../../helpers"
|
import { capitalise } from "helpers"
|
||||||
|
|
||||||
// Get Package and set store
|
// Get Package and set store
|
||||||
export let application
|
export let application
|
||||||
|
@ -60,7 +60,7 @@
|
||||||
<img
|
<img
|
||||||
src={Logo}
|
src={Logo}
|
||||||
alt="budibase icon"
|
alt="budibase icon"
|
||||||
on:click={() => $goto(`/builder/`)}
|
on:click={() => $goto(`../../portal/`)}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
import { notifications } from "@budibase/bbui"
|
import { notifications } from "@budibase/bbui"
|
||||||
import IntegrationConfigForm from "components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte"
|
import IntegrationConfigForm from "components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte"
|
||||||
import ICONS from "components/backend/DatasourceNavigator/icons"
|
import ICONS from "components/backend/DatasourceNavigator/icons"
|
||||||
import { capitalise } from "../../../../../../helpers"
|
import { capitalise } from "helpers"
|
||||||
|
|
||||||
let unsaved = false
|
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>
|
<script>
|
||||||
import api from "builderStore/api"
|
import { goto } from "@roxi/routify"
|
||||||
import AppList from "components/start/AppList.svelte"
|
$goto("./portal")
|
||||||
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()
|
|
||||||
</script>
|
</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>
|
<script>
|
||||||
import { isActive, url, goto } from "@roxi/routify"
|
import { isActive, goto } from "@roxi/routify"
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import {
|
import {
|
||||||
ActionMenu,
|
|
||||||
Checkbox,
|
|
||||||
MenuItem,
|
|
||||||
Icon,
|
Icon,
|
||||||
Heading,
|
|
||||||
Avatar,
|
Avatar,
|
||||||
Search,
|
Search,
|
||||||
Layout,
|
Layout,
|
||||||
ProgressCircle,
|
|
||||||
SideNavigation as Navigation,
|
SideNavigation as Navigation,
|
||||||
SideNavigationItem as Item,
|
SideNavigationItem as Item,
|
||||||
|
ActionMenu,
|
||||||
|
MenuItem,
|
||||||
|
Modal,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import api from "builderStore/api"
|
|
||||||
import ConfigChecklist from "components/common/ConfigChecklist.svelte"
|
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()
|
organisation.init()
|
||||||
|
apps.load()
|
||||||
|
|
||||||
let orgName
|
let orgName
|
||||||
let orgLogo
|
let orgLogo
|
||||||
let user
|
let user
|
||||||
|
let oldSettingsModal
|
||||||
|
|
||||||
async function getInfo() {
|
async function getInfo() {
|
||||||
// fetch orgInfo
|
// fetch orgInfo
|
||||||
orgName = "ACME Inc."
|
orgName = "ACME Inc."
|
||||||
orgLogo = "https://via.placeholder.com/150"
|
orgLogo = "https://via.placeholder.com/150"
|
||||||
|
|
||||||
user = { name: "John Doe" }
|
user = { name: "John Doe" }
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(getInfo)
|
onMount(getInfo)
|
||||||
|
|
||||||
let menu = [
|
let menu = [
|
||||||
{ title: "Apps", href: "/portal/apps" },
|
{ title: "Apps", href: "/builder/portal/apps" },
|
||||||
{ title: "Drafts", href: "/portal/drafts" },
|
{ title: "Drafts", href: "/builder/portal/drafts" },
|
||||||
{ title: "Users", href: "/portal/users", heading: "Manage" },
|
{ title: "Users", href: "/builder/portal/users", heading: "Manage" },
|
||||||
{ title: "Groups", href: "/portal/groups" },
|
{ title: "Groups", href: "/builder/portal/groups" },
|
||||||
{ title: "Auth", href: "/portal/oauth" },
|
{ title: "Auth", href: "/builder/portal/oauth" },
|
||||||
{ title: "Email", href: "/portal/email" },
|
{ title: "Email", href: "/builder/portal/email" },
|
||||||
{ title: "General", href: "/portal/settings/general", heading: "Settings" },
|
{
|
||||||
{ title: "Theming", href: "/portal/theming" },
|
title: "General",
|
||||||
{ title: "Account", href: "/portal/account" },
|
href: "/builder/portal/settings/general",
|
||||||
|
heading: "Settings",
|
||||||
|
},
|
||||||
|
{ title: "Theming", href: "/builder/portal/theming" },
|
||||||
|
{ title: "Account", href: "/builder/portal/account" },
|
||||||
]
|
]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -51,7 +55,7 @@
|
||||||
<div class="nav">
|
<div class="nav">
|
||||||
<Layout paddingX="L" paddingY="L">
|
<Layout paddingX="L" paddingY="L">
|
||||||
<div class="branding">
|
<div class="branding">
|
||||||
<div class="name">
|
<div class="name" on:click={() => $goto("./apps")}>
|
||||||
<img
|
<img
|
||||||
src={$organisation?.logoUrl || "https://i.imgur.com/ZKyklgF.png"}
|
src={$organisation?.logoUrl || "https://i.imgur.com/ZKyklgF.png"}
|
||||||
alt="Logotype"
|
alt="Logotype"
|
||||||
|
@ -74,30 +78,42 @@
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
<Search placeholder="Global search" />
|
<Search placeholder="Global search" />
|
||||||
<div class="avatar">
|
<ActionMenu align="right">
|
||||||
<Avatar size="M" name="John Doe" />
|
<div slot="control" class="avatar">
|
||||||
<Icon size="XL" name="ChevronDown" />
|
<Avatar size="M" name="John Doe" />
|
||||||
</div>
|
<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>
|
<div class="content">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Modal bind:this={oldSettingsModal} width="30%">
|
||||||
|
<BuilderSettingsModal />
|
||||||
|
</Modal>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.container {
|
.container {
|
||||||
min-height: 100vh;
|
height: 100%;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 250px 1fr;
|
grid-template-columns: 250px 1fr;
|
||||||
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
.nav {
|
.nav {
|
||||||
background: var(--background);
|
background: var(--background);
|
||||||
border-right: var(--border-light);
|
border-right: var(--border-light);
|
||||||
|
overflow: auto;
|
||||||
}
|
}
|
||||||
.main {
|
.main {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: auto 1fr;
|
grid-template-rows: auto 1fr;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.branding {
|
.branding {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
@ -112,6 +128,9 @@
|
||||||
grid-gap: var(--spacing-m);
|
grid-gap: var(--spacing-m);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
.name:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
.avatar {
|
.avatar {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: auto auto;
|
grid-template-columns: auto auto;
|
||||||
|
@ -129,6 +148,7 @@
|
||||||
grid-template-columns: 250px auto;
|
grid-template-columns: 250px auto;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: var(--spacing-m) calc(var(--spacing-xl) * 2);
|
padding: var(--spacing-m) calc(var(--spacing-xl) * 2);
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
img {
|
img {
|
||||||
width: 28px;
|
width: 28px;
|
||||||
|
@ -139,4 +159,7 @@
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
.content {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
</style>
|
</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>
|
</script>
|
||||||
|
|
||||||
<Page>
|
<Page>
|
||||||
<header>
|
<Layout noPadding>
|
||||||
<Heading size="M">OAuth</Heading>
|
<div>
|
||||||
<Body size="S">
|
<Heading size="M">OAuth</Heading>
|
||||||
Every budibase app comes with basic authentication (email/password)
|
<Body>
|
||||||
included. You can add additional authentication methods from the options
|
Every budibase app comes with basic authentication (email/password)
|
||||||
below.
|
included. You can add additional authentication methods from the options
|
||||||
</Body>
|
below.
|
||||||
</header>
|
</Body>
|
||||||
<Divider />
|
</div>
|
||||||
{#if google}
|
<Divider />
|
||||||
<div class="config-form">
|
{#if google}
|
||||||
<Layout gap="S">
|
<div>
|
||||||
<Heading size="S">
|
<Heading size="S">
|
||||||
<span>
|
<span>
|
||||||
<GoogleLogo />
|
<GoogleLogo />
|
||||||
Google
|
Google
|
||||||
</span>
|
</span>
|
||||||
</Heading>
|
</Heading>
|
||||||
{#each ConfigFields.Google as field}
|
<Body>
|
||||||
<div class="form-row">
|
To allow users to authenticate using their Google accounts, fill out
|
||||||
<Label>{field}</Label>
|
the fields below.
|
||||||
<Input bind:value={google.config[field]} />
|
</Body>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
|
||||||
</Layout>
|
{#each ConfigFields.Google as field}
|
||||||
<Button primary on:click={() => save(google)}>Save</Button>
|
<div class="form-row">
|
||||||
</div>
|
<Label size="L">{field}</Label>
|
||||||
<Divider />
|
<Input bind:value={google.config[field]} />
|
||||||
{/if}
|
</div>
|
||||||
|
{/each}
|
||||||
|
<div>
|
||||||
|
<Button primary on:click={() => save(google)}>Save</Button>
|
||||||
|
</div>
|
||||||
|
<Divider />
|
||||||
|
{/if}
|
||||||
|
</Layout>
|
||||||
</Page>
|
</Page>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.config-form {
|
|
||||||
margin-top: 42px;
|
|
||||||
margin-bottom: 42px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-row {
|
.form-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 20% 1fr;
|
grid-template-columns: 20% 1fr;
|
||||||
grid-gap: var(--spacing-l);
|
grid-gap: var(--spacing-l);
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
span {
|
span {
|
||||||
|
@ -108,8 +111,4 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--spacing-s);
|
gap: var(--spacing-s);
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
|
||||||
margin-bottom: 42px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
@ -45,12 +45,12 @@
|
||||||
<Layout noPadding>
|
<Layout noPadding>
|
||||||
<div class="intro">
|
<div class="intro">
|
||||||
<Heading size="M">General</Heading>
|
<Heading size="M">General</Heading>
|
||||||
<Body
|
<Body>
|
||||||
>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Hic vero, aut
|
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Hic vero, aut
|
||||||
culpa provident sunt ratione! Voluptas doloremque, dicta nisi velit
|
culpa provident sunt ratione! Voluptas doloremque, dicta nisi velit
|
||||||
perspiciatis, ratione vel blanditiis totam, nam voluptate repellat
|
perspiciatis, ratione vel blanditiis totam, nam voluptate repellat
|
||||||
aperiam fuga!</Body
|
aperiam fuga!
|
||||||
>
|
</Body>
|
||||||
</div>
|
</div>
|
||||||
<Divider size="S" />
|
<Divider size="S" />
|
||||||
<div class="information">
|
<div class="information">
|
||||||
|
@ -58,7 +58,7 @@
|
||||||
<Body>Here you can update your logo and organization name.</Body>
|
<Body>Here you can update your logo and organization name.</Body>
|
||||||
<div class="fields">
|
<div class="fields">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<Label>Organization name</Label>
|
<Label size="L">Organization name</Label>
|
||||||
<Input thin bind:value={company} />
|
<Input thin bind:value={company} />
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="field">
|
<!-- <div class="field">
|
||||||
|
@ -72,13 +72,13 @@
|
||||||
<Divider size="S" />
|
<Divider size="S" />
|
||||||
<div class="analytics">
|
<div class="analytics">
|
||||||
<Heading size="S">Analytics</Heading>
|
<Heading size="S">Analytics</Heading>
|
||||||
<Body
|
<Body>
|
||||||
>If you would like to send analytics that help us make Budibase better,
|
If you would like to send analytics that help us make Budibase better,
|
||||||
please let us know below.</Body
|
please let us know below.
|
||||||
>
|
</Body>
|
||||||
<div class="fields">
|
<div class="fields">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<Label>Send Analytics to Budibase</Label>
|
<Label size="L">Send Analytics to Budibase</Label>
|
||||||
<Toggle text="" value={!analyticsDisabled} />
|
<Toggle text="" value={!analyticsDisabled} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -97,7 +97,8 @@
|
||||||
}
|
}
|
||||||
.field {
|
.field {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 30% 1fr;
|
grid-template-columns: 32% 1fr;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
.file {
|
.file {
|
||||||
max-width: 30ch;
|
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 { writable } from "svelte/store"
|
||||||
import api from "../../builderStore/api"
|
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() {
|
export function createAuthStore() {
|
||||||
const { subscribe, set } = writable(null)
|
const store = writable({ user: null })
|
||||||
|
|
||||||
checkAuth()
|
|
||||||
.then(user => set({ user }))
|
|
||||||
.catch(() => set({ user: null }))
|
|
||||||
|
|
||||||
return {
|
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 => {
|
login: async creds => {
|
||||||
const response = await api.post(`/api/admin/auth`, creds)
|
const response = await api.post(`/api/admin/auth`, creds)
|
||||||
const json = await response.json()
|
const json = await response.json()
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
set({ user: json.user })
|
store.update(state => ({ ...state, user: json.user }))
|
||||||
} else {
|
} else {
|
||||||
throw "Invalid credentials"
|
throw "Invalid credentials"
|
||||||
}
|
}
|
||||||
|
@ -34,7 +31,7 @@ export function createAuthStore() {
|
||||||
throw "Unable to create logout"
|
throw "Unable to create logout"
|
||||||
}
|
}
|
||||||
await response.json()
|
await response.json()
|
||||||
set({ user: null })
|
store.update(state => ({ ...state, user: null }))
|
||||||
},
|
},
|
||||||
createUser: async user => {
|
createUser: async user => {
|
||||||
const response = await api.post(`/api/admin/users`, user)
|
const response = await api.post(`/api/admin/users`, user)
|
||||||
|
@ -43,13 +40,6 @@ export function createAuthStore() {
|
||||||
}
|
}
|
||||||
await response.json()
|
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 { organisation } from "./organisation"
|
||||||
export { admin } from "./admin"
|
export { admin } from "./admin"
|
||||||
|
export { apps } from "./apps"
|
||||||
|
|
|
@ -6,7 +6,7 @@ import path from "path"
|
||||||
export default ({ mode }) => {
|
export default ({ mode }) => {
|
||||||
const isProduction = mode === "production"
|
const isProduction = mode === "production"
|
||||||
return {
|
return {
|
||||||
base: "/",
|
base: "/builder/",
|
||||||
build: {
|
build: {
|
||||||
minify: isProduction,
|
minify: isProduction,
|
||||||
outDir: "../server/builder",
|
outDir: "../server/builder",
|
||||||
|
@ -52,6 +52,14 @@ export default ({ mode }) => {
|
||||||
find: "analytics",
|
find: "analytics",
|
||||||
replacement: path.resolve("./src/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>
|
<script>
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
|
|
||||||
const { styleable } = getContext("sdk")
|
const { styleable, linkable } = getContext("sdk")
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
|
|
||||||
export const className = ""
|
export const className = ""
|
||||||
|
@ -41,6 +41,7 @@
|
||||||
<footer>
|
<footer>
|
||||||
<p class="subtext">{subtext}</p>
|
<p class="subtext">{subtext}</p>
|
||||||
<a
|
<a
|
||||||
|
use:linkable
|
||||||
style="--linkColor: {linkColor}; --linkHoverColor: {linkHoverColor}"
|
style="--linkColor: {linkColor}; --linkHoverColor: {linkHoverColor}"
|
||||||
href={linkUrl || "/"}>{linkText}</a
|
href={linkUrl || "/"}>{linkText}</a
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
|
|
||||||
const { styleable } = getContext("sdk")
|
const { styleable, linkable } = getContext("sdk")
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
|
|
||||||
export let imageUrl = ""
|
export let imageUrl = ""
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="container" use:styleable={$component.styles}>
|
<div class="container" use:styleable={$component.styles}>
|
||||||
<a href={destinationUrl}>
|
<a use:linkable href={destinationUrl}>
|
||||||
<div class="stackedlist">
|
<div class="stackedlist">
|
||||||
{#if showImage}
|
{#if showImage}
|
||||||
<div class="image-block">
|
<div class="image-block">
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue