Misc fixes and improvements (#9503)

* Rewrite position dropdown helper to properly work as a svelte action, improve performance and fix bugs

* Update action button styles

* Update spacing on some onboarding pages and update background gradient

* Prevent special characters in first app name

* Fix type in onboarding tour

* Default first app name and url to having a value

* Update text in first app onboarding file upload

* Fix double mounting of apps page causing issues and templates error

* Fix null app ID when creating your first app using data upload

* Fix app deletion not causing app list to be refreshed
This commit is contained in:
Andrew Kingston 2023-02-02 10:06:41 +00:00 committed by GitHub
parent 6ec098efdd
commit 659cfd2492
18 changed files with 152 additions and 117 deletions

View File

@ -86,7 +86,7 @@
}
.is-selected:not(.spectrum-ActionButton--emphasized):not(.spectrum-ActionButton--quiet) {
background: var(--spectrum-global-color-gray-300);
border-color: var(--spectrum-global-color-gray-700);
border-color: var(--spectrum-global-color-gray-500);
}
.noPadding {
padding: 0;

View File

@ -1,11 +1,21 @@
export default function positionDropdown(
element,
{ anchor, align, maxWidth, useAnchorWidth, offset = 5 }
) {
const update = () => {
export default function positionDropdown(element, opts) {
let resizeObserver
let latestOpts = opts
// We need a static reference to this function so that we can properly
// clean up the scroll listener.
const scrollUpdate = () => {
updatePosition(latestOpts)
}
// Updates the position of the dropdown
const updatePosition = opts => {
const { anchor, align, maxWidth, useAnchorWidth, offset = 5 } = opts
if (!anchor) {
return
}
// Compute bounds
const anchorBounds = anchor.getBoundingClientRect()
const elementBounds = element.getBoundingClientRect()
let styles = {
@ -51,26 +61,47 @@ export default function positionDropdown(
})
}
// The actual svelte action callback which creates observers on the relevant
// DOM elements
const update = newOpts => {
latestOpts = newOpts
// Cleanup old state
if (resizeObserver) {
resizeObserver.disconnect()
}
// Do nothing if no anchor
const { anchor } = newOpts
if (!anchor) {
return
}
// Observe both anchor and element and resize the popover as appropriate
resizeObserver = new ResizeObserver(() => updatePosition(newOpts))
resizeObserver.observe(anchor)
resizeObserver.observe(element)
resizeObserver.observe(document.body)
}
// Apply initial styles which don't need to change
element.style.position = "absolute"
element.style.zIndex = "9999"
// Observe both anchor and element and resize the popover as appropriate
const resizeObserver = new ResizeObserver(entries => {
entries.forEach(update)
})
if (anchor) {
resizeObserver.observe(anchor)
}
resizeObserver.observe(element)
resizeObserver.observe(document.body)
// Set up a scroll listener
document.addEventListener("scroll", scrollUpdate, true)
document.addEventListener("scroll", update, true)
// Perform initial update
update(opts)
return {
update,
destroy() {
// Cleanup
if (resizeObserver) {
resizeObserver.disconnect()
document.removeEventListener("scroll", update, true)
}
document.removeEventListener("scroll", scrollUpdate, true)
},
}
}

View File

@ -57,7 +57,6 @@
</script>
{#if open}
{#key anchor}
<Portal {target}>
<div
tabindex="0"
@ -80,7 +79,6 @@
<slot />
</div>
</Portal>
{/key}
{/if}
<style>

View File

@ -1,5 +1,5 @@
<div>
In this section you can mange the data for your app:
In this section you can manage the data for your app:
<ul class="feature-list">
<li>Connect data sources</li>
<li>Edit data</li>

View File

@ -138,7 +138,6 @@
}
$goto(`/builder/app/${createdApp.instance._id}`)
// apps.load()
} catch (error) {
creating = false
console.error(error)

View File

@ -52,7 +52,7 @@
<span class="back-chev" on:click={() => $goto("../")}>
<Icon name="ChevronLeft" size="XL" />
</span>
Forgotten your password?
Forgot your password?
</div>
</Heading>
</span>
@ -83,7 +83,12 @@
</FancyForm>
</Layout>
<div>
<Button disabled={!email || error || submitted} cta on:click={forgot}>
<Button
size="L"
disabled={!email || error || submitted}
cta
on:click={forgot}
>
Reset password
</Button>
</div>
@ -92,7 +97,7 @@
<style>
img {
width: 48px;
width: 46px;
}
.back-chev {
display: inline-block;
@ -102,5 +107,6 @@
.heading-content {
display: flex;
align-items: center;
gap: var(--spacing-m);
}
</style>

View File

@ -66,7 +66,7 @@
<svelte:window on:keydown={handleKeydown} />
<TestimonialPage>
<Layout gap="S" noPadding>
<Layout gap="L" noPadding>
<Layout justifyItems="center" noPadding>
{#if loaded}
<img alt="logo" src={$organisation.logoUrl || Logo} />
@ -124,14 +124,19 @@
</FancyForm>
</Layout>
<Layout gap="XS" noPadding justifyItems="center">
<Button cta disabled={Object.keys(errors).length > 0} on:click={login}>
<Button
size="L"
cta
disabled={Object.keys(errors).length > 0}
on:click={login}
>
Log in to {company}
</Button>
</Layout>
<Layout gap="XS" noPadding justifyItems="center">
<div class="user-actions">
<ActionButton quiet on:click={() => $goto("./forgot")}>
Forgot password
<ActionButton size="L" quiet on:click={() => $goto("./forgot")}>
Forgot password?
</ActionButton>
</div>
</Layout>

View File

@ -68,7 +68,7 @@
</script>
<TestimonialPage>
<Layout gap="S" noPadding>
<Layout gap="M" noPadding>
<img alt="logo" src={$organisation.logoUrl || Logo} />
<Layout gap="XS" noPadding>
<Heading size="M">Join {company}</Heading>
@ -175,6 +175,7 @@
</Layout>
<div>
<Button
size="L"
disabled={Object.keys(errors).length > 0 || onboarding}
cta
on:click={acceptInvite}

View File

@ -14,7 +14,7 @@
let activeTab = "Apps"
$: $url(), updateActiveTab($menu)
$: fullScreen = !$apps?.length
$: fullscreen = !$apps.length
const updateActiveTab = menu => {
for (let entry of menu) {
@ -37,7 +37,8 @@
$redirect("../")
} else {
try {
await organisation.init()
// We need to load apps to know if we need to show onboarding fullscreen
await Promise.all([apps.load(), organisation.init()])
} catch (error) {
notifications.error("Error getting org config")
}
@ -47,9 +48,10 @@
})
</script>
{#if fullScreen}
{#if $auth.user && loaded}
{#if fullscreen}
<slot />
{:else if $auth.user && loaded}
{:else}
<HelpMenu />
<div class="container">
<div class="nav">
@ -78,6 +80,7 @@
</div>
<MobileMenu visible={mobileMenuVisible} on:close={hideMobileMenu} />
</div>
{/if}
{/if}
<style>

View File

@ -10,13 +10,11 @@
onMount(async () => {
try {
// Always load latest
await apps.load()
await licensing.init()
await templates.load()
if ($licensing.groupsEnabled) {
await groups.actions.init()
}
await Promise.all([
licensing.init(),
templates.load(),
groups.actions.init(),
])
if ($templates?.length === 0) {
notifications.error("There was a problem loading quick start templates")

View File

@ -5,6 +5,8 @@
export let name = ""
export let url = ""
export let onNext = () => {}
const nameRegex = /^[a-zA-Z0-9\s]*$/
let nameError = null
let urlError = null
@ -14,6 +16,9 @@
if (name.length < 1) {
return "Name must be provided"
}
if (!nameRegex.test(name)) {
return "No special characters are allowed"
}
}
const validateUrl = url => {

View File

@ -17,8 +17,8 @@
import createFromScratchScreen from "builderStore/store/screenTemplates/createFromScratchScreen"
import { Roles } from "constants/backend"
let name = ""
let url = ""
let name = "My first app"
let url = "my-first-app"
let stage = "name"
let appId = null
@ -57,7 +57,7 @@
defaultScreenTemplate.routing.roldId = Roles.BASIC
await store.actions.screens.save(defaultScreenTemplate)
return createdApp.instance._id
appId = createdApp.instance._id
}
const getIntegrations = async () => {
@ -79,14 +79,14 @@
}
}
const goToApp = appId => {
const goToApp = () => {
$goto(`/builder/app/${appId}`)
notifications.success(`App created successfully`)
}
const handleCreateApp = async ({ datasourceConfig, useSampleData }) => {
try {
appId = await createApp(useSampleData)
await createApp(useSampleData)
if (datasourceConfig) {
await saveDatasource({
@ -99,7 +99,7 @@
})
}
goToApp(appId)
goToApp()
} catch (e) {
console.log(e)
notifications.error("There was a problem creating your app")
@ -111,7 +111,7 @@
<CreateTableModal
name="Your Data"
beforeSave={createApp}
afterSave={() => goToApp(appId)}
afterSave={goToApp}
/>
</Modal>
@ -142,7 +142,7 @@
<div class="dataButtonIcon">
<FontAwesomeIcon name="fa-solid fa-file-arrow-up" />
</div>
Upload file
Upload data (CSV or JSON)
</div>
</FancyButton>
</div>

View File

@ -100,8 +100,9 @@
const deleteApp = async () => {
try {
await API.deleteApp(app?.devId)
apps.load()
notifications.success("App deleted successfully")
$goto("../")
$goto("../../")
} catch (err) {
notifications.error("Error deleting app")
}

View File

@ -1,17 +1,12 @@
<script>
import { apps, groups, licensing } from "stores/portal"
import { groups } from "stores/portal"
import { onMount } from "svelte"
let loaded = !!$apps?.length
let loaded = false
onMount(async () => {
if (!loaded) {
await apps.load()
if ($licensing.groupsEnabled) {
await groups.actions.init()
}
loaded = true
}
})
</script>

View File

@ -146,7 +146,7 @@
onMount(async () => {
try {
await Promise.all([groups.actions.init(), apps.load(), roles.fetch()])
await Promise.all([groups.actions.init(), roles.fetch()])
loaded = true
} catch (error) {
notifications.error("Error fetching user group data")

View File

@ -80,9 +80,7 @@
try {
// always load latest
await licensing.init()
if ($licensing.groupsEnabled) {
await groups.actions.init()
}
} catch (error) {
notifications.error("Error getting user groups")
}

View File

@ -215,12 +215,7 @@
onMount(async () => {
try {
await Promise.all([
fetchUser(),
groups.actions.init(),
apps.load(),
roles.fetch(),
])
await Promise.all([fetchUser(), groups.actions.init(), roles.fetch()])
loaded = true
} catch (error) {
notifications.error("Error getting user groups")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 2.3 MiB