Merge branch 'develop' of github.com:Budibase/budibase into feature/opinionated-sql
This commit is contained in:
commit
df1e15dd55
|
@ -41,6 +41,6 @@
|
||||||
"test:e2e": "lerna run cy:test",
|
"test:e2e": "lerna run cy:test",
|
||||||
"test:e2e:ci": "lerna run cy:ci",
|
"test:e2e:ci": "lerna run cy:ci",
|
||||||
"build:docker": "lerna run build:docker && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh && cd -",
|
"build:docker": "lerna run build:docker && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh && cd -",
|
||||||
"build:docker:staging": "lerna run build:docker && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh staging && cd -"
|
"build:docker:staging": "lerna run build:docker:staging && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh staging && cd -"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,9 +27,17 @@
|
||||||
visible = false
|
visible = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function cancel() {
|
||||||
|
if (!visible) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dispatch("cancel")
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
|
||||||
function handleKey(e) {
|
function handleKey(e) {
|
||||||
if (visible && e.key === "Escape") {
|
if (visible && e.key === "Escape") {
|
||||||
hide()
|
cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +49,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setContext(Context.Modal, { show, hide })
|
setContext(Context.Modal, { show, hide, cancel })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window on:keydown={handleKey} />
|
<svelte:window on:keydown={handleKey} />
|
||||||
|
@ -56,15 +64,17 @@
|
||||||
<Portal target=".modal-container">
|
<Portal target=".modal-container">
|
||||||
<div
|
<div
|
||||||
class="spectrum-Underlay is-open"
|
class="spectrum-Underlay is-open"
|
||||||
transition:fade|local={{ duration: 200 }}
|
in:fade={{ duration: 200 }}
|
||||||
on:mousedown|self={hide}
|
out:fade|local={{ duration: 200 }}
|
||||||
|
on:mousedown|self={cancel}
|
||||||
>
|
>
|
||||||
<div class="modal-wrapper" on:mousedown|self={hide}>
|
<div class="modal-wrapper" on:mousedown|self={cancel}>
|
||||||
<div class="modal-inner-wrapper" on:mousedown|self={hide}>
|
<div class="modal-inner-wrapper" on:mousedown|self={cancel}>
|
||||||
<div
|
<div
|
||||||
use:focusFirstInput
|
use:focusFirstInput
|
||||||
class="spectrum-Modal is-open"
|
class="spectrum-Modal is-open"
|
||||||
transition:fly|local={{ y: 30, duration: 200 }}
|
in:fly={{ y: 30, duration: 200 }}
|
||||||
|
out:fly|local={{ y: 30, duration: 200 }}
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
export let onConfirm = undefined
|
export let onConfirm = undefined
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
|
||||||
const { hide } = getContext(Context.Modal)
|
const { hide, cancel } = getContext(Context.Modal)
|
||||||
let loading = false
|
let loading = false
|
||||||
$: confirmDisabled = disabled || loading
|
$: confirmDisabled = disabled || loading
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@
|
||||||
>
|
>
|
||||||
<slot name="footer" />
|
<slot name="footer" />
|
||||||
{#if showCancelButton}
|
{#if showCancelButton}
|
||||||
<Button group secondary on:click={hide}>{cancelText}</Button>
|
<Button group secondary on:click={cancel}>{cancelText}</Button>
|
||||||
{/if}
|
{/if}
|
||||||
{#if showConfirmButton}
|
{#if showConfirmButton}
|
||||||
<Button
|
<Button
|
||||||
|
|
|
@ -62,6 +62,7 @@ function generateTitleContainer(table, title, formId, repeaterId) {
|
||||||
tableId: table._id,
|
tableId: table._id,
|
||||||
rowId: `{{ ${makePropSafe(repeaterId)}.${makePropSafe("_id")} }}`,
|
rowId: `{{ ${makePropSafe(repeaterId)}.${makePropSafe("_id")} }}`,
|
||||||
revId: `{{ ${makePropSafe(repeaterId)}.${makePropSafe("_rev")} }}`,
|
revId: `{{ ${makePropSafe(repeaterId)}.${makePropSafe("_rev")} }}`,
|
||||||
|
confirm: true,
|
||||||
},
|
},
|
||||||
"##eventHandlerType": "Delete Row",
|
"##eventHandlerType": "Delete Row",
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { Select, Label } from "@budibase/bbui"
|
import { Select, Label, Checkbox, Input } from "@budibase/bbui"
|
||||||
import { store, currentAsset } from "builderStore"
|
import { store, currentAsset } from "builderStore"
|
||||||
import { tables } from "stores/backend"
|
import { tables } from "stores/backend"
|
||||||
import { getBindableProperties } from "builderStore/dataBinding"
|
import { getBindableProperties } from "builderStore/dataBinding"
|
||||||
|
@ -35,6 +35,17 @@
|
||||||
value={parameters.revId}
|
value={parameters.revId}
|
||||||
on:change={value => (parameters.revId = value.detail)}
|
on:change={value => (parameters.revId = value.detail)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Label small />
|
||||||
|
<Checkbox text="Require confirmation" bind:value={parameters.confirm} />
|
||||||
|
|
||||||
|
{#if parameters.confirm}
|
||||||
|
<Label small>Confirm text</Label>
|
||||||
|
<Input
|
||||||
|
placeholder="Are you sure you want to delete this row?"
|
||||||
|
bind:value={parameters.confirmText}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -42,8 +53,8 @@
|
||||||
display: grid;
|
display: grid;
|
||||||
column-gap: var(--spacing-l);
|
column-gap: var(--spacing-l);
|
||||||
row-gap: var(--spacing-s);
|
row-gap: var(--spacing-s);
|
||||||
grid-template-columns: auto 1fr;
|
grid-template-columns: 60px 1fr;
|
||||||
align-items: baseline;
|
align-items: center;
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { Select, Layout } from "@budibase/bbui"
|
import { Select, Layout, Input, Checkbox } from "@budibase/bbui"
|
||||||
import { store, currentAsset } from "builderStore"
|
import { store, currentAsset } from "builderStore"
|
||||||
import { datasources, integrations, queries } from "stores/backend"
|
import { datasources, integrations, queries } from "stores/backend"
|
||||||
import { getBindableProperties } from "builderStore/dataBinding"
|
import { getBindableProperties } from "builderStore/dataBinding"
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Layout>
|
<Layout gap="XS">
|
||||||
<Select
|
<Select
|
||||||
label="Datasource"
|
label="Datasource"
|
||||||
bind:value={parameters.datasourceId}
|
bind:value={parameters.datasourceId}
|
||||||
|
@ -44,22 +44,34 @@
|
||||||
getOptionLabel={query => query.name}
|
getOptionLabel={query => query.name}
|
||||||
getOptionValue={query => query._id}
|
getOptionValue={query => query._id}
|
||||||
/>
|
/>
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if query?.parameters?.length > 0}
|
{#if parameters.queryId}
|
||||||
<div>
|
<Checkbox text="Require confirmation" bind:value={parameters.confirm} />
|
||||||
<ParameterBuilder
|
|
||||||
bind:customParams={parameters.queryParams}
|
{#if parameters.confirm}
|
||||||
parameters={query.parameters}
|
<Input
|
||||||
bindings={bindableProperties}
|
label="Confirm text"
|
||||||
/>
|
placeholder="Are you sure you want to execute this query?"
|
||||||
<IntegrationQueryEditor
|
bind:value={parameters.confirmText}
|
||||||
height={200}
|
/>
|
||||||
{query}
|
{/if}
|
||||||
schema={fetchQueryDefinition(query)}
|
|
||||||
editable={false}
|
{#if query?.parameters?.length > 0}
|
||||||
{datasource}
|
<div>
|
||||||
/>
|
<ParameterBuilder
|
||||||
</div>
|
bind:customParams={parameters.queryParams}
|
||||||
|
parameters={query.parameters}
|
||||||
|
bindings={bindableProperties}
|
||||||
|
/>
|
||||||
|
<IntegrationQueryEditor
|
||||||
|
height={200}
|
||||||
|
{query}
|
||||||
|
schema={fetchQueryDefinition(query)}
|
||||||
|
editable={false}
|
||||||
|
{datasource}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
|
@ -79,7 +79,7 @@
|
||||||
on:click={() => removeField(field[0])}
|
on:click={() => removeField(field[0])}
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
<div>
|
<div style="margin-top: 10px">
|
||||||
<Button icon="AddCircle" secondary on:click={addField}>
|
<Button icon="AddCircle" secondary on:click={addField}>
|
||||||
Add
|
Add
|
||||||
{fieldLabel}
|
{fieldLabel}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { Select, Label, Body } from "@budibase/bbui"
|
import { Select, Label, Body, Checkbox, Input } from "@budibase/bbui"
|
||||||
import { store, currentAsset } from "builderStore"
|
import { store, currentAsset } from "builderStore"
|
||||||
import { tables } from "stores/backend"
|
import { tables } from "stores/backend"
|
||||||
import {
|
import {
|
||||||
|
@ -33,7 +33,8 @@
|
||||||
optional.<br />
|
optional.<br />
|
||||||
You can always add or override fields manually.
|
You can always add or override fields manually.
|
||||||
</Body>
|
</Body>
|
||||||
<div class="fields">
|
|
||||||
|
<div class="params">
|
||||||
<Label small>Data Source</Label>
|
<Label small>Data Source</Label>
|
||||||
<Select
|
<Select
|
||||||
bind:value={parameters.providerId}
|
bind:value={parameters.providerId}
|
||||||
|
@ -51,37 +52,58 @@
|
||||||
getOptionValue={option => option._id}
|
getOptionValue={option => option._id}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{#if parameters.tableId}
|
<Label small />
|
||||||
|
<Checkbox text="Require confirmation" bind:value={parameters.confirm} />
|
||||||
|
|
||||||
|
{#if parameters.confirm}
|
||||||
|
<Label small>Confirm text</Label>
|
||||||
|
<Input
|
||||||
|
placeholder="Are you sure you want to save this row?"
|
||||||
|
bind:value={parameters.confirmText}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if parameters.tableId}
|
||||||
|
<div class="fields">
|
||||||
<SaveFields
|
<SaveFields
|
||||||
parameterFields={parameters.fields}
|
parameterFields={parameters.fields}
|
||||||
{schemaFields}
|
{schemaFields}
|
||||||
on:change={onFieldsChanged}
|
on:change={onFieldsChanged}
|
||||||
/>
|
/>
|
||||||
{/if}
|
</div>
|
||||||
</div>
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.root {
|
.root {
|
||||||
|
width: 100%;
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: var(--spacing-xl);
|
||||||
}
|
}
|
||||||
|
|
||||||
.root :global(p) {
|
.root :global(p) {
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.params {
|
||||||
|
display: grid;
|
||||||
|
column-gap: var(--spacing-l);
|
||||||
|
row-gap: var(--spacing-s);
|
||||||
|
grid-template-columns: 60px 1fr;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
.fields {
|
.fields {
|
||||||
display: grid;
|
display: grid;
|
||||||
column-gap: var(--spacing-l);
|
column-gap: var(--spacing-l);
|
||||||
row-gap: var(--spacing-s);
|
row-gap: var(--spacing-s);
|
||||||
grid-template-columns: auto 1fr auto 1fr auto;
|
grid-template-columns: 60px 1fr auto 1fr auto;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fields :global(> div:nth-child(2)),
|
|
||||||
.fields :global(> div:nth-child(4)) {
|
|
||||||
grid-column-start: 2;
|
|
||||||
grid-column-end: 6;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { Select, Label, Input } from "@budibase/bbui"
|
import { Select, Label, Input, Checkbox } from "@budibase/bbui"
|
||||||
import { automationStore } from "builderStore"
|
import { automationStore } from "builderStore"
|
||||||
import SaveFields from "./SaveFields.svelte"
|
import SaveFields from "./SaveFields.svelte"
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="fields">
|
<div class="params">
|
||||||
<Label small>Automation</Label>
|
<Label small>Automation</Label>
|
||||||
|
|
||||||
{#if automationStatus === AUTOMATION_STATUS.EXISTING}
|
{#if automationStatus === AUTOMATION_STATUS.EXISTING}
|
||||||
|
@ -90,6 +90,19 @@
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<Label small />
|
||||||
|
<Checkbox text="Require confirmation" bind:value={parameters.confirm} />
|
||||||
|
|
||||||
|
{#if parameters.confirm}
|
||||||
|
<Label small>Confirm text</Label>
|
||||||
|
<Input
|
||||||
|
placeholder="Are you sure you want to trigger this automation?"
|
||||||
|
bind:value={parameters.confirmText}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="fields">
|
||||||
{#key parameters.automationId}
|
{#key parameters.automationId}
|
||||||
<SaveFields
|
<SaveFields
|
||||||
schemaFields={selectedSchema}
|
schemaFields={selectedSchema}
|
||||||
|
@ -107,16 +120,21 @@
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fields {
|
.params {
|
||||||
display: grid;
|
display: grid;
|
||||||
column-gap: var(--spacing-l);
|
column-gap: var(--spacing-l);
|
||||||
row-gap: var(--spacing-s);
|
row-gap: var(--spacing-s);
|
||||||
grid-template-columns: auto 1fr auto 1fr auto;
|
grid-template-columns: 60px 1fr;
|
||||||
align-items: baseline;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fields :global(> div:nth-child(2)) {
|
.fields {
|
||||||
grid-column: 2 / span 4;
|
margin-top: var(--spacing-l);
|
||||||
|
display: grid;
|
||||||
|
column-gap: var(--spacing-l);
|
||||||
|
row-gap: var(--spacing-s);
|
||||||
|
grid-template-columns: 60px 1fr auto 1fr auto;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.radios,
|
.radios,
|
||||||
|
|
|
@ -24,15 +24,12 @@
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.root {
|
.root {
|
||||||
display: flex;
|
display: grid;
|
||||||
flex-direction: row;
|
column-gap: var(--spacing-l);
|
||||||
|
row-gap: var(--spacing-s);
|
||||||
|
grid-template-columns: 60px 1fr;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.root :global(> div) {
|
|
||||||
flex: 1;
|
|
||||||
margin-left: var(--spacing-l);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
import { setContext, onMount } from "svelte"
|
import { setContext, onMount } from "svelte"
|
||||||
import Component from "./Component.svelte"
|
import Component from "./Component.svelte"
|
||||||
import NotificationDisplay from "./NotificationDisplay.svelte"
|
import NotificationDisplay from "./NotificationDisplay.svelte"
|
||||||
|
import ConfirmationDisplay from "./ConfirmationDisplay.svelte"
|
||||||
import Provider from "./Provider.svelte"
|
import Provider from "./Provider.svelte"
|
||||||
import SDK from "../sdk"
|
import SDK from "../sdk"
|
||||||
import {
|
import {
|
||||||
|
@ -70,6 +71,7 @@
|
||||||
{/key}
|
{/key}
|
||||||
</div>
|
</div>
|
||||||
<NotificationDisplay />
|
<NotificationDisplay />
|
||||||
|
<ConfirmationDisplay />
|
||||||
<!-- Key block needs to be outside the if statement or it breaks -->
|
<!-- Key block needs to be outside the if statement or it breaks -->
|
||||||
{#key $builderStore.selectedComponentId}
|
{#key $builderStore.selectedComponentId}
|
||||||
{#if $builderStore.inBuilder}
|
{#if $builderStore.inBuilder}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
<script>
|
||||||
|
import { confirmationStore } from "../store"
|
||||||
|
import { Modal, ModalContent } from "@budibase/bbui"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if $confirmationStore.showConfirmation}
|
||||||
|
<Modal fixed on:cancel={confirmationStore.actions.cancel}>
|
||||||
|
<ModalContent
|
||||||
|
title={$confirmationStore.title}
|
||||||
|
onConfirm={confirmationStore.actions.confirm}
|
||||||
|
>
|
||||||
|
{$confirmationStore.text}
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
{/if}
|
|
@ -1,40 +1,11 @@
|
||||||
import * as API from "../api"
|
import * as API from "../api"
|
||||||
import { writable, get } from "svelte/store"
|
import { writable, get } from "svelte/store"
|
||||||
import { initialise } from "./initialise"
|
|
||||||
import { routeStore } from "./routes"
|
|
||||||
import { builderStore } from "./builder"
|
import { builderStore } from "./builder"
|
||||||
import { TableNames } from "../constants"
|
import { TableNames } from "../constants"
|
||||||
|
|
||||||
const createAuthStore = () => {
|
const createAuthStore = () => {
|
||||||
const store = writable(null)
|
const store = writable(null)
|
||||||
|
|
||||||
const goToDefaultRoute = () => {
|
|
||||||
// Setting the active route forces an update of the active screen ID,
|
|
||||||
// even if we're on the same URL
|
|
||||||
routeStore.actions.setActiveRoute("/")
|
|
||||||
|
|
||||||
// Navigating updates the URL to reflect this route
|
|
||||||
routeStore.actions.navigate("/")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Logs a user in
|
|
||||||
const logIn = async ({ email, password }) => {
|
|
||||||
const auth = await API.logIn({ email, password })
|
|
||||||
if (auth.success) {
|
|
||||||
await fetchUser()
|
|
||||||
await initialise()
|
|
||||||
goToDefaultRoute()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Logs a user out
|
|
||||||
const logOut = async () => {
|
|
||||||
store.set(null)
|
|
||||||
window.document.cookie = `budibase:auth=; budibase:currentapp=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;`
|
|
||||||
await initialise()
|
|
||||||
goToDefaultRoute()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetches the user object if someone is logged in and has reloaded the page
|
// Fetches the user object if someone is logged in and has reloaded the page
|
||||||
const fetchUser = async () => {
|
const fetchUser = async () => {
|
||||||
// Fetch the first user if inside the builder
|
// Fetch the first user if inside the builder
|
||||||
|
@ -54,7 +25,7 @@ const createAuthStore = () => {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
subscribe: store.subscribe,
|
subscribe: store.subscribe,
|
||||||
actions: { logIn, logOut, fetchUser },
|
actions: { fetchUser },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
import { writable, get } from "svelte/store"
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
showConfirmation: false,
|
||||||
|
title: null,
|
||||||
|
text: null,
|
||||||
|
callback: null,
|
||||||
|
}
|
||||||
|
|
||||||
|
const createConfirmationStore = () => {
|
||||||
|
const store = writable(initialState)
|
||||||
|
|
||||||
|
const showConfirmation = (title, text, callback) => {
|
||||||
|
store.set({
|
||||||
|
showConfirmation: true,
|
||||||
|
title,
|
||||||
|
text,
|
||||||
|
callback,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const confirm = async () => {
|
||||||
|
const state = get(store)
|
||||||
|
if (!state.showConfirmation || !state.callback) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
store.set(initialState)
|
||||||
|
await state.callback()
|
||||||
|
}
|
||||||
|
const cancel = () => {
|
||||||
|
store.set(initialState)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
subscribe: store.subscribe,
|
||||||
|
actions: { showConfirmation, confirm, cancel },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const confirmationStore = createConfirmationStore()
|
|
@ -4,6 +4,7 @@ export { routeStore } from "./routes"
|
||||||
export { screenStore } from "./screens"
|
export { screenStore } from "./screens"
|
||||||
export { builderStore } from "./builder"
|
export { builderStore } from "./builder"
|
||||||
export { dataSourceStore } from "./dataSource"
|
export { dataSourceStore } from "./dataSource"
|
||||||
|
export { confirmationStore } from "./confirmation"
|
||||||
|
|
||||||
// Context stores are layered and duplicated, so it is not a singleton
|
// Context stores are layered and duplicated, so it is not a singleton
|
||||||
export { createContextStore } from "./context"
|
export { createContextStore } from "./context"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
import { routeStore, builderStore, authStore } from "../store"
|
import { routeStore, builderStore, confirmationStore } from "../store"
|
||||||
import { saveRow, deleteRow, executeQuery, triggerAutomation } from "../api"
|
import { saveRow, deleteRow, executeQuery, triggerAutomation } from "../api"
|
||||||
import { ActionTypes } from "../constants"
|
import { ActionTypes } from "../constants"
|
||||||
|
|
||||||
|
@ -68,15 +68,6 @@ const refreshDatasourceHandler = async (action, context) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const loginHandler = async action => {
|
|
||||||
const { email, password } = action.parameters
|
|
||||||
await authStore.actions.logIn({ email, password })
|
|
||||||
}
|
|
||||||
|
|
||||||
const logoutHandler = async () => {
|
|
||||||
await authStore.actions.logOut()
|
|
||||||
}
|
|
||||||
|
|
||||||
const handlerMap = {
|
const handlerMap = {
|
||||||
["Save Row"]: saveRowHandler,
|
["Save Row"]: saveRowHandler,
|
||||||
["Delete Row"]: deleteRowHandler,
|
["Delete Row"]: deleteRowHandler,
|
||||||
|
@ -85,13 +76,19 @@ const handlerMap = {
|
||||||
["Trigger Automation"]: triggerAutomationHandler,
|
["Trigger Automation"]: triggerAutomationHandler,
|
||||||
["Validate Form"]: validateFormHandler,
|
["Validate Form"]: validateFormHandler,
|
||||||
["Refresh Datasource"]: refreshDatasourceHandler,
|
["Refresh Datasource"]: refreshDatasourceHandler,
|
||||||
["Log In"]: loginHandler,
|
}
|
||||||
["Log Out"]: logoutHandler,
|
|
||||||
|
const confirmTextMap = {
|
||||||
|
["Delete Row"]: "Are you sure you want to delete this row?",
|
||||||
|
["Save Row"]: "Are you sure you want to save this row?",
|
||||||
|
["Execute Query"]: "Are you sure you want to execute this query?",
|
||||||
|
["Trigger Automation"]: "Are you sure you want to trigger this automation?",
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses an array of actions and returns a function which will execute the
|
* Parses an array of actions and returns a function which will execute the
|
||||||
* actions in the current context.
|
* actions in the current context.
|
||||||
|
* A handler returning `false` is a flag to stop execution of handlers
|
||||||
*/
|
*/
|
||||||
export const enrichButtonActions = (actions, context) => {
|
export const enrichButtonActions = (actions, context) => {
|
||||||
// Prevent button actions in the builder preview
|
// Prevent button actions in the builder preview
|
||||||
|
@ -102,15 +99,44 @@ export const enrichButtonActions = (actions, context) => {
|
||||||
return async () => {
|
return async () => {
|
||||||
for (let i = 0; i < handlers.length; i++) {
|
for (let i = 0; i < handlers.length; i++) {
|
||||||
try {
|
try {
|
||||||
const result = await handlers[i](actions[i], context)
|
const action = actions[i]
|
||||||
// A handler returning `false` is a flag to stop execution of handlers
|
const callback = async () => handlers[i](action, context)
|
||||||
if (result === false) {
|
|
||||||
|
// If this action is confirmable, show confirmation and await a
|
||||||
|
// callback to execute further actions
|
||||||
|
if (action.parameters?.confirm) {
|
||||||
|
const defaultText = confirmTextMap[action["##eventHandlerType"]]
|
||||||
|
const confirmText = action.parameters?.confirmText || defaultText
|
||||||
|
confirmationStore.actions.showConfirmation(
|
||||||
|
action["##eventHandlerType"],
|
||||||
|
confirmText,
|
||||||
|
async () => {
|
||||||
|
// When confirmed, execute this action immediately,
|
||||||
|
// then execute the rest of the actions in the chain
|
||||||
|
const result = await callback()
|
||||||
|
if (result !== false) {
|
||||||
|
const next = enrichButtonActions(actions.slice(i + 1), context)
|
||||||
|
await next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Stop enriching actions when encountering a confirmable action,
|
||||||
|
// as the callback continues the action chain
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For non-confirmable actions, execute the handler immediately
|
||||||
|
else {
|
||||||
|
const result = await callback()
|
||||||
|
if (result === false) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error while executing button handler")
|
console.error("Error while executing button handler")
|
||||||
console.error(error)
|
console.error(error)
|
||||||
// Stop executing on an error
|
// Stop executing further actions on error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
"test:integration": "jest --coverage --detectOpenHandles",
|
"test:integration": "jest --coverage --detectOpenHandles",
|
||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
"build:docker": "docker build . -t app-service",
|
"build:docker": "docker build . -t app-service",
|
||||||
|
"build:docker:staging": "docker build . -t app-service:staging",
|
||||||
"run:docker": "node src/index",
|
"run:docker": "node src/index",
|
||||||
"dev:stack:up": "node scripts/dev/manage.js up",
|
"dev:stack:up": "node scripts/dev/manage.js up",
|
||||||
"dev:stack:down": "node scripts/dev/manage.js down",
|
"dev:stack:down": "node scripts/dev/manage.js down",
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"run:docker": "node src/index.js",
|
"run:docker": "node src/index.js",
|
||||||
"build:docker": "docker build . -t worker-service",
|
"build:docker": "docker build . -t worker-service",
|
||||||
|
"build:docker:staging": "docker build . -t worker-service:staging",
|
||||||
"dev:stack:init": "node ./scripts/dev/manage.js init",
|
"dev:stack:init": "node ./scripts/dev/manage.js init",
|
||||||
"dev:builder": "npm run dev:stack:init && nodemon src/index.js",
|
"dev:builder": "npm run dev:stack:init && nodemon src/index.js",
|
||||||
"test": "jest --runInBand"
|
"test": "jest --runInBand"
|
||||||
|
|
Loading…
Reference in New Issue