Browse Source

Merge branch 'app-list' into admin/user-management-ui

pull/1491/head
Keviin Åberg Kultalahti 5 years ago
parent
commit
c2bae6da4f
  1. 13
      hosting/envoy.dev.yaml.hbs
  2. 1
      hosting/envoy.yaml
  3. 3
      packages/bbui/src/ActionMenu/ActionMenu.svelte
  4. 2
      packages/bbui/src/Form/Core/Picker.svelte
  5. 2
      packages/bbui/src/Form/Core/Select.svelte
  6. 2
      packages/bbui/src/Form/Select.svelte
  7. 60
      packages/bbui/src/Label/Label.svelte
  8. 3
      packages/bbui/src/Layout/Layout.svelte
  9. 8
      packages/bbui/src/Menu/Item.svelte
  10. 11
      packages/bbui/src/Typography/Body.svelte
  11. 64
      packages/builder/src/actions.js
  12. 2
      packages/builder/src/components/backend/DataTable/RowFieldControl.svelte
  13. 2
      packages/builder/src/components/backend/DataTable/Table.svelte
  14. 2
      packages/builder/src/components/backend/DataTable/modals/ManageAccessModal.svelte
  15. 2
      packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte
  16. 2
      packages/builder/src/components/common/bindings/BindingPanel.svelte
  17. 2
      packages/builder/src/components/common/bindings/ServerBindingPanel.svelte
  18. 5
      packages/builder/src/components/design/AppPreview/CurrentItemPreview.svelte
  19. 2
      packages/builder/src/components/design/PropertiesPanel/PropertyControls/PropertyControl.svelte
  20. 18
      packages/builder/src/components/login/LoginForm.svelte
  21. 2
      packages/builder/src/components/settings/tabs/DangerZone.svelte
  22. 86
      packages/builder/src/components/start/AppCard.svelte
  23. 53
      packages/builder/src/components/start/AppList.svelte
  24. 15
      packages/builder/src/components/start/BuilderSettingsButton.svelte
  25. 2
      packages/builder/src/components/start/CreateAppModal.svelte
  26. 6
      packages/builder/src/components/start/LogoutButton.svelte
  27. 29
      packages/builder/src/pages/_layout.svelte
  28. 69
      packages/builder/src/pages/admin/index.svelte
  29. 131
      packages/builder/src/pages/builder/_layout.svelte
  30. 78
      packages/builder/src/pages/builder/admin/index.svelte
  31. 6
      packages/builder/src/pages/builder/app/[application]/_reset.svelte
  32. 0
      packages/builder/src/pages/builder/app/[application]/automate/[automation]/_layout.svelte
  33. 0
      packages/builder/src/pages/builder/app/[application]/automate/[automation]/index.svelte
  34. 0
      packages/builder/src/pages/builder/app/[application]/automate/_layout.svelte
  35. 0
      packages/builder/src/pages/builder/app/[application]/automate/index.svelte
  36. 0
      packages/builder/src/pages/builder/app/[application]/data/_layout.svelte
  37. 0
      packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/[query]/_layout.svelte
  38. 0
      packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/[query]/index.svelte
  39. 0
      packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/_layout.svelte
  40. 2
      packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte
  41. 0
      packages/builder/src/pages/builder/app/[application]/data/datasource/_layout.svelte
  42. 0
      packages/builder/src/pages/builder/app/[application]/data/datasource/index.svelte
  43. 0
      packages/builder/src/pages/builder/app/[application]/data/index.svelte
  44. 0
      packages/builder/src/pages/builder/app/[application]/data/table/[selectedTable]/_layout.svelte
  45. 0
      packages/builder/src/pages/builder/app/[application]/data/table/[selectedTable]/index.svelte
  46. 0
      packages/builder/src/pages/builder/app/[application]/data/table/[selectedTable]/relationship/[selectedRow]/[selectedField]/index.svelte
  47. 0
      packages/builder/src/pages/builder/app/[application]/data/table/[selectedTable]/relationship/[selectedRow]/index.svelte
  48. 0
      packages/builder/src/pages/builder/app/[application]/data/table/[selectedTable]/relationship/index.svelte
  49. 0
      packages/builder/src/pages/builder/app/[application]/data/table/_layout.svelte
  50. 0
      packages/builder/src/pages/builder/app/[application]/data/table/index.svelte
  51. 0
      packages/builder/src/pages/builder/app/[application]/data/view/[selectedView]/_layout.svelte
  52. 0
      packages/builder/src/pages/builder/app/[application]/data/view/[selectedView]/index.svelte
  53. 0
      packages/builder/src/pages/builder/app/[application]/deploy/_layout.svelte
  54. 0
      packages/builder/src/pages/builder/app/[application]/deploy/index.svelte
  55. 0
      packages/builder/src/pages/builder/app/[application]/design/[assetType]/[asset]/_fallback.svelte
  56. 0
      packages/builder/src/pages/builder/app/[application]/design/[assetType]/_layout.svelte
  57. 0
      packages/builder/src/pages/builder/app/[application]/design/[assetType]/index.svelte
  58. 0
      packages/builder/src/pages/builder/app/[application]/design/_layout.svelte
  59. 0
      packages/builder/src/pages/builder/app/[application]/design/index.svelte
  60. 0
      packages/builder/src/pages/builder/app/[application]/index.svelte
  61. 0
      packages/builder/src/pages/builder/app/[application]/settings/_layout.svelte
  62. 0
      packages/builder/src/pages/builder/app/[application]/settings/index.svelte
  63. 4
      packages/builder/src/pages/builder/app/index.svelte
  64. 4
      packages/builder/src/pages/builder/auth/index.svelte
  65. 5
      packages/builder/src/pages/builder/auth/login.svelte
  66. 123
      packages/builder/src/pages/builder/index.svelte
  67. 73
      packages/builder/src/pages/builder/portal/_layout.svelte
  68. 0
      packages/builder/src/pages/builder/portal/apps/_layout.svelte
  69. 92
      packages/builder/src/pages/builder/portal/apps/index.svelte
  70. 0
      packages/builder/src/pages/builder/portal/index.svelte
  71. 70
      packages/builder/src/pages/builder/portal/oauth/index.svelte
  72. 0
      packages/builder/src/pages/builder/portal/settings/_layout.svelte
  73. 23
      packages/builder/src/pages/builder/portal/settings/general.svelte
  74. 0
      packages/builder/src/pages/builder/portal/settings/index.svelte
  75. 5
      packages/builder/src/pages/index.svelte
  76. 27
      packages/builder/src/pages/portal/apps/index.svelte
  77. 43
      packages/builder/src/pages/portal/manage/auth/logos/Google.svelte
  78. 36
      packages/builder/src/stores/backend/auth.js
  79. 27
      packages/builder/src/stores/portal/apps.js
  80. 1
      packages/builder/src/stores/portal/index.js
  81. 10
      packages/builder/vite.config.js
  82. 3
      packages/standard-components/src/CardHorizontal.svelte
  83. 4
      packages/standard-components/src/StackedList.svelte

13
hosting/envoy.dev.yaml.hbs

@ -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: "/" }

1
hosting/envoy.yaml

@ -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

3
packages/bbui/src/ActionMenu/ActionMenu.svelte

@ -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>

2
packages/bbui/src/Form/Core/Picker.svelte

@ -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}

2
packages/bbui/src/Form/Core/Select.svelte

@ -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}

2
packages/bbui/src/Form/Select.svelte

@ -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}

60
packages/bbui/src/Label/Label.svelte

@ -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>

3
packages/bbui/src/Layout/Layout.svelte

@ -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);
} }

8
packages/bbui/src/Menu/Item.svelte

@ -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>

11
packages/bbui/src/Typography/Body.svelte

@ -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>

64
packages/builder/src/actions.js

@ -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)
},
}
}

2
packages/builder/src/components/backend/DataTable/RowFieldControl.svelte

@ -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

2
packages/builder/src/components/backend/DataTable/Table.svelte

@ -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}`
) )
} }

2
packages/builder/src/components/backend/DataTable/modals/ManageAccessModal.svelte

@ -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

2
packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte

@ -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

2
packages/builder/src/components/common/bindings/BindingPanel.svelte

@ -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"

2
packages/builder/src/components/common/bindings/ServerBindingPanel.svelte

@ -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()

5
packages/builder/src/components/design/AppPreview/CurrentItemPreview.svelte

@ -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>

2
packages/builder/src/components/design/PropertiesPanel/PropertyControls/PropertyControl.svelte

@ -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

18
packages/builder/src/components/login/LoginForm.svelte

@ -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>

2
packages/builder/src/components/settings/tabs/DangerZone.svelte

@ -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>

86
packages/builder/src/components/start/AppCard.svelte

@ -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;
cursor: pointer;
transition: 0.2s all;
} }
.title :global(h1:hover) {
i:hover { color: var(--spectrum-global-color-blue-600);
color: var(--blue); cursor: pointer;
transition: color 130ms ease;
} }
</style> </style>

53
packages/builder/src/components/start/AppList.svelte

@ -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>

15
packages/builder/src/components/start/BuilderSettingsButton.svelte

@ -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>

2
packages/builder/src/components/start/CreateAppModal.svelte

@ -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)

6
packages/builder/src/components/start/LogoutButton.svelte

@ -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>

29
packages/builder/src/pages/_layout.svelte

@ -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}

69
packages/builder/src/pages/admin/index.svelte

@ -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>

131
packages/builder/src/pages/builder/_layout.svelte

@ -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
</script> $: hasAdminUser = !!$admin?.checklist?.adminUser
{#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}
<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 { onMount(async () => {
grid-column: 2; await admin.init()
overflow: auto; await auth.checkAuth()
} loaded = true
})
.ui-nav { // Force creation of an admin user if one doesn't exist
grid-column: 1; $: {
background-color: var(--background); if (loaded && !hasAdminUser) {
padding: 20px; $goto("./admin")
display: flex; }
flex-direction: column;
border-right: var(--border-light);
} }
.home-logo { // Redirect to log in at any time if the user isn't authenticated
cursor: pointer; $: {
height: 40px; if (loaded && hasAdminUser && !$auth.user) {
margin-bottom: 20px; $goto("./auth/login")
} }
.home-logo img {
height: 40px;
}
.nav-section {
margin: 20px 0 0 0;
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
} }
</script>
.nav-bottom :global(> *) { {#if loaded}
margin-top: 5px; <slot />
} {/if}
</style>

78
packages/builder/src/pages/builder/admin/index.svelte

@ -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
packages/builder/src/pages/builder/[application]/_reset.svelte → packages/builder/src/pages/builder/app/[application]/_reset.svelte

@ -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>

0
packages/builder/src/pages/builder/[application]/automate/[automation]/_layout.svelte → packages/builder/src/pages/builder/app/[application]/automate/[automation]/_layout.svelte

0
packages/builder/src/pages/builder/[application]/automate/[automation]/index.svelte → packages/builder/src/pages/builder/app/[application]/automate/[automation]/index.svelte

0
packages/builder/src/pages/builder/[application]/automate/_layout.svelte → packages/builder/src/pages/builder/app/[application]/automate/_layout.svelte

0
packages/builder/src/pages/builder/[application]/automate/index.svelte → packages/builder/src/pages/builder/app/[application]/automate/index.svelte

0
packages/builder/src/pages/builder/[application]/data/_layout.svelte → packages/builder/src/pages/builder/app/[application]/data/_layout.svelte

0
packages/builder/src/pages/builder/[application]/data/datasource/[selectedDatasource]/[query]/_layout.svelte → packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/[query]/_layout.svelte

0
packages/builder/src/pages/builder/[application]/data/datasource/[selectedDatasource]/[query]/index.svelte → packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/[query]/index.svelte

0
packages/builder/src/pages/builder/[application]/data/datasource/[selectedDatasource]/_layout.svelte → packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/_layout.svelte

2
packages/builder/src/pages/builder/[application]/data/datasource/[selectedDatasource]/index.svelte → packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte

@ -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
packages/builder/src/pages/builder/[application]/data/datasource/_layout.svelte → packages/builder/src/pages/builder/app/[application]/data/datasource/_layout.svelte

0
packages/builder/src/pages/builder/[application]/data/datasource/index.svelte → packages/builder/src/pages/builder/app/[application]/data/datasource/index.svelte

0
packages/builder/src/pages/builder/[application]/data/index.svelte → packages/builder/src/pages/builder/app/[application]/data/index.svelte

0
packages/builder/src/pages/builder/[application]/data/table/[selectedTable]/_layout.svelte → packages/builder/src/pages/builder/app/[application]/data/table/[selectedTable]/_layout.svelte

0
packages/builder/src/pages/builder/[application]/data/table/[selectedTable]/index.svelte → packages/builder/src/pages/builder/app/[application]/data/table/[selectedTable]/index.svelte

0
packages/builder/src/pages/builder/[application]/data/table/[selectedTable]/relationship/[selectedRow]/[selectedField]/index.svelte → packages/builder/src/pages/builder/app/[application]/data/table/[selectedTable]/relationship/[selectedRow]/[selectedField]/index.svelte

0
packages/builder/src/pages/builder/[application]/data/table/[selectedTable]/relationship/[selectedRow]/index.svelte → packages/builder/src/pages/builder/app/[application]/data/table/[selectedTable]/relationship/[selectedRow]/index.svelte

0
packages/builder/src/pages/builder/[application]/data/table/[selectedTable]/relationship/index.svelte → packages/builder/src/pages/builder/app/[application]/data/table/[selectedTable]/relationship/index.svelte

0
packages/builder/src/pages/builder/[application]/data/table/_layout.svelte → packages/builder/src/pages/builder/app/[application]/data/table/_layout.svelte

0
packages/builder/src/pages/builder/[application]/data/table/index.svelte → packages/builder/src/pages/builder/app/[application]/data/table/index.svelte

0
packages/builder/src/pages/builder/[application]/data/view/[selectedView]/_layout.svelte → packages/builder/src/pages/builder/app/[application]/data/view/[selectedView]/_layout.svelte

0
packages/builder/src/pages/builder/[application]/data/view/[selectedView]/index.svelte → packages/builder/src/pages/builder/app/[application]/data/view/[selectedView]/index.svelte

0
packages/builder/src/pages/builder/[application]/deploy/_layout.svelte → packages/builder/src/pages/builder/app/[application]/deploy/_layout.svelte

0
packages/builder/src/pages/builder/[application]/deploy/index.svelte → packages/builder/src/pages/builder/app/[application]/deploy/index.svelte

0
packages/builder/src/pages/builder/[application]/design/[assetType]/[asset]/_fallback.svelte → packages/builder/src/pages/builder/app/[application]/design/[assetType]/[asset]/_fallback.svelte

0
packages/builder/src/pages/builder/[application]/design/[assetType]/_layout.svelte → packages/builder/src/pages/builder/app/[application]/design/[assetType]/_layout.svelte

0
packages/builder/src/pages/builder/[application]/design/[assetType]/index.svelte → packages/builder/src/pages/builder/app/[application]/design/[assetType]/index.svelte

0
packages/builder/src/pages/builder/[application]/design/_layout.svelte → packages/builder/src/pages/builder/app/[application]/design/_layout.svelte

0
packages/builder/src/pages/builder/[application]/design/index.svelte → packages/builder/src/pages/builder/app/[application]/design/index.svelte

0
packages/builder/src/pages/builder/[application]/index.svelte → packages/builder/src/pages/builder/app/[application]/index.svelte

0
packages/builder/src/pages/builder/[application]/settings/_layout.svelte → packages/builder/src/pages/builder/app/[application]/settings/_layout.svelte

0
packages/builder/src/pages/builder/[application]/settings/index.svelte → packages/builder/src/pages/builder/app/[application]/settings/index.svelte

4
packages/builder/src/pages/builder/app/index.svelte

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

4
packages/builder/src/pages/builder/auth/index.svelte

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

5
packages/builder/src/pages/builder/auth/login.svelte

@ -0,0 +1,5 @@
<script>
import LoginForm from "components/login/LoginForm.svelte"
</script>
<LoginForm />

123
packages/builder/src/pages/builder/index.svelte

@ -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>

73
packages/builder/src/pages/portal/_layout.svelte → packages/builder/src/pages/builder/portal/_layout.svelte

@ -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/manage/users", heading: "Manage" }, { title: "Users", href: "/builder/portal/manage/users", heading: "Manage" },
{ title: "Groups", href: "/portal/manage/groups" }, { title: "Groups", href: "/builder/portal/manage/groups" },
{ title: "Auth", href: "/portal/manage/auth" }, { title: "Auth", href: "/builder/portal/manage/auth" },
{ title: "Email", href: "/portal/manage/email" }, { title: "Email", href: "/builder/portal/manage/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
packages/builder/src/pages/portal/apps/_layout.svelte → packages/builder/src/pages/builder/portal/apps/_layout.svelte

92
packages/builder/src/pages/builder/portal/apps/index.svelte

@ -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>

0
packages/builder/src/pages/portal/index.svelte → packages/builder/src/pages/builder/portal/index.svelte

70
packages/builder/src/pages/portal/manage/auth/index.svelte → packages/builder/src/pages/builder/portal/oauth/index.svelte

@ -1,7 +1,8 @@
<script> <script>
import GoogleLogo from "./logos/Google.svelte" import GoogleLogo from "./_logos/Google.svelte"
import { import {
Button, Button,
Page,
Heading, Heading,
Divider, Divider,
Label, Label,
@ -58,48 +59,51 @@
}) })
</script> </script>
<section> <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>
<Button primary on:click={() => save(google)}>Save</Button>
</div>
<Divider />
{/if}
</section>
<style> {#each ConfigFields.Google as field}
.config-form { <div class="form-row">
margin-top: 42px; <Label size="L">{field}</Label>
margin-bottom: 42px; <Input bind:value={google.config[field]} />
} </div>
{/each}
<div>
<Button primary on:click={() => save(google)}>Save</Button>
</div>
<Divider />
{/if}
</Layout>
</Page>
<style>
.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 {
@ -107,8 +111,4 @@
align-items: center; align-items: center;
gap: var(--spacing-s); gap: var(--spacing-s);
} }
header {
margin-bottom: 42px;
}
</style> </style>

0
packages/builder/src/pages/portal/settings/_layout.svelte → packages/builder/src/pages/builder/portal/settings/_layout.svelte

23
packages/builder/src/pages/portal/settings/general.svelte → packages/builder/src/pages/builder/portal/settings/general.svelte

@ -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;

0
packages/builder/src/pages/portal/settings/index.svelte → packages/builder/src/pages/builder/portal/settings/index.svelte

5
packages/builder/src/pages/index.svelte

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

27
packages/builder/src/pages/portal/apps/index.svelte

@ -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>

43
packages/builder/src/pages/portal/manage/auth/logos/Google.svelte

@ -1,43 +0,0 @@
<svg
width="18"
height="18"
viewBox="0 0 268 268"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clip-path="url(#clip0)">
<path
d="M58.8037 109.043C64.0284 93.2355 74.1116 79.4822 87.615 69.7447C101.118
60.0073 117.352 54.783 134 54.8172C152.872 54.8172 169.934 61.5172 183.334
72.4828L222.328 33.5C198.566 12.7858 168.114 0 134 0C81.1817 0 35.711
30.1277 13.8467 74.2583L58.8037 109.043Z"
fill="#EA4335"
/>
<path
d="M179.113 201.145C166.942 208.995 151.487 213.183 134 213.183C117.418
213.217 101.246 208.034 87.7727 198.369C74.2993 188.703 64.2077 175.044
58.9265 159.326L13.8132 193.574C24.8821 215.978 42.012 234.828 63.2572
247.984C84.5024 261.14 109.011 268.075 134 268C166.752 268 198.041 256.353
221.48 234.5L179.125 201.145H179.113Z"
fill="#34A853"
/>
<path
d="M221.48 234.5C245.991 211.631 261.903 177.595 261.903 134C261.903
126.072 260.686 117.552 258.866 109.634H134V161.414H205.869C202.329
178.823 192.804 192.301 179.125 201.145L221.48 234.5Z"
fill="#4A90E2"
/>
<path
d="M58.9265 159.326C56.1947 151.162 54.8068 142.609 54.8172 134C54.8172
125.268 56.213 116.882 58.8037 109.043L13.8467 74.2584C4.64957 92.825
-0.0915078 113.28 1.86708e-05 134C1.86708e-05 155.44 4.96919 175.652
13.8132 193.574L58.9265 159.326Z"
fill="#FBBC05"
/>
</g>
<defs>
<clipPath id="clip0">
<rect width="268" height="268" fill="white" />
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

36
packages/builder/src/stores/backend/auth.js

@ -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()
},
} }
} }

27
packages/builder/src/stores/portal/apps.js

@ -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
packages/builder/src/stores/portal/index.js

@ -1,3 +1,4 @@
export { organisation } from "./organisation" export { organisation } from "./organisation"
export { users } from "./users" export { users } from "./users"
export { admin } from "./admin" export { admin } from "./admin"
export { apps } from "./apps"

10
packages/builder/vite.config.js

@ -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"),
},
], ],
}, },
} }

3
packages/standard-components/src/CardHorizontal.svelte

@ -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
> >

4
packages/standard-components/src/StackedList.svelte

@ -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">

Loading…
Cancel
Save