mirror of https://github.com/Budibase/budibase.git
committed by
GitHub
86 changed files with 592 additions and 3671 deletions
@ -1,62 +1,16 @@ |
|||
<script> |
|||
export let forAttr = "", |
|||
extraSmall = false, |
|||
small = false, |
|||
medium = false, |
|||
large = false, |
|||
extraLarge = false, |
|||
white = false, |
|||
grey = false, |
|||
black = false |
|||
import "@spectrum-css/fieldlabel/dist/index-vars.css" |
|||
|
|||
export let size = "M" |
|||
</script> |
|||
|
|||
<label |
|||
class="bb-label" |
|||
class:extraSmall |
|||
class:small |
|||
class:medium |
|||
class:large |
|||
class:extraLarge |
|||
class:white |
|||
class:grey |
|||
class:black |
|||
for={forAttr} |
|||
> |
|||
<label class={`spectrum-FieldLabel spectrum-FieldLabel--size${size}`}> |
|||
<slot /> |
|||
</label> |
|||
|
|||
<style> |
|||
.bb-label { |
|||
font-family: var(--font-sans); |
|||
font-weight: 500; |
|||
text-rendering: var(--text-render); |
|||
color: var(--ink); |
|||
font-size: var(--font-size-s); |
|||
margin-bottom: var(--spacing-s); |
|||
display: block; |
|||
} |
|||
.extraSmall { |
|||
font-size: var(--font-size-xs); |
|||
} |
|||
.small { |
|||
font-size: var(--font-size-s); |
|||
} |
|||
.medium { |
|||
font-size: var(--font-size-m); |
|||
} |
|||
.large { |
|||
font-size: var(--font-size-l); |
|||
} |
|||
.extraLarge { |
|||
font-size: var(--font-size-xl); |
|||
} |
|||
.white { |
|||
color: white; |
|||
} |
|||
.grey { |
|||
color: var(--grey-6); |
|||
} |
|||
.black { |
|||
color: var(--ink); |
|||
label { |
|||
padding: 0; |
|||
white-space: nowrap; |
|||
} |
|||
</style> |
|||
|
|||
@ -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,50 +1,25 @@ |
|||
<script> |
|||
import { onMount } from "svelte" |
|||
import AppCard from "./AppCard.svelte" |
|||
import { Heading, Divider } from "@budibase/bbui" |
|||
import Spinner from "components/common/Spinner.svelte" |
|||
import { get } from "builderStore/api" |
|||
import { apps } from "stores/portal" |
|||
|
|||
let promise = getApps() |
|||
|
|||
async function getApps() { |
|||
const res = await get("/api/applications") |
|||
const json = await res.json() |
|||
|
|||
if (res.ok) { |
|||
return json |
|||
} else { |
|||
throw new Error(json) |
|||
} |
|||
} |
|||
onMount(apps.load) |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
<Heading size="M">Your Apps</Heading> |
|||
<Divider size="M" /> |
|||
{#await promise} |
|||
<div class="spinner-container"> |
|||
<Spinner size="30" /> |
|||
</div> |
|||
{:then apps} |
|||
<div class="apps"> |
|||
{#each apps as app} |
|||
<AppCard {...app} /> |
|||
{/each} |
|||
</div> |
|||
{:catch err} |
|||
<h1 style="color:red">{err}</h1> |
|||
{/await} |
|||
</div> |
|||
{#if $apps.length} |
|||
<div class="appList"> |
|||
{#each $apps as app} |
|||
<AppCard {...app} /> |
|||
{/each} |
|||
</div> |
|||
{:else} |
|||
<div>No apps found.</div> |
|||
{/if} |
|||
|
|||
<style> |
|||
.root { |
|||
margin-top: 10px; |
|||
} |
|||
.apps { |
|||
margin-top: var(--layout-m); |
|||
.appList { |
|||
display: grid; |
|||
grid-gap: 50px; |
|||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); |
|||
grid-gap: var(--layout-s); |
|||
justify-content: start; |
|||
} |
|||
</style> |
|||
|
|||
@ -1,15 +0,0 @@ |
|||
<script> |
|||
import { Button, Modal } from "@budibase/bbui" |
|||
import BuilderSettingsModal from "./BuilderSettingsModal.svelte" |
|||
|
|||
let modal |
|||
</script> |
|||
|
|||
<div> |
|||
<Button primary quiet icon="Settings" text on:click={modal.show}> |
|||
Settings |
|||
</Button> |
|||
</div> |
|||
<Modal bind:this={modal} width="30%"> |
|||
<BuilderSettingsModal /> |
|||
</Modal> |
|||
@ -1,6 +0,0 @@ |
|||
<script> |
|||
import { Button } from "@budibase/bbui" |
|||
import { auth } from "stores/backend" |
|||
</script> |
|||
|
|||
<Button primary quiet text icon="LogOut" on:click={auth.logout}>Log Out</Button> |
|||
@ -1,29 +0,0 @@ |
|||
<script> |
|||
import { onMount } from "svelte" |
|||
import { goto } from "@roxi/routify" |
|||
import { |
|||
SideNavigation as Navigation, |
|||
SideNavigationItem as Item, |
|||
} from "@budibase/bbui" |
|||
import { admin } from "stores/portal" |
|||
import LoginForm from "components/login/LoginForm.svelte" |
|||
import BuilderSettingsButton from "components/start/BuilderSettingsButton.svelte" |
|||
import LogoutButton from "components/start/LogoutButton.svelte" |
|||
import Logo from "/assets/budibase-logo.svg" |
|||
import api from "builderStore/api" |
|||
|
|||
let checklist |
|||
|
|||
onMount(async () => { |
|||
await admin.init() |
|||
if (!$admin?.checklist?.adminUser) { |
|||
$goto("./admin") |
|||
} else { |
|||
$goto("./portal") |
|||
} |
|||
}) |
|||
</script> |
|||
|
|||
{#if $admin.checklist} |
|||
<slot /> |
|||
{/if} |
|||
@ -1,69 +0,0 @@ |
|||
<script> |
|||
import { |
|||
Button, |
|||
Heading, |
|||
Label, |
|||
notifications, |
|||
Layout, |
|||
Input, |
|||
Body, |
|||
} from "@budibase/bbui" |
|||
import { goto } from "@roxi/routify" |
|||
import { onMount } from "svelte" |
|||
import api from "builderStore/api" |
|||
|
|||
let adminUser = {} |
|||
|
|||
async function save() { |
|||
try { |
|||
// Save the admin user |
|||
const response = await api.post(`/api/admin/users/init`, adminUser) |
|||
|
|||
const json = await response.json() |
|||
if (response.status !== 200) throw new Error(json.message) |
|||
notifications.success(`Admin user created.`) |
|||
$goto("../portal") |
|||
} catch (err) { |
|||
notifications.error(`Failed to create admin user.`) |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<section> |
|||
<div class="container"> |
|||
<header> |
|||
<Heading size="M">Create an admin user</Heading> |
|||
<Body size="S">The admin user has access to everything in budibase.</Body> |
|||
</header> |
|||
<div class="config-form"> |
|||
<Layout gap="S"> |
|||
<Input label="email" bind:value={adminUser.email} /> |
|||
<Input |
|||
label="password" |
|||
type="password" |
|||
bind:value={adminUser.password} |
|||
/> |
|||
<Button cta on:click={save}>Create super admin user</Button> |
|||
</Layout> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
|
|||
<style> |
|||
section { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
height: 100%; |
|||
} |
|||
|
|||
header { |
|||
text-align: center; |
|||
width: 80%; |
|||
margin: 0 auto; |
|||
} |
|||
|
|||
.config-form { |
|||
margin-bottom: 42px; |
|||
} |
|||
</style> |
|||
@ -1,116 +1,33 @@ |
|||
<script> |
|||
import { |
|||
SideNavigation as Navigation, |
|||
SideNavigationItem as Item, |
|||
} from "@budibase/bbui" |
|||
import { onMount } from "svelte" |
|||
import { goto } from "@roxi/routify" |
|||
import { auth } from "stores/backend" |
|||
import LoginForm from "components/login/LoginForm.svelte" |
|||
import BuilderSettingsButton from "components/start/BuilderSettingsButton.svelte" |
|||
import LogoutButton from "components/start/LogoutButton.svelte" |
|||
import Logo from "/assets/budibase-logo.svg" |
|||
import { admin } from "stores/portal" |
|||
|
|||
let modal |
|||
</script> |
|||
|
|||
{#if $auth} |
|||
{#if $auth.user} |
|||
<div class="root"> |
|||
<div class="ui-nav"> |
|||
<div class="home-logo"> |
|||
<img src={Logo} alt="Budibase icon" /> |
|||
</div> |
|||
<div class="nav-section"> |
|||
<div class="nav-top"> |
|||
<Navigation> |
|||
<Item href="/builder/" icon="Apps" selected>Apps</Item> |
|||
<Item external href="https://portal.budi.live/" icon="Servers"> |
|||
Hosting |
|||
</Item> |
|||
<Item external href="https://docs.budibase.com/" icon="Book"> |
|||
Documentation |
|||
</Item> |
|||
<Item |
|||
external |
|||
href="https://github.com/Budibase/budibase/discussions" |
|||
icon="PeopleGroup" |
|||
> |
|||
Community |
|||
</Item> |
|||
<Item |
|||
external |
|||
href="https://github.com/Budibase/budibase/issues/new/choose" |
|||
icon="Bug" |
|||
> |
|||
Raise an issue |
|||
</Item> |
|||
</Navigation> |
|||
</div> |
|||
<div class="nav-bottom"> |
|||
<BuilderSettingsButton /> |
|||
<LogoutButton /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="main"> |
|||
<slot /> |
|||
</div> |
|||
</div> |
|||
{:else} |
|||
<section class="login"> |
|||
<LoginForm /> |
|||
</section> |
|||
{/if} |
|||
{/if} |
|||
|
|||
<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%; |
|||
} |
|||
let loaded = false |
|||
$: hasAdminUser = !!$admin?.checklist?.adminUser |
|||
|
|||
.main { |
|||
grid-column: 2; |
|||
overflow: auto; |
|||
} |
|||
onMount(async () => { |
|||
await admin.init() |
|||
await auth.checkAuth() |
|||
loaded = true |
|||
}) |
|||
|
|||
.ui-nav { |
|||
grid-column: 1; |
|||
background-color: var(--background); |
|||
padding: 20px; |
|||
display: flex; |
|||
flex-direction: column; |
|||
border-right: var(--border-light); |
|||
// Force creation of an admin user if one doesn't exist |
|||
$: { |
|||
if (loaded && !hasAdminUser) { |
|||
$goto("./admin") |
|||
} |
|||
} |
|||
|
|||
.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%; |
|||
// Redirect to log in at any time if the user isn't authenticated |
|||
$: { |
|||
if (loaded && hasAdminUser && !$auth.user) { |
|||
$goto("./auth/login") |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
.nav-bottom :global(> *) { |
|||
margin-top: 5px; |
|||
} |
|||
</style> |
|||
{#if loaded} |
|||
<slot /> |
|||
{/if} |
|||
|
|||
@ -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> |
|||
@ -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 /> |
|||
@ -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> |
|||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
@ -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> |
|||
@ -0,0 +1,27 @@ |
|||
import { writable } from "svelte/store" |
|||
import { get } from "builderStore/api" |
|||
|
|||
export function createAppStore() { |
|||
const store = writable([]) |
|||
|
|||
async function load() { |
|||
try { |
|||
const res = await get("/api/applications") |
|||
const json = await res.json() |
|||
if (res.ok && Array.isArray(json)) { |
|||
store.set(json) |
|||
} else { |
|||
store.set([]) |
|||
} |
|||
} catch (error) { |
|||
store.set([]) |
|||
} |
|||
} |
|||
|
|||
return { |
|||
subscribe: store.subscribe, |
|||
load, |
|||
} |
|||
} |
|||
|
|||
export const apps = createAppStore() |
|||
@ -1,2 +1,3 @@ |
|||
export { organisation } from "./organisation" |
|||
export { admin } from "./admin" |
|||
export { apps } from "./apps" |
|||
|
|||
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
Loading…
Reference in new issue