templates working end to end

This commit is contained in:
Martin McKeaveney 2021-10-05 23:02:28 +01:00
parent f7667f7e47
commit 4c59087904
8 changed files with 109 additions and 1024 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

View File

@ -31,11 +31,14 @@
APP_NAME_REGEX, APP_NAME_REGEX,
"App name must be letters, numbers and spaces only" "App name must be letters, numbers and spaces only"
), ),
file: template ? mixed().required("Please choose a file to import") : null, file: template?.fromFile
? mixed().required("Please choose a file to import")
: null,
} }
let submitting = false let submitting = false
let valid = false let valid = false
$: checkValidity($values, validator) $: checkValidity($values, validator)
onMount(async () => { onMount(async () => {
@ -73,7 +76,7 @@
submitting = true submitting = true
// Check a template exists if we are important // Check a template exists if we are important
if (template && !$values.file) { if (template?.fromFile && !$values.file) {
$errors.file = "Please choose a file to import" $errors.file = "Please choose a file to import"
valid = false valid = false
submitting = false submitting = false
@ -134,12 +137,12 @@
</script> </script>
<ModalContent <ModalContent
title={template ? "Import app" : "Create app"} title={template?.fromFile ? "Import app" : "Create app"}
confirmText={template ? "Import app" : "Create app"} confirmText={template?.fromFile ? "Import app" : "Create app"}
onConfirm={createNewApp} onConfirm={createNewApp}
disabled={!valid} disabled={!valid}
> >
{#if template} {#if template?.fromFile}
<Dropzone <Dropzone
error={$touched.file && $errors.file} error={$touched.file && $errors.file}
gallery={false} gallery={false}

View File

@ -0,0 +1,68 @@
<script>
import { ModalContent, Body } from "@budibase/bbui"
import { auth } from "stores/portal"
import TemplateList from "./TemplateList.svelte"
import CreateAppModal from "./CreateAppModal.svelte"
import BudiWorldImage from "assets/budiworld.webp"
let step = 0
let template
function nextStep() {
step += 1
}
function selectTemplate(selectedTemplate) {
// get the template from the URL
template = selectedTemplate
nextStep()
}
</script>
{#if step === 0}
<ModalContent
title={`Welcome ${$auth.user?.firstName + "!" || "!"}`}
confirmText="Next"
onConfirm={() => {
nextStep()
return false
}}
>
<Body size="S">
<p>Welcome to Budibase!</p>
<p>
We're different from other development tools in some really special
ways, so we'd like to take you through them.
</p>
</Body>
<img
alt="Budibase community world"
class="budibase-world-image"
src={BudiWorldImage}
/>
</ModalContent>
{:else if step === 1}
<ModalContent
title={"Start from scratch or select a template"}
confirmText="Start from Scratch"
onConfirm={() => {
nextStep()
return false
}}
>
<Body size="S">
One of the coolest things about Budibase is that you don't have to start
from scratch. Simply select a template below, and get to work.
</Body>
<TemplateList onSelect={selectTemplate} />
</ModalContent>
{:else if step === 2}
<CreateAppModal {template} />
{/if}
<style>
.budibase-world-image {
height: 200px;
}
</style>

View File

@ -1,5 +1,5 @@
<script> <script>
import { Button, Heading, Body } from "@budibase/bbui" import { Heading, Body } from "@budibase/bbui"
import Spinner from "components/common/Spinner.svelte" import Spinner from "components/common/Spinner.svelte"
import api from "builderStore/api" import api from "builderStore/api"
@ -14,7 +14,6 @@
</script> </script>
<div class="root"> <div class="root">
<Heading size="M">Start With a Template</Heading>
{#await templatesPromise} {#await templatesPromise}
<div class="spinner-container"> <div class="spinner-container">
<Spinner size="30" /> <Spinner size="30" />
@ -23,16 +22,14 @@
<div class="templates"> <div class="templates">
{#each templates as template} {#each templates as template}
<div class="templates-card"> <div class="templates-card">
<Heading size="S">{template.name}</Heading> <img
<Body size="M" grey>{template.category}</Body> alt="template"
on:click={() => onSelect(template)}
src={template.image}
width="100%"
/>
<Heading size="XS">{template.name}</Heading>
<Body size="S" black>{template.description}</Body> <Body size="S" black>{template.description}</Body>
<div><img alt="template" src={template.image} width="100%" /></div>
<div class="card-footer">
<Button secondary on:click={() => onSelect(template)}>
Create
{template.name}
</Button>
</div>
</div> </div>
{/each} {/each}
</div> </div>
@ -44,19 +41,20 @@
<style> <style>
.templates { .templates {
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); width: 100%;
grid-template-columns: 1fr 1fr;
grid-gap: var(--layout-m); grid-gap: var(--layout-m);
justify-content: start; justify-content: start;
} }
.templates-card { .templates-card {
background-color: var(--background); background-color: var(--background);
padding: var(--spacing-xl);
border-radius: var(--border-radius-m);
border: var(--border-dark);
} }
.card-footer { img {
margin-top: var(--spacing-m); height: 135px;
width: 278px;
margin-bottom: var(--layout-m);
cursor: pointer;
} }
</style> </style>

View File

@ -159,8 +159,6 @@
cursor: pointer; cursor: pointer;
filter: brightness(110%); filter: brightness(110%);
} }
.group {
}
.app { .app {
display: grid; display: grid;
grid-template-columns: auto 1fr auto; grid-template-columns: auto 1fr auto;

View File

@ -16,6 +16,7 @@
} from "@budibase/bbui" } from "@budibase/bbui"
import CreateAppModal from "components/start/CreateAppModal.svelte" import CreateAppModal from "components/start/CreateAppModal.svelte"
import UpdateAppModal from "components/start/UpdateAppModal.svelte" import UpdateAppModal from "components/start/UpdateAppModal.svelte"
import OnboardingModal from "components/start/OnboardingModal.svelte"
import { del } from "builderStore/api" import { del } from "builderStore/api"
import { onMount } from "svelte" import { onMount } from "svelte"
import { apps, auth, admin } from "stores/portal" import { apps, auth, admin } from "stores/portal"
@ -34,6 +35,7 @@
let updatingModal let updatingModal
let deletionModal let deletionModal
let unpublishModal let unpublishModal
let onboardingModal
let creatingApp = false let creatingApp = false
let loaded = false let loaded = false
let searchTerm = "" let searchTerm = ""
@ -197,6 +199,9 @@
onMount(async () => { onMount(async () => {
await apps.load() await apps.load()
loaded = true loaded = true
// TODO: only show when they have not onboarded yet
// If apps = 0 or user isn't onboarded?
onboardingModal.show()
}) })
</script> </script>
@ -316,6 +321,9 @@
</ConfirmDialog> </ConfirmDialog>
<UpdateAppModal app={selectedApp} bind:this={updatingModal} /> <UpdateAppModal app={selectedApp} bind:this={updatingModal} />
<Modal width={"100px"} bind:this={onboardingModal}>
<OnboardingModal />
</Modal>
<style> <style>
.title, .title,

File diff suppressed because it is too large Load Diff

View File

@ -76,7 +76,7 @@ exports.getTemplateStream = async template => {
if (template.file) { if (template.file) {
return fs.createReadStream(template.file.path) return fs.createReadStream(template.file.path)
} else { } else {
const tmpPath = await exports.downloadTemplate(...template.key.split("/")) const tmpPath = await exports.downloadTemplate(template.key)
return fs.createReadStream(join(tmpPath, "db", "dump.txt")) return fs.createReadStream(join(tmpPath, "db", "dump.txt"))
} }
} }
@ -218,11 +218,13 @@ exports.deleteApp = async appId => {
* @param name * @param name
* @return {Promise<*>} * @return {Promise<*>}
*/ */
exports.downloadTemplate = async (type, name) => { exports.downloadTemplate = async path => {
const [type, name] = path.split("/")
const DEFAULT_TEMPLATES_BUCKET = const DEFAULT_TEMPLATES_BUCKET =
"prod-budi-templates.s3-eu-west-1.amazonaws.com" "prod-budi-templates.s3-eu-west-1.amazonaws.com"
const templateUrl = `https://${DEFAULT_TEMPLATES_BUCKET}/templates/${type}/${name}.tar.gz` const templateUrl = `https://${DEFAULT_TEMPLATES_BUCKET}/templates/${type}/${name}.tar.gz`
return downloadTarball(templateUrl, ObjectStoreBuckets.TEMPLATES, type) return downloadTarball(templateUrl, ObjectStoreBuckets.TEMPLATES, path)
} }
/** /**