Merge branch 'master' of github.com:Budibase/budibase into deployment

This commit is contained in:
Martin McKeaveney 2020-07-02 20:52:50 +01:00
commit 993eb71019
28 changed files with 260 additions and 228 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

View File

@ -55,7 +55,7 @@
] ]
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "^1.15.0", "@budibase/bbui": "^1.15.4",
"@budibase/client": "^0.0.32", "@budibase/client": "^0.0.32",
"@nx-js/compiler-util": "^2.0.0", "@nx-js/compiler-util": "^2.0.0",
"codemirror": "^5.51.0", "codemirror": "^5.51.0",
@ -87,6 +87,7 @@
"babel-jest": "^24.8.0", "babel-jest": "^24.8.0",
"browser-sync": "^2.26.7", "browser-sync": "^2.26.7",
"cypress": "^4.8.0", "cypress": "^4.8.0",
"eslint-plugin-cypress": "^2.11.1",
"http-proxy-middleware": "^0.19.1", "http-proxy-middleware": "^0.19.1",
"jest": "^24.8.0", "jest": "^24.8.0",
"ncp": "^2.0.0", "ncp": "^2.0.0",

View File

@ -1,11 +1,5 @@
/* Budibase Component Styles */ /* Budibase Component Styles */
.header {
font-size: 0.75rem;
color: var(--ink);
text-transform: uppercase;
margin-top: 1rem;
font-weight: 500;
}
.budibase__title { .budibase__title {
font-weight: 900; font-weight: 900;

View File

@ -29,7 +29,8 @@ import {
export const getStore = () => { export const getStore = () => {
const initial = { const initial = {
apps: [], apps: [],
appname: "", name: "",
description: "",
pages: DEFAULT_PAGES_OBJECT, pages: DEFAULT_PAGES_OBJECT,
mainUi: {}, mainUi: {},
unauthenticatedUi: {}, unauthenticatedUi: {},
@ -101,7 +102,8 @@ const setPackage = (store, initial) => async pkg => {
initial.libraries = pkg.application.componentLibraries initial.libraries = pkg.application.componentLibraries
initial.components = await fetchComponentLibDefinitions(pkg.application._id) initial.components = await fetchComponentLibDefinitions(pkg.application._id)
initial.appname = pkg.application.name initial.name = pkg.application.name
initial.description = pkg.application.description
initial.appId = pkg.application._id initial.appId = pkg.application._id
initial.pages = pkg.pages initial.pages = pkg.pages
initial.hasAppPackage = true initial.hasAppPackage = true

View File

@ -11,7 +11,7 @@
} }
$: currentAppInfo = { $: currentAppInfo = {
appname: $store.appname, name: $store.name,
} }
async function fetchUsers() { async function fetchUsers() {

View File

@ -1,15 +1,39 @@
<script> <script>
import { Input, TextArea, Button } from "@budibase/bbui" import { Input, TextArea, Button } from "@budibase/bbui"
import { store } from "builderStore"
import api from "builderStore/api"
import Title from "../TabTitle.svelte" import Title from "../TabTitle.svelte"
async function updateApplication(data) {
const response = await api.put(`/api/${$store.appId}`, data)
const app = await response.json()
store.update(state => {
state = {
...state,
...data,
}
return state
})
}
</script> </script>
<Title>General</Title> <Title>General</Title>
<div class="container"> <div class="container">
<div class="background"> <div class="background">
<Input thin edit placeholder="Enter your name" label="Name" /> <Input
on:save={e => updateApplication({ name: e.detail })}
thin
edit
value={$store.name}
label="Name" />
</div> </div>
<div class="background"> <div class="background">
<TextArea thin edit placeholder="Enter your name" label="Name" /> <TextArea
on:save={e => updateApplication({ description: e.detail })}
thin
edit
value={$store.description}
label="Name" />
</div> </div>
</div> </div>

View File

@ -25,7 +25,7 @@
<style> <style>
.apps { .apps {
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, 380px); grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
grid-gap: 20px 40px; grid-gap: 20px 40px;
justify-content: start; justify-content: start;
} }

View File

@ -63,7 +63,7 @@
<span class="icon"> <span class="icon">
<AppsIcon /> <AppsIcon />
</span> </span>
<h3>Create new web app</h3> <h3 class="header">Create new web app</h3>
</div> </div>
<Input <Input
name="name" name="name"
@ -90,7 +90,7 @@
<InfoIcon /> <InfoIcon />
How to get started How to get started
</a> </a>
<Button outline thin on:click={_onCancel}>Cancel</Button> <Button secondary thin on:click={_onCancel}>Cancel</Button>
<Button primary thin on:click={_onOkay}>Save</Button> <Button primary thin on:click={_onOkay}>Save</Button>
</div> </div>
<div class="close-button" on:click={_onCancel}> <div class="close-button" on:click={_onCancel}>
@ -125,10 +125,11 @@
align-items: center; align-items: center;
margin-bottom: 20px; margin-bottom: 20px;
} }
h3 { .header {
margin: 0; margin: 0;
font-size: 24px; font-size: 24px;
font-weight: bold; font-weight: 600;
font-family: inter;
} }
.icon { .icon {
display: grid; display: grid;

View File

@ -17,8 +17,9 @@
import { keyevents } from "../actions" import { keyevents } from "../actions"
export let value = "#3ec1d3ff" export let value = "#3ec1d3ff"
export let open = false; export let open = false
export let swatches = [] //TODO: Safe swatches - limit to 12. warn in console export let swatches = []
export let disableSwatches = false export let disableSwatches = false
export let format = "hexa" export let format = "hexa"
export let style = "" export let style = ""
@ -27,6 +28,7 @@
let colorPicker = null let colorPicker = null
let adder = null let adder = null
let swatchesSetFromLocalStore = false
let h = 0 let h = 0
let s = 0 let s = 0
@ -38,7 +40,14 @@
onMount(() => { onMount(() => {
if (!swatches.length > 0) { if (!swatches.length > 0) {
//Don't use locally stored recent colors if swatches have been passed as props //Don't use locally stored recent colors if swatches have been passed as props
getRecentColors() swatchesSetFromLocalStore = true
swatches = getRecentColors() || []
}
if (swatches.length > 12) {
console.warn(
`Colorpicker - ${swatches.length} swatches were provided. Only the first 12 swatches will be displayed.`
)
} }
if (colorPicker) { if (colorPicker) {
@ -53,24 +62,21 @@
function getRecentColors() { function getRecentColors() {
let colorStore = localStorage.getItem("cp:recent-colors") let colorStore = localStorage.getItem("cp:recent-colors")
if (colorStore) { if (colorStore) {
swatches = JSON.parse(colorStore) return JSON.parse(colorStore)
} }
} }
function handleEscape() { function handleEscape() {
if (open) { if (open) {
open = false; open = false
} }
} }
function setRecentColor(color) { function setRecentColors(color) {
if (swatches.length === 12) { const s = swatchesSetFromLocalStore
swatches.splice(0, 1) ? swatches
} : [...getRecentColors(), color]
if (!swatches.includes(color)) { localStorage.setItem("cp:recent-colors", JSON.stringify(s))
swatches = [...swatches, color]
localStorage.setItem("cp:recent-colors", JSON.stringify(swatches))
}
} }
function convertAndSetHSVA() { function convertAndSetHSVA() {
@ -134,8 +140,16 @@
function addSwatch() { function addSwatch() {
if (format) { if (format) {
if (swatches.length === 12) {
swatches.splice(0, 1)
}
if (!swatches.includes(value)) {
swatches = [...swatches, value]
setRecentColors(value)
}
dispatch("addswatch", value) dispatch("addswatch", value)
setRecentColor(value)
} }
} }
@ -143,7 +157,10 @@
let removedSwatch = swatches.splice(idx, 1) let removedSwatch = swatches.splice(idx, 1)
swatches = swatches swatches = swatches
dispatch("removeswatch", removedSwatch) dispatch("removeswatch", removedSwatch)
localStorage.setItem("cp:recent-colors", JSON.stringify(swatches)) if (swatchesSetFromLocalStore) {
//as could be a swatch not present in local storage
setRecentColors()
}
} }
function applySwatch(color) { function applySwatch(color) {
@ -159,14 +176,14 @@
$: border = v > 90 && s < 5 ? "1px dashed #dedada" : "" $: border = v > 90 && s < 5 ? "1px dashed #dedada" : ""
$: selectedColorStyle = buildStyle({ background: value, border }) $: selectedColorStyle = buildStyle({ background: value, border })
$: shrink = swatches.length > 0 $: hasSwatches = swatches.length > 0
</script> </script>
<Portal> <Portal>
<div <div
class="colorpicker-container" class="colorpicker-container"
use:keyevents={{"Escape": handleEscape}} use:keyevents={{ Escape: handleEscape }}
transition:fade transition:fade
bind:this={colorPicker} bind:this={colorPicker}
{style} {style}
@ -182,7 +199,10 @@
<div class="alpha-hue-panel"> <div class="alpha-hue-panel">
<div> <div>
<CheckedBackground borderRadius="50%" backgroundSize="8px"> <CheckedBackground borderRadius="50%" backgroundSize="8px">
<div class="selected-color" style={selectedColorStyle} /> <div
class="selected-color"
title={value}
style={selectedColorStyle} />
</CheckedBackground> </CheckedBackground>
</div> </div>
<div> <div>
@ -205,22 +225,24 @@
{#if !disableSwatches} {#if !disableSwatches}
<div transition:fade class="swatch-panel"> <div transition:fade class="swatch-panel">
{#if swatches.length > 0} {#if hasSwatches}
{#each swatches as color, idx} {#each swatches as color, idx}
{#if idx < 12}
<Swatch <Swatch
{color} {color}
on:click={() => applySwatch(color)} on:click={() => applySwatch(color)}
on:removeswatch={() => removeSwatch(idx)} /> on:removeswatch={() => removeSwatch(idx)} />
{/if}
{/each} {/each}
{/if} {/if}
{#if swatches.length !== 12} {#if swatches.length < 12}
<div <div
tabindex="0" tabindex="0"
use:keyevents={{"Enter": addSwatch}} use:keyevents={{ Enter: addSwatch }}
bind:this={adder} bind:this={adder}
transition:fade transition:fade
class="adder" class="adder"
class:shrink class:shrink={hasSwatches}
on:click={addSwatch}> on:click={addSwatch}>
<span>&plus;</span> <span>&plus;</span>
</div> </div>

View File

@ -38,7 +38,6 @@
} }
}) })
function openColorpicker(event) { function openColorpicker(event) {
if (colorPreview) { if (colorPreview) {
open = true open = true
@ -62,17 +61,17 @@
const spaceBelow = window.innerHeight - bottom const spaceBelow = window.innerHeight - bottom
const previewCenter = previewWidth / 2 const previewCenter = previewWidth / 2
let y, x; let y, x
if (spaceAbove > spaceBelow) { if (spaceAbove > spaceBelow) {
positionSide = "bottom" positionSide = "bottom"
y = (window.innerHeight - spaceAbove) y = window.innerHeight - spaceAbove
} else { } else {
positionSide = "top" positionSide = "top"
y = bottom y = bottom
} }
x = (left + previewCenter) - (220 / 2) x = left + previewCenter - 220 / 2
dimensions = { [positionSide]: y.toFixed(1), left: x.toFixed(1) } dimensions = { [positionSide]: y.toFixed(1), left: x.toFixed(1) }
} }
@ -87,7 +86,6 @@
[positionSide]: `${dimensions[positionSide]}px`, [positionSide]: `${dimensions[positionSide]}px`,
left: `${dimensions.left}px`, left: `${dimensions.left}px`,
}) })
</script> </script>
<svelte:window on:resize={debounce(calculateDimensions, 200)} /> <svelte:window on:resize={debounce(calculateDimensions, 200)} />
@ -99,6 +97,7 @@
bind:this={colorPreview} bind:this={colorPreview}
bind:clientHeight={previewHeight} bind:clientHeight={previewHeight}
bind:clientWidth={previewWidth} bind:clientWidth={previewWidth}
title={value}
class="color-preview" class="color-preview"
style={previewStyle} style={previewStyle}
on:click={openColorpicker} /> on:click={openColorpicker} />
@ -116,12 +115,14 @@
bind:pickerWidth bind:pickerWidth
bind:open bind:open
{swatches} {swatches}
{disableSwatches} {disableSwatches} />
/>
<div on:click|self={() => (open = false)} class="overlay" /> <div on:click|self={() => (open = false)} class="overlay" />
{/if} {/if}
{:else} {:else}
<div class="color-preview preview-error" style={errorPreviewStyle}> <div
class="color-preview preview-error"
title="Invalid Color"
style={errorPreviewStyle}>
<span>&times;</span> <span>&times;</span>
</div> </div>
{/if} {/if}

View File

@ -1,35 +1,35 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte"
export let target = document.body; export let target = document.body
let targetEl; let targetEl
let portal; let portal
let componentInstance; let componentInstance
onMount(() => { onMount(() => {
if (typeof target === "string") { if (typeof target === "string") {
targetEl = document.querySelector(target); targetEl = document.querySelector(target)
// Force exit // Force exit
if (targetEl === null) { if (targetEl === null) {
return () => {}; return () => {}
} }
} else if (target instanceof HTMLElement) { } else if (target instanceof HTMLElement) {
targetEl = target; targetEl = target
} else { } else {
throw new TypeError( throw new TypeError(
`Unknown target type: ${typeof target}. Allowed types: String (CSS selector), HTMLElement.` `Unknown target type: ${typeof target}. Allowed types: String (CSS selector), HTMLElement.`
); )
} }
portal = document.createElement("div"); portal = document.createElement("div")
targetEl.appendChild(portal); targetEl.appendChild(portal)
portal.appendChild(componentInstance); portal.appendChild(componentInstance)
return () => { return () => {
targetEl.removeChild(portal); targetEl.removeChild(portal)
}; }
}); })
</script> </script>
<div bind:this={componentInstance}> <div bind:this={componentInstance}>

View File

@ -14,8 +14,9 @@
<CheckedBackground borderRadius="6px"> <CheckedBackground borderRadius="6px">
<div <div
tabindex="0" tabindex="0"
use:keyevents={{"Enter": () => dispatch("click")}} use:keyevents={{ Enter: () => dispatch('click') }}
in:fade in:fade
title={color}
class="swatch" class="swatch"
style={`background: ${color};`} style={`background: ${color};`}
on:click|self on:click|self

View File

@ -3,7 +3,6 @@
import PropertyGroup from "./PropertyGroup.svelte" import PropertyGroup from "./PropertyGroup.svelte"
import FlatButtonGroup from "./FlatButtonGroup.svelte" import FlatButtonGroup from "./FlatButtonGroup.svelte"
export let panelDefinition = {} export let panelDefinition = {}
export let componentInstance = {} export let componentInstance = {}
export let componentDefinition = {} export let componentDefinition = {}
@ -34,7 +33,6 @@
] ]
$: propertyGroupNames = Object.keys(panelDefinition) $: propertyGroupNames = Object.keys(panelDefinition)
</script> </script>
<div class="design-view-container"> <div class="design-view-container">
@ -55,7 +53,7 @@
{componentDefinition} {componentDefinition}
{componentInstance} {componentInstance}
open={currentGroup === groupName} open={currentGroup === groupName}
on:open={() => currentGroup = groupName} /> on:open={() => (currentGroup = groupName)} />
{/each} {/each}
{:else} {:else}
<div class="no-design"> <div class="no-design">

View File

@ -13,13 +13,13 @@
let selectMenu let selectMenu
let icon let icon
let selectAnchor = null; let selectAnchor = null
let dimensions = { top: 0, bottom: 0, left: 0 } let dimensions = { top: 0, bottom: 0, left: 0 }
let positionSide = "top" let positionSide = "top"
let maxHeight = 0 let maxHeight = 0
let scrollTop = 0; let scrollTop = 0
let containerEl = null; let containerEl = null
const handleStyleBind = value => const handleStyleBind = value =>
!!styleBindingProperty ? { style: `${styleBindingProperty}: ${value}` } : {} !!styleBindingProperty ? { style: `${styleBindingProperty}: ${value}` } : {}
@ -32,7 +32,6 @@
return () => { return () => {
select.removeEventListener("keydown", handleEnter) select.removeEventListener("keydown", handleEnter)
} }
}) })
function handleEscape(e) { function handleEscape(e) {
@ -42,15 +41,19 @@
} }
function getDimensions() { function getDimensions() {
const { bottom, top: spaceAbove, left } = selectAnchor.getBoundingClientRect() const {
bottom,
top: spaceAbove,
left,
} = selectAnchor.getBoundingClientRect()
const spaceBelow = window.innerHeight - bottom const spaceBelow = window.innerHeight - bottom
let y; let y
if (spaceAbove > spaceBelow) { if (spaceAbove > spaceBelow) {
positionSide = "bottom" positionSide = "bottom"
maxHeight = spaceAbove - 20 maxHeight = spaceAbove - 20
y = (window.innerHeight - spaceAbove) y = window.innerHeight - spaceAbove
} else { } else {
positionSide = "top" positionSide = "top"
y = bottom y = bottom
@ -76,17 +79,17 @@
open = isOpen open = isOpen
} }
function handleClick(val) { function handleClick(val) {
value = val value = val
onChange(value) onChange(value)
toggleSelect(false)
} }
$: menuStyle = buildStyle({ $: menuStyle = buildStyle({
"max-height": `${maxHeight.toFixed(0)}px`, "max-height": `${maxHeight.toFixed(0)}px`,
"transform-origin": `center ${positionSide}`, "transform-origin": `center ${positionSide}`,
[positionSide]: `${dimensions[positionSide]}px`, [positionSide]: `${dimensions[positionSide]}px`,
"left": `${dimensions.left.toFixed(0)}px`, left: `${dimensions.left.toFixed(0)}px`,
}) })
$: isOptionsObject = options.every(o => typeof o === "object") $: isOptionsObject = options.every(o => typeof o === "object")
@ -108,7 +111,7 @@
bind:this={select} bind:this={select}
class="bb-select-container" class="bb-select-container"
on:click={() => toggleSelect(!open)}> on:click={() => toggleSelect(!open)}>
<div bind:this={selectAnchor} class="bb-select-anchor selected"> <div bind:this={selectAnchor} title={value} class="bb-select-anchor selected">
<span>{displayLabel}</span> <span>{displayLabel}</span>
<i bind:this={icon} class="ri-arrow-down-s-fill" /> <i bind:this={icon} class="ri-arrow-down-s-fill" />
</div> </div>
@ -174,6 +177,7 @@
background-color: var(--grey-2); background-color: var(--grey-2);
border-radius: 5px; border-radius: 5px;
align-items: center; align-items: center;
white-space: nowrap;
} }
.bb-select-anchor > span { .bb-select-anchor > span {

View File

@ -28,7 +28,7 @@
</script> </script>
<section> <section>
<Button purple wide on:click{newWorkflow}>Create New Workflow</Button> <Button purple wide on:click={newWorkflow}>Create New Workflow</Button>
<ul> <ul>
{#each $workflowStore.workflows as workflow} {#each $workflowStore.workflows as workflow}
<li <li

View File

@ -38,7 +38,9 @@
if (!activeTopNav) return if (!activeTopNav) return
store.update(state => { store.update(state => {
if (!state.previousTopNavPath) state.previousTopNavPath = {} if (!state.previousTopNavPath) state.previousTopNavPath = {}
state.previousTopNavPath[activeTopNav.path] = window.location.pathname.replace("/_builder", "") state.previousTopNavPath[
activeTopNav.path
] = window.location.pathname.replace("/_builder", "")
$goto(state.previousTopNavPath[path] || path) $goto(state.previousTopNavPath[path] || path)
return state return state
}) })

View File

@ -3,12 +3,13 @@ import { params } from "@sveltech/routify"
import { backendUiStore } from "builderStore" import { backendUiStore } from "builderStore"
if ($params.selectedModel) { if ($params.selectedModel) {
const model = $backendUiStore.models.find(m => m._id === $params.selectedModel) const model = $backendUiStore.models.find(
m => m._id === $params.selectedModel
)
if (model) { if (model) {
backendUiStore.actions.models.select(model) backendUiStore.actions.models.select(model)
} }
} }
</script> </script>
<slot /> <slot />

View File

@ -10,7 +10,11 @@
onMount(async () => { onMount(async () => {
// navigate to first model in list, if not already selected // navigate to first model in list, if not already selected
// and this is the final url (i.e. no selectedModel) // and this is the final url (i.e. no selectedModel)
if (!$leftover && $backendUiStore.models.length > 0 && (!$backendUiStore.selectedModel || !$backendUiStore.selectedModel._id)) { if (
!$leftover &&
$backendUiStore.models.length > 0 &&
(!$backendUiStore.selectedModel || !$backendUiStore.selectedModel._id)
) {
$goto(`./${$backendUiStore.models[0]._id}`) $goto(`./${$backendUiStore.models[0]._id}`)
} }
}) })

View File

@ -10,7 +10,11 @@
onMount(async () => { onMount(async () => {
// navigate to first model in list, if not already selected // navigate to first model in list, if not already selected
// and this is the final url (i.e. no selectedModel) // and this is the final url (i.e. no selectedModel)
if (!$leftover && $backendUiStore.models.length > 0 && (!$backendUiStore.selectedModel || !$backendUiStore.selectedModel._id)) { if (
!$leftover &&
$backendUiStore.models.length > 0 &&
(!$backendUiStore.selectedModel || !$backendUiStore.selectedModel._id)
) {
// this file routes as .../models/index, so, go up one. // this file routes as .../models/index, so, go up one.
$goto(`../${$backendUiStore.models[0]._id}`) $goto(`../${$backendUiStore.models[0]._id}`)
} }
@ -19,6 +23,4 @@
{#if $backendUiStore.models.length === 0} {#if $backendUiStore.models.length === 0}
Please create a model Please create a model
{:else} {:else}Please select a model{/if}
Please select a model
{/if}

View File

@ -14,7 +14,9 @@
if ($leftover) { if ($leftover) {
// Get the correct screen children. // Get the correct screen children.
const screenChildren = $store.pages[$params.page]._screens.find( const screenChildren = $store.pages[$params.page]._screens.find(
screen => screen.props._instanceName === $params.screen screen =>
(screen.props._instanceName === $params.screen
|| screen.props._instanceName === decodeURIComponent($params.screen))
).props._children ).props._children
findComponent(componentIds, screenChildren) findComponent(componentIds, screenChildren)
} }

View File

@ -40,10 +40,7 @@
<div class="nav-section"> <div class="nav-section">
<div class="nav-section-title">Contact</div> <div class="nav-section-title">Contact</div>
<Link <Link icon={ContributionIcon} title="Contribute" href="/" />
icon={ContributionIcon}
title="Contribute to our product"
href="/" />
<Link icon={BugIcon} title="Report bug" href="/" /> <Link icon={BugIcon} title="Report bug" href="/" />
<Link icon={EmailIcon} title="Email" href="/" /> <Link icon={EmailIcon} title="Email" href="/" />
<Link icon={TwitterIcon} title="Twitter" href="/" /> <Link icon={TwitterIcon} title="Twitter" href="/" />
@ -59,7 +56,7 @@
<style> <style>
.root { .root {
display: grid; display: grid;
grid-template-columns: 300px 1fr; grid-template-columns: 260px 1fr;
height: 100%; height: 100%;
width: 100%; width: 100%;
background: var(--grey-1); background: var(--grey-1);
@ -67,6 +64,7 @@
.main { .main {
grid-column: 2; grid-column: 2;
overflow: auto;
} }
.ui-nav { .ui-nav {
@ -75,7 +73,6 @@
padding: 20px; padding: 20px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
border-right: 1px solid var(--grey-4);
} }
.home-logo { .home-logo {

View File

@ -8,6 +8,7 @@
import { get } from "builderStore/api" import { get } from "builderStore/api"
import Spinner from "components/common/Spinner.svelte" import Spinner from "components/common/Spinner.svelte"
import CreateAppModal from "components/start/CreateAppModal.svelte" import CreateAppModal from "components/start/CreateAppModal.svelte"
import { Button } from "@budibase/bbui"
let promise = getApps() let promise = getApps()
@ -43,21 +44,17 @@
} }
</script> </script>
<div class="welcome">Welcome to Budibase</div> <div class="header">
<div class="welcome">Welcome to the Budibase Beta</div>
<Button purple large on:click={showCreateAppModal}>Create New Web App</Button>
</div>
<div class="banner"> <div class="banner">
<img src="/_builder/assets/rocket.jpg" alt="rocket" /> <img src="/_builder/assets/orange-landscape.png" alt="rocket" />
<div class="banner-content"> <div class="banner-content">
Every accomplishment starts with a decision to try. Every accomplishment starts with a decision to try.
</div> </div>
</div> </div>
<div class="app-section-header">
<div class="app-section-title">Your Web Apps</div>
<button class="banner-button" type="button" on:click={showCreateAppModal}>
<i class="ri-add-circle-fill" />
Create New Web App
</button>
</div>
{#await promise} {#await promise}
<div class="spinner-container"> <div class="spinner-container">
@ -70,11 +67,17 @@
{/await} {/await}
<style> <style>
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin: 40px 80px 0px 80px;
}
.welcome { .welcome {
font-size: 42px; font-size: 42px;
color: var(--ink); color: var(--ink);
font-weight: 700; font-weight: 700;
margin: 40px 0px 0px 80px;
} }
.banner { .banner {
@ -108,48 +111,4 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.banner-button {
background-color: var(--ink);
color: var(--white);
padding: 12px 24px;
border-radius: 5px;
border: var(--ink) 1px solid;
font-size: 16px;
font-weight: 400;
box-sizing: border-box;
align-items: center;
display: flex;
cursor: pointer;
transition: all 0.2s ease 0s;
overflow: hidden;
outline: none;
user-select: none;
white-space: nowrap;
}
.ri-add-circle-fill {
margin-right: 4px;
font-size: 24px;
}
.banner-button:hover {
background-color: var(--white);
color: var(--ink);
border: var(--grey-4) 1px solid;
}
.app-section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin: 40px 80px 0px 80px;
}
.app-section-title {
font-size: 20px;
color: var(--ink);
font-weight: 600;
margin-bottom: 20px;
}
</style> </style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 MiB

View File

@ -90,6 +90,22 @@ exports.create = async function(ctx) {
ctx.message = `Application ${ctx.request.body.name} created successfully` ctx.message = `Application ${ctx.request.body.name} created successfully`
} }
exports.update = async function(ctx) {
const clientId = await lookupClientId(ctx.params.applicationId)
const db = new CouchDB(ClientDb.name(clientId))
const application = await db.get(ctx.params.applicationId)
const data = ctx.request.body
const newData = { ...application, ...data }
const response = await db.put(newData)
data._rev = response.rev
ctx.status = 200
ctx.message = `Application ${application.name} updated successfully.`
ctx.body = response
}
const createEmptyAppPackage = async (ctx, app) => { const createEmptyAppPackage = async (ctx, app) => {
const templateFolder = resolve( const templateFolder = resolve(
__dirname, __dirname,

View File

@ -12,6 +12,7 @@ router
authorized(BUILDER), authorized(BUILDER),
controller.fetchAppPackage controller.fetchAppPackage
) )
.put("/api/:applicationId", authorized(BUILDER), controller.update)
.post("/api/applications", authorized(BUILDER), controller.create) .post("/api/applications", authorized(BUILDER), controller.create)
module.exports = router module.exports = router