Initial commit of screen workflow updates

This commit is contained in:
Dean 2022-04-06 10:10:53 +01:00
parent d82e15463d
commit 186dcc00f6
4 changed files with 257 additions and 111 deletions

View File

@ -0,0 +1,153 @@
<script>
import { store, selectedAccessRole } from "builderStore"
import { ModalContent, Layout, Select } from "@budibase/bbui"
import { tables, datasources, roles } from "stores/backend"
import getTemplates from "builderStore/store/screenTemplates"
import ICONS from "../../backend/DatasourceNavigator/icons"
import { IntegrationNames } from "constants"
export let onCancel
export let onConfirm
let selectedScreens = []
let screenAccessRole = $selectedAccessRole + ""
const toggleScreenSelection = table => {
if (selectedScreens.find(s => s.table === table.name)) {
selectedScreens = selectedScreens.filter(
screen => screen.table !== table.name
)
} else {
let partialTemplates = getTemplates($store, $tables.list).filter(
template => template.table === table.name
)
selectedScreens = [...partialTemplates, ...selectedScreens]
}
}
const confirmDatasourceSelection = async () => {
await onConfirm({
templates: selectedScreens,
screenAccessRole,
})
}
$: filteredSources = $datasources.list.reduce((acc, datasource) => {
acc["restSources"] = !acc["restSources"] ? [] : acc["restSources"]
acc["otherSources"] = !acc["otherSources"] ? [] : acc["otherSources"]
if (datasource.source === IntegrationNames.REST) {
acc["restSources"].push(datasource)
} else {
acc["otherSources"].push(datasource)
}
return acc
}, {})
</script>
<ModalContent
title="Create CRUD Screens"
confirmText="Confirm"
cancelText="Back"
onConfirm={confirmDatasourceSelection}
{onCancel}
disabled={!selectedScreens.length}
size="L"
>
<Layout noPadding gap="XS">
{#each filteredSources.otherSources as datasource}
<div class="data-source-header">
<div class="datasource-icon">
<svelte:component
this={ICONS[datasource.source]}
height="24"
width="24"
/>
</div>
<div class="content">
<div class="text">{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)}
>
<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])}
>
<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}
<Select
bind:value={screenAccessRole}
label="Screen access"
getOptionLabel={role => role.name}
getOptionValue={role => role._id}
getOptionColor={role => role.color}
options={$roles}
/>
</Layout>
</ModalContent>
<style>
.data-source-header {
display: flex;
align-items: center;
}
.data-source-header .content {
padding: var(--spectrum-alias-item-padding-l);
}
.data-source-entry {
cursor: pointer;
grid-gap: var(--spectrum-alias-grid-margin-xsmall);
padding: var(--spectrum-alias-item-padding-s);
background: var(--spectrum-alias-background-color-primary);
transition: 0.3s all;
border: 1px solid var(--spectrum-global-color-gray-300);
border-radius: 4px;
box-sizing: border-box;
border-width: 1px;
display: flex;
align-items: center;
}
.data-source-entry:hover,
.selected {
background: var(--spectrum-alias-background-color-tertiary);
}
</style>

View File

@ -1,43 +1,17 @@
<script>
import { store } from "builderStore"
import { tables } from "stores/backend"
import {
ModalContent,
Body,
Detail,
Layout,
Icon,
ProgressCircle,
} from "@budibase/bbui"
import getTemplates from "builderStore/store/screenTemplates"
import { ModalContent, Body, Layout, Icon } from "@budibase/bbui"
export let onConfirm
export let onCancel
export let showProgressCircle = false
const blankScreen = "createFromScratch"
let autoCreateModeKey = "autoCreate"
let blankScreenModeKey = "blankScreen"
let selectedScreens = []
let templates = getTemplates($store, $tables.list)
$: blankSelected = selectedScreens?.length === 1
$: autoSelected = selectedScreens?.length > 0 && !blankSelected
const toggleScreenSelection = table => {
if (selectedScreens.find(s => s.table === table.name)) {
selectedScreens = selectedScreens.filter(
screen => screen.table !== table.name
)
} else {
let partialTemplates = getTemplates($store, $tables.list).filter(
template => template.table === table.name
)
selectedScreens = [...partialTemplates, ...selectedScreens]
}
}
let selectedScreenMode
const confirmScreenSelection = async () => {
await onConfirm(selectedScreens)
await onConfirm(selectedScreenMode)
}
</script>
@ -48,70 +22,74 @@
cancelText="Cancel"
onConfirm={confirmScreenSelection}
{onCancel}
disabled={!selectedScreens.length}
disabled={!selectedScreenMode}
size="L"
>
<Body size="S">
Please select the screens you would like to add to your application.
Autogenerated screens come with CRUD functionality.
</Body>
<Body size="S">Add a blank screen</Body>
<Layout noPadding gap="S">
<Detail size="S">Blank screen</Detail>
<div
class="item"
class:selected={selectedScreens.find(x => x.id.includes(blankScreen))}
on:click={() =>
toggleScreenSelection(templates.find(t => t.id === blankScreen))}
class:disabled={autoSelected}
class="screen-type item"
class:selected={selectedScreenMode == blankScreenModeKey}
on:click={() => {
selectedScreenMode = blankScreenModeKey
}}
>
<div data-cy="blank-screen" class="content">
<div class="text">Blank</div>
<div data-cy="blank-screen" class="content screen-type-wrap">
<Icon name="WebPage" />
<span class="text">Blank</span>
</div>
<div
style="color: var(--spectrum-global-color-green-600); float: right"
>
{#if selectedScreens.find(x => x.id === blankScreen)}
{#if selectedScreenMode == blankScreenModeKey}
<div class="checkmark-spacing">
<Icon size="S" name="CheckmarkCircleOutline" />
<Icon size="S" name="CheckmarkCircle" />
</div>
{/if}
</div>
</div>
{#if $tables.list.filter(table => table._id !== "ta_users").length > 0}
<Detail size="S">Autogenerated Screens</Detail>
{#each $tables.list.filter(table => table._id !== "ta_users") as table}
<Body size="S">
Add autogenerated screens with CRUD functionality to get a working app
quickly! (Requires a data source)
</Body>
<div
class:disabled={blankSelected}
class:selected={selectedScreens.find(x => x.table === table.name)}
on:click={() => toggleScreenSelection(table)}
class="item"
class="screen-type item"
class:selected={selectedScreenMode == autoCreateModeKey}
on:click={() => {
selectedScreenMode = autoCreateModeKey
}}
class:disabled={!$tables.list.filter(table => table._id !== "ta_users")
.length}
>
<div class="content">
<div class="text">{table.name}</div>
<div data-cy="autogenerated-screens" class="content screen-type-wrap">
<Icon name="WebPages" />
<span class="text">Autogenerated Screens</span>
</div>
<div
style="color: var(--spectrum-global-color-green-600); float: right"
>
{#if selectedScreens.find(x => x.table === table.name)}
{#if selectedScreenMode == autoCreateModeKey}
<div class="checkmark-spacing">
<Icon size="S" name="CheckmarkCircleOutline" />
<Icon size="S" name="CheckmarkCircle" />
</div>
{/if}
</div>
</div>
{/each}
{/if}
</Layout>
<div slot="footer">
{#if showProgressCircle}
<div class="footer-progress"><ProgressCircle size="S" /></div>
{/if}
</div>
</ModalContent>
</div>
<style>
.screen-type.item {
padding: var(--spectrum-alias-item-padding-xl);
}
.screen-type-wrap {
display: flex;
flex-direction: row;
align-items: center;
}
.disabled {
opacity: 0.3;
pointer-events: none;
@ -119,22 +97,15 @@
.checkmark-spacing {
margin-right: var(--spacing-m);
}
.content {
letter-spacing: 0px;
}
.footer-progress {
margin-top: var(--spacing-s);
}
.text {
font-weight: 600;
margin-left: var(--spacing-m);
font-size: 14px;
text-transform: capitalize;
}
.item {
cursor: pointer;
grid-gap: var(--spectrum-alias-grid-margin-xsmall);
@ -150,7 +121,6 @@
align-items: center;
height: 60px;
}
.item:hover,
.selected {
background: var(--spectrum-alias-background-color-tertiary);

View File

@ -1,18 +1,18 @@
<script>
import { ModalContent, Input, ProgressCircle } from "@budibase/bbui"
import { ModalContent, Input, Select } from "@budibase/bbui"
import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl"
import { selectedAccessRole, allScreens } from "builderStore"
import { get } from "svelte/store"
import { roles } from "stores/backend"
export let onConfirm
export let onCancel
export let showProgressCircle = false
export let screenName
export let screenUrl
export let confirmText = "Continue"
let routeError
let touched = false
let screenAccessRole = $selectedAccessRole + ""
const routeChanged = event => {
if (!event.detail.startsWith("/")) {
@ -38,8 +38,8 @@
const confirmScreenDetails = async () => {
await onConfirm({
screenName,
screenUrl,
screenAccessRole,
})
}
</script>
@ -51,24 +51,24 @@
onConfirm={confirmScreenDetails}
{onCancel}
cancelText={"Back"}
disabled={!screenName || !screenUrl || routeError || !touched}
disabled={!screenAccessRole || !screenUrl || routeError || !touched}
>
<Input label="Name" bind:value={screenName} />
<!-- <Input label="Name" bind:value={screenName} /> -->
<Input
label="URL"
error={routeError}
bind:value={screenUrl}
on:change={routeChanged}
/>
<div slot="footer">
{#if showProgressCircle}
<div class="footer-progress"><ProgressCircle size="S" /></div>
{/if}
</div>
<Select
bind:value={screenAccessRole}
label="Screen access"
getOptionLabel={role => role.name}
getOptionValue={role => role._id}
getOptionColor={role => role.color}
options={$roles}
/>
</ModalContent>
<style>
.footer-progress {
margin-top: var(--spacing-s);
}
</style>

View File

@ -1,11 +1,14 @@
<script>
import ScreenDetailsModal from "components/design/NavigationPanel/ScreenDetailsModal.svelte"
import NewScreenModal from "components/design/NavigationPanel/NewScreenModal.svelte"
import DatasourceModal from "components/design/NavigationPanel/DatasourceModal.svelte"
import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl"
import { Modal, notifications } from "@budibase/bbui"
import { store, selectedAccessRole } from "builderStore"
import analytics, { Events } from "analytics"
import { get } from "svelte/store"
import getTemplates from "builderStore/store/screenTemplates"
import { tables } from "stores/backend"
let pendingScreen
let showProgressCircle = false
@ -13,6 +16,7 @@
// Modal refs
let newScreenModal
let screenDetailsModal
let datasourceModal
// External handler to show the screen wizard
export const showModal = () => {
@ -24,7 +28,7 @@
}
// Creates an array of screens, checking and sanitising their URLs
const createScreens = async screens => {
const createScreens = async ({ screens, screenAccessRole }) => {
if (!screens?.length) {
return
}
@ -46,7 +50,9 @@
screen.routing.route = sanitizeUrl(screen.routing.route)
// Use the currently selected role
screen.routing.roleId = get(selectedAccessRole) || "BASIC"
screen.routing.roleId = screenAccessRole
? screenAccessRole
: get(selectedAccessRole) || "BASIC"
// Create the screen
await store.actions.screens.save(screen)
@ -98,37 +104,54 @@
}
// Handler for NewScreenModal
const confirmScreenSelection = async templates => {
// Handle template selection
if (templates?.length > 1) {
// Autoscreens, so create immediately
const screens = templates.map(template => template.create())
await createScreens(screens)
const confirmScreenSelection = async mode => {
if (mode == "autoCreate") {
datasourceModal.show()
} else {
// Empty screen, so proceed to the next modal
pendingScreen = templates[0].create()
let templates = getTemplates($store, $tables.list)
const blankScreenTemplate = templates.find(
t => t.id === "createFromScratch"
)
pendingScreen = blankScreenTemplate.create()
screenDetailsModal.show()
}
}
// Handler for DatasourceModal
const confirmScreenDatasources = async ({ templates, screenAccessRole }) => {
console.log("selected ", screenAccessRole)
console.log("global ", $selectedAccessRole)
// // Handle template selection
if (templates?.length > 1) {
// Autoscreens, so create immediately
const screens = templates.map(template => template.create())
await createScreens({ screens, screenAccessRole })
}
}
// Handler for ScreenDetailsModal
const confirmScreenDetails = async ({ screenName, screenUrl }) => {
const confirmScreenDetails = async ({ screenUrl, screenAccessRole }) => {
if (!pendingScreen) {
return
}
pendingScreen.props._instanceName = screenName
pendingScreen.routing.route = screenUrl
await createScreens([pendingScreen])
await createScreens({ screens: [pendingScreen], screenAccessRole })
}
</script>
<Modal bind:this={newScreenModal}>
<NewScreenModal onConfirm={confirmScreenSelection} {showProgressCircle} />
<NewScreenModal onConfirm={confirmScreenSelection} />
</Modal>
<Modal bind:this={datasourceModal}>
<DatasourceModal
onConfirm={confirmScreenDatasources}
onCancel={() => newScreenModal.show()}
/>
</Modal>
<Modal bind:this={screenDetailsModal}>
<ScreenDetailsModal
{showProgressCircle}
onConfirm={confirmScreenDetails}
onCancel={() => newScreenModal.show()}
/>