Minor UI updates for the create app modal. A spinner has been added to the modal confirmation UX. The app name is pre-populated using the app name. The app URL can no longer be null

This commit is contained in:
Dean 2022-03-23 12:43:20 +00:00
parent 9f9832f36b
commit bc4d0b7e51
5 changed files with 66 additions and 43 deletions

View File

@ -5,6 +5,7 @@
import Divider from "../Divider/Divider.svelte" import Divider from "../Divider/Divider.svelte"
import Icon from "../Icon/Icon.svelte" import Icon from "../Icon/Icon.svelte"
import Context from "../context" import Context from "../context"
import ProgressCircle from "../ProgressCircle/ProgressCircle.svelte"
export let title = undefined export let title = undefined
export let size = "S" export let size = "S"
@ -102,15 +103,22 @@
<Button group secondary on:click={close}>{cancelText}</Button> <Button group secondary on:click={close}>{cancelText}</Button>
{/if} {/if}
{#if showConfirmButton} {#if showConfirmButton}
<Button <span class="confirm-wrap">
group <Button
cta group
{...$$restProps} cta
disabled={confirmDisabled} {...$$restProps}
on:click={confirm} disabled={confirmDisabled}
> on:click={confirm}
{confirmText} >
</Button> {#if loading}
<ProgressCircle overBackground={true} size="S" />
{/if}
{#if !loading}
{confirmText}
{/if}
</Button>
</span>
{/if} {/if}
</div> </div>
{/if} {/if}
@ -169,4 +177,8 @@
.spectrum-Dialog-buttonGroup { .spectrum-Dialog-buttonGroup {
padding-left: 0; padding-left: 0;
} }
.confirm-wrap :global(.spectrum-Button-label) {
display: contents;
}
</style> </style>

View File

@ -13,18 +13,20 @@
export let template export let template
let creating = false
const values = writable({ name: "", url: null }) const values = writable({ name: "", url: null })
const validation = createValidationStore() const validation = createValidationStore()
$: validation.check($values) $: validation.check($values)
onMount(async () => { onMount(async () => {
$values.url = resolveAppUrl(template, $values.name, $values.url)
$values.name = resolveAppName(template, $values.name) $values.name = resolveAppName(template, $values.name)
nameToUrl($values.name)
await setupValidation() await setupValidation()
}) })
$: appUrl = `${window.location.origin}${ $: appUrl = `${window.location.origin}${
$values.url ? $values.url : `/${resolveAppUrl(template, $values.name)}` $values.url ? $values.url : `${resolveAppUrl(template, $values.name)}`
}` }`
const resolveAppUrl = (template, name) => { const resolveAppUrl = (template, name) => {
@ -39,7 +41,19 @@
if (template && !name) { if (template && !name) {
return template.name return template.name
} }
return name.trim() return name ? name.trim() : null
}
const tidyUrl = url => {
if (url && !url.startsWith("/")) {
url = `/${url}`
}
$values.url = url === "" ? null : url
}
const nameToUrl = appName => {
let resolvedUrl = resolveAppUrl(template, appName)
tidyUrl(resolvedUrl)
} }
const setupValidation = async () => { const setupValidation = async () => {
@ -52,6 +66,8 @@
} }
async function createNewApp() { async function createNewApp() {
creating = true
try { try {
// Create form data to create app // Create form data to create app
let data = new FormData() let data = new FormData()
@ -86,17 +102,11 @@
await auth.setInitInfo({}) await auth.setInitInfo({})
$goto(`/builder/app/${createdApp.instance._id}`) $goto(`/builder/app/${createdApp.instance._id}`)
} catch (error) { } catch (error) {
creating = false
console.error(error) console.error(error)
notifications.error("Error creating app") notifications.error("Error creating app")
} }
} }
// auto add slash to url
$: {
if ($values.url && !$values.url.startsWith("/")) {
$values.url = `/${$values.url}`
}
}
</script> </script>
<ModalContent <ModalContent
@ -128,41 +138,38 @@
{/if} {/if}
<Input <Input
bind:value={$values.name} bind:value={$values.name}
disabled={creating}
error={$validation.touched.name && $validation.errors.name} error={$validation.touched.name && $validation.errors.name}
on:blur={() => ($validation.touched.name = true)} on:blur={() => ($validation.touched.name = true)}
on:change={nameToUrl($values.name)}
label="Name" label="Name"
placeholder={$auth.user.firstName placeholder={$auth.user?.firstName
? `${$auth.user.firstName}s app` ? `${$auth.user.firstName}s app`
: "My app"} : "My app"}
/> />
<span> <span>
<Input <Input
bind:value={$values.url} bind:value={$values.url}
disabled={creating}
error={$validation.touched.url && $validation.errors.url} error={$validation.touched.url && $validation.errors.url}
on:blur={() => ($validation.touched.url = true)} on:blur={() => ($validation.touched.url = true)}
on:change={tidyUrl($values.url)}
label="URL" label="URL"
placeholder={$values.url placeholder={$values.url
? $values.url ? $values.url
: `/${resolveAppUrl(template, $values.name)}`} : `/${resolveAppUrl(template, $values.name)}`}
/> />
{#if $values.name} {#if $values.url && $values.url !== "" && !$validation.errors.url}
<div class="app-server-wrap" title={appUrl}> <div class="app-server" title={appUrl}>
<span class="app-server-prefix"> {`${window.location.origin}${$values.url}`}
{window.location.origin}
</span>
{$values.url
? $values.url
: `/${resolveAppUrl(template, $values.name)}`}
</div> </div>
{/if} {/if}
</span> </span>
</ModalContent> </ModalContent>
<style> <style>
.app-server-prefix { .app-server {
color: var(--spectrum-global-color-gray-500); color: var(--spectrum-global-color-gray-600);
}
.app-server-wrap {
margin-top: 10px; margin-top: 10px;
width: 320px; width: 320px;
white-space: nowrap; white-space: nowrap;

View File

@ -35,13 +35,14 @@ export const url = (validation, { apps, currentApp } = { apps: [] }) => {
validation.addValidator( validation.addValidator(
"url", "url",
string() string()
.trim()
.nullable() .nullable()
.matches(APP_URL_REGEX, "App URL must not contain spaces") .required("Your application must have a url")
.matches(APP_URL_REGEX, "Please enter a valid url")
.test( .test(
"non-existing-app-url", "non-existing-app-url",
"Another app with the same URL already exists", "Another app with the same URL already exists",
value => { value => {
// url is nullable
if (!value) { if (!value) {
return true return true
} }

View File

@ -9,6 +9,7 @@
Body, Body,
Modal, Modal,
Divider, Divider,
Link,
} from "@budibase/bbui" } from "@budibase/bbui"
import CreateAppModal from "components/start/CreateAppModal.svelte" import CreateAppModal from "components/start/CreateAppModal.svelte"
import TemplateDisplay from "components/common/TemplateDisplay.svelte" import TemplateDisplay from "components/common/TemplateDisplay.svelte"
@ -60,14 +61,15 @@
<Page wide> <Page wide>
<Layout noPadding gap="XL"> <Layout noPadding gap="XL">
<span> <span>
<Button <Link
primary quiet
secondary
on:click={() => { on:click={() => {
$goto("../") $goto("../")
}} }}
> >
Back &lt;&nbsp;Back
</Button> </Link>
</span> </span>
<div class="title"> <div class="title">

View File

@ -1,6 +1,6 @@
<script> <script>
import { goto } from "@roxi/routify" import { goto } from "@roxi/routify"
import { Layout, Page, notifications, Button } from "@budibase/bbui" import { Layout, Page, notifications, Link } from "@budibase/bbui"
import TemplateDisplay from "components/common/TemplateDisplay.svelte" import TemplateDisplay from "components/common/TemplateDisplay.svelte"
import { onMount } from "svelte" import { onMount } from "svelte"
import { templates } from "stores/portal" import { templates } from "stores/portal"
@ -25,14 +25,15 @@
<Page wide> <Page wide>
<Layout noPadding gap="XL"> <Layout noPadding gap="XL">
<span> <span>
<Button <Link
primary quiet
secondary
on:click={() => { on:click={() => {
$goto("../") $goto("../")
}} }}
> >
Back &lt;&nbsp;Back
</Button> </Link>
</span> </span>
{#if loaded && $templates?.length} {#if loaded && $templates?.length}
<TemplateDisplay templates={$templates} /> <TemplateDisplay templates={$templates} />