Refactored the screen wizard to separate out the role selection. Design and layout updates to address feedback

This commit is contained in:
Dean 2022-04-22 12:22:21 +01:00
parent e0ebd08cf6
commit d601ec47f4
5 changed files with 208 additions and 136 deletions

View File

@ -288,7 +288,7 @@ Cypress.Commands.add("createScreen", (screenName, route, accessLevelLabel) => {
cy.get("[aria-label=AddCircle]").click() cy.get("[aria-label=AddCircle]").click()
cy.get(".spectrum-Modal").within(() => { cy.get(".spectrum-Modal").within(() => {
cy.get(".item").contains("Blank").click() cy.get(".item").contains("Blank").click()
cy.get(".spectrum-Button").contains("Add screens").click({ force: true }) cy.get(".spectrum-Button").contains("Continue").click({ force: true })
cy.wait(500) cy.wait(500)
}) })
cy.get(".spectrum-Dialog-grid").within(() => { cy.get(".spectrum-Dialog-grid").within(() => {
@ -311,7 +311,7 @@ Cypress.Commands.add("navigateToAutogeneratedModal", () => {
cy.get("[aria-label=AddCircle]").click() cy.get("[aria-label=AddCircle]").click()
cy.get(".spectrum-Modal").within(() => { cy.get(".spectrum-Modal").within(() => {
cy.get(".item").contains("Autogenerated Screens").click() cy.get(".item").contains("Autogenerated Screens").click()
cy.get(".spectrum-Button").contains("Add screens").click({ force: true }) cy.get(".spectrum-Button").contains("Continue").click({ force: true })
cy.wait(500) cy.wait(500)
}) })
}) })

View File

@ -1,24 +1,17 @@
<script> <script>
import { store, selectedAccessRole } from "builderStore" import { store } from "builderStore"
import { import { ModalContent, Layout, notifications, Icon } from "@budibase/bbui"
ModalContent, import { tables, datasources } from "stores/backend"
Layout,
Select,
Divider,
notifications,
} from "@budibase/bbui"
import { tables, datasources, roles } from "stores/backend"
import getTemplates from "builderStore/store/screenTemplates" import getTemplates from "builderStore/store/screenTemplates"
import ICONS from "../../backend/DatasourceNavigator/icons" import ICONS from "../../backend/DatasourceNavigator/icons"
import { IntegrationNames } from "constants" import { IntegrationNames } from "constants"
import analytics, { Events } from "analytics"
import { onMount } from "svelte" import { onMount } from "svelte"
export let onCancel export let onCancel
export let onConfirm export let onConfirm
export let initalScreens = []
let selectedScreens = [] let selectedScreens = [...initalScreens]
let screenAccessRole = $selectedAccessRole + ""
const toggleScreenSelection = (table, datasource) => { const toggleScreenSelection = (table, datasource) => {
if (selectedScreens.find(s => s.table === table.name)) { if (selectedScreens.find(s => s.table === table.name)) {
@ -43,7 +36,6 @@
const confirmDatasourceSelection = async () => { const confirmDatasourceSelection = async () => {
await onConfirm({ await onConfirm({
templates: selectedScreens, templates: selectedScreens,
screenAccessRole,
}) })
} }
@ -77,93 +69,94 @@
disabled={!selectedScreens.length} disabled={!selectedScreens.length}
size="L" size="L"
> >
<Layout noPadding gap="XS"> <Layout noPadding gap="S">
{#each filteredSources as datasource} {#each filteredSources as datasource}
<div class="data-source-header"> <div class="data-source-wrap">
<div class="datasource-icon"> <div class="data-source-header">
<svelte:component <div class="datasource-icon">
this={ICONS[datasource.source]} <svelte:component
height="24" this={ICONS[datasource.source]}
width="24" height="24"
/> width="24"
</div> />
<div class="content"> </div>
<div class="text">{datasource.name}</div> <div class="data-source-name">{datasource.name}</div>
</div> </div>
{#if Array.isArray(datasource.entities)}
{#each datasource.entities.filter(table => table._id !== "ta_users") as table}
<div
class="data-source-entry"
class:selected={selectedScreens.find(x => x.table === table.name)}
on:click={() => toggleScreenSelection(table, datasource)}
>
<svg
width="16px"
height="16px"
class="spectrum-Icon"
style="color: white"
focusable="false"
>
<use xlink:href="#spectrum-icon-18-Table" />
</svg>
{table.name}
{#if selectedScreens.find(x => x.table === table.name)}
<span class="data-source-check">
<Icon size="S" name="CheckmarkCircle" />
</span>
{/if}
</div>
{/each}
{/if}
{#if datasource["entities"] && !Array.isArray(datasource.entities)}
{#each Object.keys(datasource.entities).filter(table => table._id !== "ta_users") as table_key}
<div
class="data-source-entry"
class:selected={selectedScreens.find(
x => x.table === datasource.entities[table_key].name
)}
on:click={() =>
toggleScreenSelection(
datasource.entities[table_key],
datasource
)}
>
<svg
width="16px"
height="16px"
class="spectrum-Icon"
style="color: white"
focusable="false"
>
<use xlink:href="#spectrum-icon-18-Table" />
</svg>
{datasource.entities[table_key].name}
{#if selectedScreens.find(x => x.table === datasource.entities[table_key].name)}
<span class="data-source-check">
<Icon size="S" name="CheckmarkCircle" />
</span>
{/if}
</div>
{/each}
{/if}
</div> </div>
{#if Array.isArray(datasource.entities)}
{#each datasource.entities.filter(table => table._id !== "ta_users") as table}
<div
class="data-source-entry"
class:selected={selectedScreens.find(x => x.table === table.name)}
on:click={() => toggleScreenSelection(table, datasource)}
>
<svg
width="16px"
height="16px"
class="spectrum-Icon"
style="color: white"
focusable="false"
>
<use xlink:href="#spectrum-icon-18-Table" />
</svg>
{table.name}
</div>
{/each}
{/if}
{#if datasource["entities"] && !Array.isArray(datasource.entities)}
{#each Object.keys(datasource.entities).filter(table => table._id !== "ta_users") as table_key}
<div
class="data-source-entry"
class:selected={selectedScreens.find(
x => x.table === datasource.entities[table_key].name
)}
on:click={() =>
toggleScreenSelection(datasource.entities[table_key], datasource)}
>
<svg
width="16px"
height="16px"
class="spectrum-Icon"
style="color: white"
focusable="false"
>
<use xlink:href="#spectrum-icon-18-Table" />
</svg>
{datasource.entities[table_key].name}
</div>
{/each}
{/if}
{/each} {/each}
<div>
<Divider size="S" />
</div>
<Select
bind:value={screenAccessRole}
on:change={() => {
analytics.captureEvent(Events.SCREEN.CREATE_ROLE_UPDATED, {
screenAccessRole,
})
}}
label="Screen access"
getOptionLabel={role => role.name}
getOptionValue={role => role._id}
getOptionColor={role => role.color}
options={$roles}
/>
</Layout> </Layout>
</ModalContent> </ModalContent>
<style> <style>
.data-source-wrap {
padding-bottom: var(--spectrum-alias-item-padding-s);
display: grid;
grid-gap: var(--spacing-xs);
}
.data-source-header { .data-source-header {
display: flex; display: flex;
align-items: center; align-items: center;
} }
.data-source-header .content {
padding: var(--spectrum-alias-item-padding-l);
}
.data-source-entry { .data-source-entry {
cursor: pointer; cursor: pointer;
grid-gap: var(--spectrum-alias-grid-margin-xsmall); grid-gap: var(--spectrum-alias-grid-margin-xsmall);
@ -172,7 +165,6 @@
transition: 0.3s all; transition: 0.3s all;
border: 1px solid var(--spectrum-global-color-gray-300); border: 1px solid var(--spectrum-global-color-gray-300);
border-radius: 4px; border-radius: 4px;
box-sizing: border-box;
border-width: 1px; border-width: 1px;
display: flex; display: flex;
align-items: center; align-items: center;
@ -182,4 +174,22 @@
.selected { .selected {
background: var(--spectrum-alias-background-color-tertiary); background: var(--spectrum-alias-background-color-tertiary);
} }
.data-source-name {
padding: var(--spectrum-alias-item-padding-s);
min-height: var(--spectrum-icon-size-s);
}
.data-source-entry .data-source-check {
margin-left: auto;
}
.data-source-entry :global(.spectrum-Icon) {
min-width: 16px;
}
.data-source-entry .data-source-check :global(.spectrum-Icon) {
color: var(--spectrum-global-color-green-600);
display: block;
}
</style> </style>

View File

@ -1,6 +1,6 @@
<script> <script>
import { tables } from "stores/backend" import { tables } from "stores/backend"
import { ModalContent, Body, Layout, Icon } from "@budibase/bbui" import { ModalContent, Body, Layout, Icon, Heading } from "@budibase/bbui"
export let onConfirm export let onConfirm
export let onCancel export let onCancel
@ -18,14 +18,13 @@
<div> <div>
<ModalContent <ModalContent
title="Add screens" title="Add screens"
confirmText="Add screens" confirmText="Continue"
cancelText="Cancel" cancelText="Cancel"
onConfirm={confirmScreenSelection} onConfirm={confirmScreenSelection}
{onCancel} {onCancel}
disabled={!selectedScreenMode} disabled={!selectedScreenMode}
size="L" size="L"
> >
<Body size="S">Add a blank screen</Body>
<Layout noPadding gap="S"> <Layout noPadding gap="S">
<div <div
class="screen-type item" class="screen-type item"
@ -36,7 +35,10 @@
> >
<div data-cy="blank-screen" class="content screen-type-wrap"> <div data-cy="blank-screen" class="content screen-type-wrap">
<Icon name="WebPage" /> <Icon name="WebPage" />
<span class="text">Blank</span> <div class="screen-type-text">
<Heading size="XS">Blank screen</Heading>
<Body size="S">Add a blank screen</Body>
</div>
</div> </div>
<div <div
style="color: var(--spectrum-global-color-green-600); float: right" style="color: var(--spectrum-global-color-green-600); float: right"
@ -49,11 +51,6 @@
</div> </div>
</div> </div>
<Body size="S">
Add autogenerated screens with CRUD functionality to get a working app
quickly! (Requires a data source)
</Body>
<div <div
class="screen-type item" class="screen-type item"
class:selected={selectedScreenMode == autoCreateModeKey} class:selected={selectedScreenMode == autoCreateModeKey}
@ -65,7 +62,13 @@
> >
<div data-cy="autogenerated-screens" class="content screen-type-wrap"> <div data-cy="autogenerated-screens" class="content screen-type-wrap">
<Icon name="WebPages" /> <Icon name="WebPages" />
<span class="text">Autogenerated Screens</span> <div class="screen-type-text">
<Heading size="XS">Autogenerated Screens</Heading>
<Body size="S">
Add autogenerated screens with CRUD functionality to get a working
app quickly! (Requires a data source)
</Body>
</div>
</div> </div>
<div <div
style="color: var(--spectrum-global-color-green-600); float: right" style="color: var(--spectrum-global-color-green-600); float: right"
@ -100,12 +103,6 @@
.content { .content {
letter-spacing: 0px; letter-spacing: 0px;
} }
.text {
font-weight: 600;
margin-left: var(--spacing-m);
font-size: 14px;
text-transform: capitalize;
}
.item { .item {
cursor: pointer; cursor: pointer;
grid-gap: var(--spectrum-alias-grid-margin-xsmall); grid-gap: var(--spectrum-alias-grid-margin-xsmall);
@ -114,15 +111,22 @@
transition: 0.3s all; transition: 0.3s all;
border: 1px solid var(--spectrum-global-color-gray-300); border: 1px solid var(--spectrum-global-color-gray-300);
border-radius: 4px; border-radius: 4px;
box-sizing: border-box;
border-width: 1px; border-width: 1px;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
height: 60px;
} }
.item:hover, .item:hover,
.selected { .selected {
background: var(--spectrum-alias-background-color-tertiary); background: var(--spectrum-alias-background-color-tertiary);
} }
.screen-type-wrap .screen-type-text {
padding-left: var(--spectrum-alias-item-padding-xl);
}
.screen-type-wrap :global(.spectrum-Icon) {
min-width: var(--spectrum-icon-size-m);
}
.screen-type-wrap :global(.spectrum-Heading) {
padding-bottom: var(--spectrum-alias-item-padding-s);
}
</style> </style>

View File

@ -1,10 +1,8 @@
<script> <script>
import { ModalContent, Input, Select } from "@budibase/bbui" import { ModalContent, Input } from "@budibase/bbui"
import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl" import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl"
import { selectedAccessRole, allScreens } from "builderStore" import { selectedAccessRole, allScreens } from "builderStore"
import { get } from "svelte/store" import { get } from "svelte/store"
import { roles } from "stores/backend"
import analytics, { Events } from "analytics"
export let onConfirm export let onConfirm
export let onCancel export let onCancel
@ -40,7 +38,6 @@
const confirmScreenDetails = async () => { const confirmScreenDetails = async () => {
await onConfirm({ await onConfirm({
screenUrl, screenUrl,
screenAccessRole,
}) })
} }
</script> </script>
@ -55,22 +52,9 @@
disabled={!screenAccessRole || !screenUrl || routeError || !touched} disabled={!screenAccessRole || !screenUrl || routeError || !touched}
> >
<Input <Input
label="URL" label="Enter a URL for the new screen"
error={routeError} error={routeError}
bind:value={screenUrl} bind:value={screenUrl}
on:change={routeChanged} on:change={routeChanged}
/> />
<Select
bind:value={screenAccessRole}
on:change={() => {
analytics.captureEvent(Events.SCREEN.CREATE_ROLE_UPDATED, {
screenAccessRole,
})
}}
label="Screen access"
getOptionLabel={role => role.name}
getOptionValue={role => role._id}
getOptionColor={role => role.color}
options={$roles}
/>
</ModalContent> </ModalContent>

View File

@ -3,12 +3,12 @@
import NewScreenModal from "components/design/NavigationPanel/NewScreenModal.svelte" import NewScreenModal from "components/design/NavigationPanel/NewScreenModal.svelte"
import DatasourceModal from "components/design/NavigationPanel/DatasourceModal.svelte" import DatasourceModal from "components/design/NavigationPanel/DatasourceModal.svelte"
import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl" import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl"
import { Modal, notifications } from "@budibase/bbui" import { Modal, ModalContent, Select, notifications } from "@budibase/bbui"
import { store, selectedAccessRole } from "builderStore" import { store, selectedAccessRole } from "builderStore"
import analytics, { Events } from "analytics" import analytics, { Events } from "analytics"
import { get } from "svelte/store" import { get } from "svelte/store"
import getTemplates from "builderStore/store/screenTemplates" import getTemplates from "builderStore/store/screenTemplates"
import { tables } from "stores/backend" import { tables, roles } from "stores/backend"
let pendingScreen let pendingScreen
@ -16,11 +16,22 @@
let newScreenModal let newScreenModal
let screenDetailsModal let screenDetailsModal
let datasourceModal let datasourceModal
let screenAccessRoleModal
// Cache variables for workflow
let screenAccessRole = $selectedAccessRole + ""
let selectedTemplates = null
let blankScreenUrl = null
let screenMode = null
// External handler to show the screen wizard // External handler to show the screen wizard
export const showModal = () => { export const showModal = () => {
newScreenModal.show() selectedTemplates = null
blankScreenUrl = null
screenMode = null
newScreenModal.show()
// Reset state when showing modal again // Reset state when showing modal again
pendingScreen = null pendingScreen = null
} }
@ -102,6 +113,8 @@
// Handler for NewScreenModal // Handler for NewScreenModal
const confirmScreenSelection = async mode => { const confirmScreenSelection = async mode => {
screenMode = mode
if (mode == "autoCreate") { if (mode == "autoCreate") {
datasourceModal.show() datasourceModal.show()
} else { } else {
@ -114,12 +127,18 @@
} }
} }
// Handler for DatasourceModal // Handler for DatasourceModal confirmation, move to screen access select
const confirmScreenDatasources = async ({ templates, screenAccessRole }) => { const confirmScreenDatasources = async ({ templates }) => {
selectedTemplates = templates
screenAccessRoleModal.show()
}
// Handler for Datasource Screen Creation
const completeDatasourceScreenCreation = async () => {
// // Handle template selection // // Handle template selection
if (templates?.length > 1) { if (selectedTemplates?.length > 1) {
// Autoscreens, so create immediately // Autoscreens, so create immediately
const screens = templates.map(template => { const screens = selectedTemplates.map(template => {
let screenTemplate = template.create() let screenTemplate = template.create()
screenTemplate.datasource = template.datasource screenTemplate.datasource = template.datasource
return screenTemplate return screenTemplate
@ -128,14 +147,42 @@
} }
} }
// Handler for ScreenDetailsModal const confirmScreenBlank = async ({ screenUrl }) => {
const confirmScreenDetails = async ({ screenUrl, screenAccessRole }) => { blankScreenUrl = screenUrl
screenAccessRoleModal.show()
}
// Submit request for a blank screen
const confirmBlankScreenCreation = async ({
screenUrl,
screenAccessRole,
}) => {
if (!pendingScreen) { if (!pendingScreen) {
return return
} }
pendingScreen.routing.route = screenUrl pendingScreen.routing.route = screenUrl
await createScreens({ screens: [pendingScreen], screenAccessRole }) await createScreens({ screens: [pendingScreen], screenAccessRole })
} }
// Submit screen config for creation.
const confirmScreenCreation = async () => {
if (screenMode === "blankScreen") {
confirmBlankScreenCreation({
screenUrl: blankScreenUrl,
screenAccessRole,
})
} else {
completeDatasourceScreenCreation()
}
}
const roleSelectBack = () => {
if (screenMode === "blankScreen") {
newScreenModal.show()
} else {
datasourceModal.show()
}
}
</script> </script>
<Modal bind:this={newScreenModal}> <Modal bind:this={newScreenModal}>
@ -146,12 +193,39 @@
<DatasourceModal <DatasourceModal
onConfirm={confirmScreenDatasources} onConfirm={confirmScreenDatasources}
onCancel={() => newScreenModal.show()} onCancel={() => newScreenModal.show()}
initalScreens={!selectedTemplates ? [] : [...selectedTemplates]}
/> />
</Modal> </Modal>
<Modal bind:this={screenAccessRoleModal}>
<ModalContent
title={"Create CRUD Screens"}
confirmText={"Done"}
cancelText={"Back"}
onConfirm={confirmScreenCreation}
onCancel={roleSelectBack}
>
Select which level of access you want your screens to have
<Select
bind:value={screenAccessRole}
on:change={() => {
analytics.captureEvent(Events.SCREEN.CREATE_ROLE_UPDATED, {
screenAccessRole,
})
}}
label="Access"
getOptionLabel={role => role.name}
getOptionValue={role => role._id}
getOptionColor={role => role.color}
options={$roles}
/>
</ModalContent>
</Modal>
<Modal bind:this={screenDetailsModal}> <Modal bind:this={screenDetailsModal}>
<ScreenDetailsModal <ScreenDetailsModal
onConfirm={confirmScreenDetails} onConfirm={confirmScreenBlank}
onCancel={() => newScreenModal.show()} onCancel={() => newScreenModal.show()}
initialUrl={blankScreenUrl}
/> />
</Modal> </Modal>