1
0
Fork 0
mirror of synced 2024-09-21 11:53:49 +12:00
budibase/packages/builder/src/components/start/CreateAppModal.svelte

247 lines
6.4 KiB
Svelte
Raw Normal View History

<script>
import { writable, get as svelteGet } from "svelte/store"
2021-04-20 21:39:09 +12:00
import { notifications, Heading, Button } from "@budibase/bbui"
import { store, automationStore, hostingStore } from "builderStore"
2020-08-04 01:59:50 +12:00
import { string, object } from "yup"
2020-08-04 02:26:28 +12:00
import api, { get } from "builderStore/api"
2020-05-27 22:54:53 +12:00
import Spinner from "components/common/Spinner.svelte"
2021-03-24 02:01:28 +13:00
import { Info, User } from "./Steps"
2020-07-31 20:46:23 +12:00
import Indicator from "./Indicator.svelte"
2021-03-18 00:40:24 +13:00
import { goto } from "@roxi/routify"
2020-05-27 22:54:53 +12:00
import { fade } from "svelte/transition"
import { post } from "builderStore/api"
import analytics from "analytics"
2021-01-07 06:28:22 +13:00
import { onMount } from "svelte"
import Logo from "/assets/bb-logo.svg"
import { capitalise } from "../../helpers"
2020-05-27 22:54:53 +12:00
2020-09-26 01:47:42 +12:00
export let template
2020-05-27 22:54:53 +12:00
const currentStep = writable(0)
const values = writable({ roleId: "ADMIN" })
const errors = writable({})
const touched = writable({})
const steps = [Info, User]
let validators = [
{
applicationName: string().required("Your application must have a name"),
},
{
roleId: string()
.nullable()
2021-04-29 02:53:21 +12:00
.required("You need to select a role for this app"),
},
]
2020-08-04 01:59:50 +12:00
let submitting = false
let valid = false
$: checkValidity($values, validators[$currentStep])
onMount(async () => {
const hostingInfo = await hostingStore.actions.fetch()
2021-01-07 10:25:52 +13:00
if (hostingInfo.type === "self") {
await hostingStore.actions.fetchDeployedApps()
const existingAppNames = svelteGet(hostingStore).deployedAppNames
validators[0].applicationName = string()
2021-01-15 06:02:05 +13:00
.required("Your application must have a name.")
2021-02-24 07:37:37 +13:00
.test(
"non-existing-app-name",
"App with same name already exists. Please try another app name.",
value =>
2021-02-25 00:57:53 +13:00
!existingAppNames.some(
appName => appName.toLowerCase() === value.toLowerCase()
2021-02-24 07:37:37 +13:00
)
)
}
})
2020-08-04 01:59:50 +12:00
const checkValidity = async (values, validator) => {
const obj = object().shape(validator)
Object.keys(validator).forEach(key => ($errors[key] = null))
2020-08-04 01:59:50 +12:00
try {
await obj.validate(values, { abortEarly: false })
} catch (validationErrors) {
validationErrors.inner.forEach(error => {
$errors[error.path] = capitalise(error.message)
})
2020-08-04 01:59:50 +12:00
}
valid = await obj.isValid(values)
2020-08-04 01:59:50 +12:00
}
async function createNewApp() {
2020-08-04 01:59:50 +12:00
submitting = true
try {
2021-03-16 07:32:20 +13:00
// Create form data to create app
let data = new FormData()
data.append("name", $values.applicationName)
2021-03-16 07:32:20 +13:00
data.append("useTemplate", template != null)
if (template) {
data.append("templateName", template.name)
data.append("templateKey", template.key)
data.append("templateFile", template.file)
}
2020-08-04 02:26:28 +12:00
// Create App
2021-03-16 07:32:20 +13:00
const appResp = await post("/api/applications", data, {})
2020-08-04 02:26:28 +12:00
const appJson = await appResp.json()
2021-02-24 07:37:37 +13:00
if (!appResp.ok) {
throw new Error(appJson.message)
}
analytics.captureEvent("App Created", {
name: $values.applicationName,
2020-08-04 02:26:28 +12:00
appId: appJson._id,
2020-09-26 01:47:42 +12:00
template,
2020-08-04 01:59:50 +12:00
})
2020-08-04 02:26:28 +12:00
// Select Correct Application/DB in prep for creating user
2020-11-20 05:56:23 +13:00
const applicationPkg = await get(
`/api/applications/${appJson._id}/appPackage`
)
2020-08-04 02:26:28 +12:00
const pkg = await applicationPkg.json()
if (applicationPkg.ok) {
2020-11-05 06:09:45 +13:00
await store.actions.initialise(pkg)
2020-11-25 07:11:34 +13:00
await automationStore.actions.fetch()
2020-08-04 02:26:28 +12:00
} else {
throw new Error(pkg)
}
// Create user
const user = {
roleId: $values.roleId,
2020-08-04 02:26:28 +12:00
}
const userResp = await api.post(`/api/users/metadata/self`, user)
await userResp.json()
$goto(`./${appJson._id}`)
2020-08-04 01:59:50 +12:00
} catch (error) {
console.error(error)
notifications.error(error)
2021-02-24 07:37:37 +13:00
submitting = false
2020-05-27 21:44:15 +12:00
}
}
2020-05-27 02:25:37 +12:00
</script>
2020-10-08 21:35:11 +13:00
<div class="container">
<div class="sidebar">
2021-04-20 21:39:09 +12:00
<img src={Logo} alt="budibase icon" />
<div class="steps">
{#each steps as component, i}
<Indicator
active={$currentStep === i}
done={i < $currentStep}
step={i + 1} />
{/each}
</div>
2020-10-08 21:35:11 +13:00
</div>
<div class="body">
<div class="heading">
2021-04-20 21:39:09 +12:00
<Heading h2 l>Get started with Budibase</Heading>
2020-10-08 21:35:11 +13:00
</div>
<div class="step">
{#each steps as component, i (i)}
<div class:hidden={$currentStep !== i}>
<svelte:component
this={component}
{template}
{values}
{errors}
{touched} />
</div>
{/each}
2020-07-31 20:46:23 +12:00
</div>
2020-10-08 21:35:11 +13:00
<div class="footer">
{#if $currentStep > 0}
<Button medium secondary on:click={() => $currentStep--}>Back</Button>
2020-10-08 21:35:11 +13:00
{/if}
{#if $currentStep < steps.length - 1}
2021-04-29 01:29:39 +12:00
<Button medium cta on:click={() => $currentStep++} disabled={!valid}>
2020-10-08 21:35:11 +13:00
Next
</Button>
{/if}
{#if $currentStep === steps.length - 1}
2020-10-08 21:35:11 +13:00
<Button
medium
2021-04-29 01:29:39 +12:00
cta
on:click={createNewApp}
disabled={!valid || submitting}>
2020-10-08 21:35:11 +13:00
{submitting ? 'Loading...' : 'Submit'}
</Button>
{/if}
2020-05-27 02:25:37 +12:00
</div>
2020-05-27 22:54:53 +12:00
</div>
2020-10-08 21:35:11 +13:00
{#if submitting}
<div in:fade class="spinner-container">
<Spinner />
<span class="spinner-text">Creating your app...</span>
</div>
{/if}
</div>
2020-05-27 02:25:37 +12:00
<style>
2020-05-27 22:54:53 +12:00
.container {
2020-08-04 01:59:50 +12:00
min-height: 600px;
2020-07-31 20:46:23 +12:00
display: grid;
grid-template-columns: 80px 1fr;
2020-05-27 22:54:53 +12:00
position: relative;
}
2020-07-31 20:46:23 +12:00
.sidebar {
2021-04-20 21:39:09 +12:00
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
padding: 40px 0;
background: var(--grey-1);
}
.steps {
flex: 1 1 auto;
2020-07-31 20:46:23 +12:00
display: grid;
border-bottom-left-radius: 0.5rem;
border-top-left-radius: 0.5rem;
grid-gap: 30px;
align-content: center;
}
2020-05-27 02:25:37 +12:00
.heading {
display: flex;
flex-direction: row;
align-items: center;
2020-05-27 03:37:11 +12:00
margin-bottom: 20px;
2020-05-27 02:25:37 +12:00
}
.body {
2021-04-20 21:39:09 +12:00
padding: 40px 60px 40px 60px;
2020-05-27 02:25:37 +12:00
display: grid;
2020-08-04 01:59:50 +12:00
align-items: center;
2020-07-31 20:46:23 +12:00
grid-template-rows: auto 1fr auto;
2020-05-27 02:25:37 +12:00
}
.footer {
2021-04-20 21:39:09 +12:00
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
gap: 15px;
2020-05-27 02:25:37 +12:00
}
2020-05-27 22:54:53 +12:00
.spinner-container {
2020-10-30 09:42:34 +13:00
background: var(--background);
2020-05-27 22:54:53 +12:00
position: absolute;
border-radius: 5px;
left: 0;
top: 0;
right: 0;
bottom: 0;
display: grid;
justify-items: center;
align-content: center;
grid-gap: 50px;
}
.spinner-text {
font-size: 2em;
}
2020-08-04 01:59:50 +12:00
.hidden {
display: none;
2020-05-27 23:48:38 +12:00
}
2020-08-04 02:30:26 +12:00
img {
height: 40px;
2021-04-20 21:39:09 +12:00
margin-bottom: 20px;
2020-08-04 02:30:26 +12:00
}
2020-05-27 02:25:37 +12:00
</style>