forked from tsai/budibase
92 changed files with 679 additions and 3762 deletions
@ -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> |
||||
|
|||||
@ -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> |
<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</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-template-columns: repeat(auto-fill, minmax(280px, 1fr)); |
grid-gap: 50px; |
||||
grid-gap: var(--layout-s); |
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); |
||||
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> |
|
||||
@ -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} |
|
||||
@ -0,0 +1,27 @@ |
|||||
|
<script> |
||||
|
import { onMount } from "svelte" |
||||
|
import { goto } from "@roxi/routify" |
||||
|
import { auth } from "stores/backend" |
||||
|
import { admin } from "stores/portal" |
||||
|
|
||||
|
let checked = false |
||||
|
|
||||
|
onMount(async () => { |
||||
|
await admin.init() |
||||
|
await auth.checkAuth() |
||||
|
if (!$admin?.checklist?.adminUser) { |
||||
|
$goto("./admin") |
||||
|
} |
||||
|
checked = true |
||||
|
}) |
||||
|
|
||||
|
$: { |
||||
|
if (checked && !$auth.user) { |
||||
|
$goto("./auth/login") |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
{#if checked} |
||||
|
<slot /> |
||||
|
{/if} |
||||
@ -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,4 @@ |
|||||
|
<script> |
||||
|
import { goto } from "@roxi/routify" |
||||
|
$goto("../portal") |
||||
|
</script> |
||||
@ -0,0 +1,4 @@ |
|||||
|
<script> |
||||
|
import { goto } from "@roxi/routify" |
||||
|
$goto("./portal") |
||||
|
</script> |
||||
@ -0,0 +1,159 @@ |
|||||
|
<script> |
||||
|
import { isActive, goto } from "@roxi/routify" |
||||
|
import { onMount } from "svelte" |
||||
|
import { |
||||
|
Icon, |
||||
|
Avatar, |
||||
|
Search, |
||||
|
Layout, |
||||
|
SideNavigation as Navigation, |
||||
|
SideNavigationItem as Item, |
||||
|
ActionMenu, |
||||
|
MenuItem, |
||||
|
Modal, |
||||
|
} from "@budibase/bbui" |
||||
|
import ConfigChecklist from "components/common/ConfigChecklist.svelte" |
||||
|
import { organisation, apps } from "stores/portal" |
||||
|
import { auth } from "stores/backend" |
||||
|
import BuilderSettingsModal from "components/start/BuilderSettingsModal.svelte" |
||||
|
|
||||
|
organisation.init() |
||||
|
apps.load() |
||||
|
|
||||
|
let orgName |
||||
|
let orgLogo |
||||
|
let user |
||||
|
let oldSettingsModal |
||||
|
|
||||
|
async function getInfo() { |
||||
|
// fetch orgInfo |
||||
|
orgName = "ACME Inc." |
||||
|
orgLogo = "https://via.placeholder.com/150" |
||||
|
user = { name: "John Doe" } |
||||
|
} |
||||
|
|
||||
|
onMount(getInfo) |
||||
|
|
||||
|
let menu = [ |
||||
|
{ title: "Apps", href: "/app/portal/apps" }, |
||||
|
{ title: "Drafts", href: "/app/portal/drafts" }, |
||||
|
{ title: "Users", href: "/app/portal/users", heading: "Manage" }, |
||||
|
{ title: "Groups", href: "/app/portal/groups" }, |
||||
|
{ title: "Auth", href: "/app/portal/oauth" }, |
||||
|
{ title: "Email", href: "/app/portal/email" }, |
||||
|
{ |
||||
|
title: "General", |
||||
|
href: "/app/portal/settings/general", |
||||
|
heading: "Settings", |
||||
|
}, |
||||
|
{ title: "Theming", href: "/app/portal/theming" }, |
||||
|
{ title: "Account", href: "/app/portal/account" }, |
||||
|
] |
||||
|
</script> |
||||
|
|
||||
|
<div class="container"> |
||||
|
<div class="nav"> |
||||
|
<Layout paddingX="L" paddingY="L"> |
||||
|
<div class="branding"> |
||||
|
<div class="name" on:click={() => $goto("./apps")}> |
||||
|
<img |
||||
|
src={$organisation?.logoUrl || "https://i.imgur.com/ZKyklgF.png"} |
||||
|
alt="Logotype" |
||||
|
/> |
||||
|
<span>{$organisation?.company || "Budibase"}</span> |
||||
|
</div> |
||||
|
<div class="onboarding"> |
||||
|
<ConfigChecklist /> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="menu"> |
||||
|
<Navigation> |
||||
|
{#each menu as { title, href, heading }} |
||||
|
<Item selected={$isActive(href)} {href} {heading}>{title}</Item> |
||||
|
{/each} |
||||
|
</Navigation> |
||||
|
</div> |
||||
|
</Layout> |
||||
|
</div> |
||||
|
<div class="main"> |
||||
|
<div class="toolbar"> |
||||
|
<Search placeholder="Global search" /> |
||||
|
<ActionMenu align="right"> |
||||
|
<div slot="control" class="avatar"> |
||||
|
<Avatar size="M" name="John Doe" /> |
||||
|
<Icon size="XL" name="ChevronDown" /> |
||||
|
</div> |
||||
|
<MenuItem icon="Settings" on:click={oldSettingsModal.show}> |
||||
|
Old settings |
||||
|
</MenuItem> |
||||
|
<MenuItem icon="LogOut" on:click={auth.logout}>Log out</MenuItem> |
||||
|
</ActionMenu> |
||||
|
</div> |
||||
|
<div> |
||||
|
<slot /> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<Modal bind:this={oldSettingsModal} width="30%"> |
||||
|
<BuilderSettingsModal /> |
||||
|
</Modal> |
||||
|
|
||||
|
<style> |
||||
|
.container { |
||||
|
min-height: 100vh; |
||||
|
display: grid; |
||||
|
grid-template-columns: 250px 1fr; |
||||
|
} |
||||
|
.nav { |
||||
|
background: var(--background); |
||||
|
border-right: var(--border-light); |
||||
|
} |
||||
|
.main { |
||||
|
display: grid; |
||||
|
grid-template-rows: auto 1fr; |
||||
|
} |
||||
|
.branding { |
||||
|
display: grid; |
||||
|
grid-gap: var(--spacing-s); |
||||
|
grid-template-columns: auto auto; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
} |
||||
|
.name { |
||||
|
display: grid; |
||||
|
grid-template-columns: auto auto; |
||||
|
grid-gap: var(--spacing-m); |
||||
|
align-items: center; |
||||
|
} |
||||
|
.name:hover { |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
.avatar { |
||||
|
display: grid; |
||||
|
grid-template-columns: auto auto; |
||||
|
place-items: center; |
||||
|
grid-gap: var(--spacing-xs); |
||||
|
} |
||||
|
.avatar:hover { |
||||
|
cursor: pointer; |
||||
|
filter: brightness(110%); |
||||
|
} |
||||
|
.toolbar { |
||||
|
background: var(--background); |
||||
|
border-bottom: var(--border-light); |
||||
|
display: grid; |
||||
|
grid-template-columns: 250px auto; |
||||
|
justify-content: space-between; |
||||
|
padding: var(--spacing-m) calc(var(--spacing-xl) * 2); |
||||
|
align-items: center; |
||||
|
} |
||||
|
img { |
||||
|
width: 28px; |
||||
|
height: 28px; |
||||
|
} |
||||
|
span { |
||||
|
overflow: hidden; |
||||
|
text-overflow: ellipsis; |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
</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 |
||||
|
{/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,108 +0,0 @@ |
|||||
<script> |
|
||||
import { |
|
||||
SideNavigation as Navigation, |
|
||||
SideNavigationItem as Item, |
|
||||
} from "@budibase/bbui" |
|
||||
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" |
|
||||
|
|
||||
let modal |
|
||||
</script> |
|
||||
|
|
||||
<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> |
|
||||
|
|
||||
<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> |
|
||||
@ -1,123 +0,0 @@ |
|||||
<script> |
|
||||
import api from "builderStore/api" |
|
||||
import AppList from "components/start/AppList.svelte" |
|
||||
import { get } from "builderStore/api" |
|
||||
import CreateAppModal from "components/start/CreateAppModal.svelte" |
|
||||
import { Button, Heading, Modal, ButtonGroup } from "@budibase/bbui" |
|
||||
import TemplateList from "components/start/TemplateList.svelte" |
|
||||
import analytics from "analytics" |
|
||||
import Banner from "/assets/orange-landscape.png" |
|
||||
|
|
||||
let hasKey |
|
||||
let template |
|
||||
let modal |
|
||||
|
|
||||
async function getApps() { |
|
||||
const res = await get("/api/applications") |
|
||||
const json = await res.json() |
|
||||
|
|
||||
if (res.ok) { |
|
||||
return json |
|
||||
} else { |
|
||||
throw new Error(json) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
async function fetchKeys() { |
|
||||
const response = await api.get(`/api/keys/`) |
|
||||
return await response.json() |
|
||||
} |
|
||||
|
|
||||
async function checkIfKeysAndApps() { |
|
||||
const keys = await fetchKeys() |
|
||||
const apps = await getApps() |
|
||||
if (keys.userId) { |
|
||||
hasKey = true |
|
||||
analytics.identify(keys.userId) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
function selectTemplate(newTemplate) { |
|
||||
template = newTemplate |
|
||||
modal.show() |
|
||||
} |
|
||||
|
|
||||
function initiateAppImport() { |
|
||||
template = { fromFile: true } |
|
||||
modal.show() |
|
||||
} |
|
||||
|
|
||||
function closeModal() { |
|
||||
template = null |
|
||||
modal.hide() |
|
||||
} |
|
||||
|
|
||||
checkIfKeysAndApps() |
|
||||
</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 +1,4 @@ |
|||||
Index route |
<script> |
||||
|
import { goto } from "@roxi/routify" |
||||
|
$goto("./app") |
||||
|
</script> |
||||
|
|||||
@ -1,153 +0,0 @@ |
|||||
<script> |
|
||||
import { isActive, url, goto } from "@roxi/routify" |
|
||||
import { onMount } from "svelte" |
|
||||
import { |
|
||||
ActionMenu, |
|
||||
Checkbox, |
|
||||
MenuItem, |
|
||||
Icon, |
|
||||
Heading, |
|
||||
Avatar, |
|
||||
Search, |
|
||||
Layout, |
|
||||
ProgressCircle, |
|
||||
SideNavigation as Navigation, |
|
||||
SideNavigationItem as Item, |
|
||||
} from "@budibase/bbui" |
|
||||
import LoginForm from "components/login/LoginForm.svelte" |
|
||||
import api from "builderStore/api" |
|
||||
import ConfigChecklist from "components/common/ConfigChecklist.svelte" |
|
||||
import { auth } from "stores/backend" |
|
||||
import { organisation, admin } from "stores/portal" |
|
||||
|
|
||||
organisation.init() |
|
||||
|
|
||||
let orgName |
|
||||
let orgLogo |
|
||||
let user |
|
||||
|
|
||||
async function getInfo() { |
|
||||
// fetch orgInfo |
|
||||
orgName = "ACME Inc." |
|
||||
orgLogo = "https://via.placeholder.com/150" |
|
||||
|
|
||||
user = { name: "John Doe" } |
|
||||
} |
|
||||
|
|
||||
onMount(getInfo) |
|
||||
|
|
||||
let menu = [ |
|
||||
{ title: "Apps", href: "/portal/apps" }, |
|
||||
{ title: "Drafts", href: "/portal/drafts" }, |
|
||||
{ title: "Users", href: "/portal/users", heading: "Manage" }, |
|
||||
{ title: "Groups", href: "/portal/groups" }, |
|
||||
{ title: "Auth", href: "/portal/oauth" }, |
|
||||
{ title: "Email", href: "/portal/email" }, |
|
||||
{ title: "General", href: "/portal/settings/general", heading: "Settings" }, |
|
||||
{ title: "Theming", href: "/portal/theming" }, |
|
||||
{ title: "Account", href: "/portal/account" }, |
|
||||
] |
|
||||
</script> |
|
||||
|
|
||||
{#if $auth} |
|
||||
{#if $auth.user} |
|
||||
<div class="container"> |
|
||||
<div class="nav"> |
|
||||
<Layout paddingX="L" paddingY="L"> |
|
||||
<div class="branding"> |
|
||||
<div class="name"> |
|
||||
<img |
|
||||
src={$organisation?.logoUrl || |
|
||||
"https://i.imgur.com/ZKyklgF.png"} |
|
||||
alt="Logotype" |
|
||||
/> |
|
||||
<span>{$organisation?.company || "Budibase"}</span> |
|
||||
</div> |
|
||||
<div class="onboarding"> |
|
||||
<ConfigChecklist /> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="menu"> |
|
||||
<Navigation> |
|
||||
{#each menu as { title, href, heading }} |
|
||||
<Item selected={$isActive(href)} {href} {heading}>{title}</Item> |
|
||||
{/each} |
|
||||
</Navigation> |
|
||||
</div> |
|
||||
</Layout> |
|
||||
</div> |
|
||||
<div class="main"> |
|
||||
<div class="toolbar"> |
|
||||
<Search placeholder="Global search" /> |
|
||||
<div class="avatar"> |
|
||||
<Avatar size="M" name="John Doe" /> |
|
||||
<Icon size="XL" name="ChevronDown" /> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="content"> |
|
||||
<slot /> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
{:else} |
|
||||
<section class="login"> |
|
||||
<LoginForm /> |
|
||||
</section> |
|
||||
{/if} |
|
||||
{/if} |
|
||||
|
|
||||
<style> |
|
||||
.container { |
|
||||
min-height: 100vh; |
|
||||
display: grid; |
|
||||
grid-template-columns: 250px 1fr; |
|
||||
} |
|
||||
.nav { |
|
||||
background: var(--background); |
|
||||
border-right: var(--border-light); |
|
||||
} |
|
||||
.main { |
|
||||
display: grid; |
|
||||
grid-template-rows: auto 1fr; |
|
||||
} |
|
||||
.branding { |
|
||||
display: grid; |
|
||||
grid-gap: var(--spacing-s); |
|
||||
grid-template-columns: auto auto; |
|
||||
justify-content: space-between; |
|
||||
align-items: center; |
|
||||
} |
|
||||
.name { |
|
||||
display: grid; |
|
||||
grid-template-columns: auto auto; |
|
||||
grid-gap: var(--spacing-m); |
|
||||
align-items: center; |
|
||||
} |
|
||||
.avatar { |
|
||||
display: grid; |
|
||||
grid-template-columns: auto auto; |
|
||||
place-items: center; |
|
||||
grid-gap: var(--spacing-xs); |
|
||||
} |
|
||||
.avatar:hover { |
|
||||
cursor: pointer; |
|
||||
filter: brightness(110%); |
|
||||
} |
|
||||
.toolbar { |
|
||||
background: var(--background); |
|
||||
border-bottom: var(--border-light); |
|
||||
display: grid; |
|
||||
grid-template-columns: 250px auto; |
|
||||
justify-content: space-between; |
|
||||
padding: var(--spacing-m) calc(var(--spacing-xl) * 2); |
|
||||
} |
|
||||
img { |
|
||||
width: 28px; |
|
||||
height: 28px; |
|
||||
} |
|
||||
span { |
|
||||
overflow: hidden; |
|
||||
text-overflow: ellipsis; |
|
||||
font-weight: 500; |
|
||||
} |
|
||||
</style> |
|
||||
@ -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 { organisation } from "./organisation" |
||||
export { admin } from "./admin" |
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