binding data context in component
This commit is contained in:
parent
98a7085bbc
commit
d710874ef1
|
@ -1,43 +0,0 @@
|
||||||
<script>
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
TextButton as Button,
|
|
||||||
Icon,
|
|
||||||
Modal,
|
|
||||||
ModalContent,
|
|
||||||
} from "@budibase/bbui"
|
|
||||||
import { backendUiStore } from "builderStore"
|
|
||||||
import api from "builderStore/api"
|
|
||||||
import EditIntegrationConfig from "../modals/EditIntegrationConfig.svelte"
|
|
||||||
|
|
||||||
export let table
|
|
||||||
|
|
||||||
$: console.log("The table config is", table)
|
|
||||||
|
|
||||||
let modal
|
|
||||||
|
|
||||||
// TODO: revisit
|
|
||||||
async function saveTable() {
|
|
||||||
const SAVE_TABLE_URL = `/api/tables`
|
|
||||||
const response = await api.post(SAVE_TABLE_URL, table)
|
|
||||||
const savedTable = await response.json()
|
|
||||||
await backendUiStore.actions.tables.fetch()
|
|
||||||
backendUiStore.actions.tables.select(savedTable)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Button text small on:click={modal.show}>
|
|
||||||
<Icon name="edit" />
|
|
||||||
Configure Datasource
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<Modal bind:this={modal}>
|
|
||||||
<ModalContent
|
|
||||||
confirmText="Save"
|
|
||||||
cancelText="Cancel"
|
|
||||||
onConfirm={saveTable}
|
|
||||||
title={'Datasource Configuration'}>
|
|
||||||
<EditIntegrationConfig onClosed={modal.hide} bind:table />
|
|
||||||
</ModalContent>
|
|
||||||
</Modal>
|
|
|
@ -3,15 +3,19 @@
|
||||||
import Portal from "svelte-portal"
|
import Portal from "svelte-portal"
|
||||||
|
|
||||||
export let title
|
export let title
|
||||||
export let onClose
|
export let onClose = () => {}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Portal>
|
<Portal>
|
||||||
<section class="drawer" transition:slide>
|
<section class="drawer" transition:slide>
|
||||||
{#if title}
|
<header>
|
||||||
<heading>{title}</heading>
|
{title}
|
||||||
{/if}
|
<div class="controls">
|
||||||
<slot />
|
<slot name="buttons" />
|
||||||
|
<i class="ri-close-fill close" on:click={onClose} />
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<slot name="body" />
|
||||||
</section>
|
</section>
|
||||||
</Portal>
|
</Portal>
|
||||||
|
|
||||||
|
@ -20,10 +24,28 @@
|
||||||
height: 50vh;
|
height: 50vh;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 97%;
|
width: 100vw;
|
||||||
background: var(--background);
|
background: var(--background);
|
||||||
padding: var(--spacing-xl);
|
|
||||||
border-top: var(--border-light);
|
border-top: var(--border-light);
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
border: var(--border-light);
|
||||||
|
padding: var(--spacing-s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
display: grid;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
grid-gap: var(--spacing-m);
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
font-size: var(--font-size-xl);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -44,4 +44,7 @@
|
||||||
background: var(--background);
|
background: var(--background);
|
||||||
border-radius: var(--border-radius-m);
|
border-radius: var(--border-radius-m);
|
||||||
}
|
}
|
||||||
|
:global(.CodeMirror) {
|
||||||
|
border-radius: var(--border-radius-m);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { TextArea, Label, Input, Heading } from "@budibase/bbui"
|
import { Button, TextArea, Label, Input, Heading } from "@budibase/bbui"
|
||||||
import Editor from "./QueryEditor.svelte"
|
import Editor from "./QueryEditor.svelte"
|
||||||
import BindableInput from "components/userInterface/BindableInput.svelte"
|
import BindableInput from "components/userInterface/BindableInput.svelte"
|
||||||
|
|
||||||
|
@ -17,26 +17,28 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Heading extraSmall black>Parameters</Heading>
|
<section>
|
||||||
<div class="parameters">
|
<Heading extraSmall black>Parameters</Heading>
|
||||||
<Label extraSmall grey>Parameter Name</Label>
|
<div class="parameters">
|
||||||
<Label extraSmall grey>Default</Label>
|
<Label extraSmall grey>Parameter Name</Label>
|
||||||
<Label extraSmall grey>Value</Label>
|
<Label extraSmall grey>Default</Label>
|
||||||
<div />
|
<Label extraSmall grey>Value</Label>
|
||||||
{#each parameters as parameter, idx}
|
<div />
|
||||||
<Input thin bind:value={parameter.name} />
|
{#each parameters as parameter, idx}
|
||||||
<Input thin bind:value={parameter.default} />
|
<Input thin bind:value={parameter.name} />
|
||||||
<BindableInput
|
<Input thin bind:value={parameter.default} />
|
||||||
type="string"
|
<BindableInput
|
||||||
thin
|
type="string"
|
||||||
bind:value={customParams[parameter.name]}
|
thin
|
||||||
{bindings} />
|
bind:value={customParams[parameter.name]}
|
||||||
<i
|
{bindings} />
|
||||||
class="ri-close-circle-line delete"
|
<i
|
||||||
on:click={() => deleteQueryParameter(idx)} />
|
class="ri-close-circle-line delete"
|
||||||
{/each}
|
on:click={() => deleteQueryParameter(idx)} />
|
||||||
</div>
|
{/each}
|
||||||
<i class="ri-add-circle-line add" on:click={newQueryParameter} />
|
</div>
|
||||||
|
<Button thin secondary on:click={newQueryParameter}>New Parameter</Button>
|
||||||
|
</section>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.parameters {
|
.parameters {
|
||||||
|
@ -44,9 +46,6 @@
|
||||||
grid-template-columns: 1fr 1fr 1fr 5%;
|
grid-template-columns: 1fr 1fr 1fr 5%;
|
||||||
grid-gap: 10px;
|
grid-gap: 10px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
margin-bottom: var(--spacing-xl);
|
||||||
|
|
||||||
.add {
|
|
||||||
margin-top: var(--spacing-m);
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { TextArea, Label, Input, Heading } from "@budibase/bbui"
|
import { TextArea, Label, Input, Heading, Spacer } from "@budibase/bbui"
|
||||||
import Editor from "./QueryEditor.svelte"
|
import Editor from "./QueryEditor.svelte"
|
||||||
import ParameterBuilder from "./QueryParameterBuilder.svelte"
|
import ParameterBuilder from "./QueryParameterBuilder.svelte"
|
||||||
|
|
||||||
|
@ -12,5 +12,6 @@
|
||||||
|
|
||||||
{#if query.queryType === QueryTypes.SQL}
|
{#if query.queryType === QueryTypes.SQL}
|
||||||
<ParameterBuilder bind:parameters={query.parameters} />
|
<ParameterBuilder bind:parameters={query.parameters} />
|
||||||
|
<Spacer large />
|
||||||
<Editor label="Query" bind:value={query.queryString} />
|
<Editor label="Query" bind:value={query.queryString} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -1,35 +1,62 @@
|
||||||
<script>
|
<script>
|
||||||
import BottomDrawer from "components/common/BottomDrawer.svelte"
|
import BottomDrawer from "components/common/BottomDrawer.svelte"
|
||||||
import { store, backendUiStore } from "builderStore"
|
import { Button } from "@budibase/bbui"
|
||||||
|
import { store, backendUiStore, currentAsset } from "builderStore"
|
||||||
import { slide } from "svelte/transition"
|
import { slide } from "svelte/transition"
|
||||||
import QueryInterface from "components/integration/QueryViewer.svelte"
|
import fetchBindableProperties from "builderStore/fetchBindableProperties"
|
||||||
|
import ParameterBuilder from "components/integration/QueryParameterBuilder.svelte"
|
||||||
|
|
||||||
$: query = $backendUiStore.queries.find(
|
export let query
|
||||||
query => query._id === $backendUiStore.selectedQueryId
|
export let parameters = {}
|
||||||
)
|
|
||||||
|
$: console.log("CUSTOM PARAMS", parameters)
|
||||||
|
|
||||||
|
$: bindableProperties = fetchBindableProperties({
|
||||||
|
componentInstanceId: $store.selectedComponentId,
|
||||||
|
components: $store.components,
|
||||||
|
screen: $currentAsset,
|
||||||
|
tables: $backendUiStore.tables,
|
||||||
|
}).map(property => ({
|
||||||
|
...property,
|
||||||
|
category: property.type === "instance" ? "Component" : "Table",
|
||||||
|
label: property.readableBinding,
|
||||||
|
path: property.runtimeBinding,
|
||||||
|
}))
|
||||||
|
|
||||||
function closeDatabindingDrawer() {
|
function closeDatabindingDrawer() {
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
state.bindingDrawerVisible = false
|
state.bottomDrawerVisible = false
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function saveComponentQuery() {
|
||||||
|
// save the parameters to the datasource of the component
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if query}
|
{#if query}
|
||||||
<BottomDrawer>
|
<BottomDrawer title={'Query'} onClose={closeDatabindingDrawer}>
|
||||||
<div class="drawer-contents">
|
<div slot="buttons">
|
||||||
<i class="ri-close-fill close" on:click={closeDatabindingDrawer} />
|
<Button blue thin on:click={saveComponentQuery}>Save</Button>
|
||||||
<QueryInterface {query} />
|
</div>
|
||||||
|
<div class="drawer-contents" slot="body">
|
||||||
|
<pre>{query.queryString}</pre>
|
||||||
|
<ParameterBuilder
|
||||||
|
bind:customParams={parameters}
|
||||||
|
parameters={query.parameters}
|
||||||
|
bindings={bindableProperties} />
|
||||||
</div>
|
</div>
|
||||||
</BottomDrawer>
|
</BottomDrawer>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
i {
|
.drawer-contents {
|
||||||
position: absolute;
|
display: grid;
|
||||||
top: var(--spacing-xl);
|
grid-auto-flow: column;
|
||||||
right: var(--spacing-xl);
|
grid-template-columns: 20% 1fr;
|
||||||
font-size: var(--font-size-m);
|
grid-gap: var(--spacing-m);
|
||||||
|
height: 100%;
|
||||||
|
padding: var(--spacing-xl);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,200 @@
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
TextButton,
|
||||||
|
Body,
|
||||||
|
DropdownMenu,
|
||||||
|
ModalContent,
|
||||||
|
} from "@budibase/bbui"
|
||||||
|
import { AddIcon, ArrowDownIcon } from "components/common/Icons/"
|
||||||
|
import actionTypes from "./actions"
|
||||||
|
import { createEventDispatcher } from "svelte"
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
const eventTypeKey = "##eventHandlerType"
|
||||||
|
|
||||||
|
export let event
|
||||||
|
|
||||||
|
let addActionButton
|
||||||
|
let addActionDropdown
|
||||||
|
let selectedAction
|
||||||
|
|
||||||
|
let draftEventHandler = { parameters: [] }
|
||||||
|
|
||||||
|
$: actions = event || []
|
||||||
|
$: selectedActionComponent =
|
||||||
|
selectedAction &&
|
||||||
|
actionTypes.find(t => t.name === selectedAction[eventTypeKey]).component
|
||||||
|
|
||||||
|
const updateEventHandler = (updatedHandler, index) => {
|
||||||
|
actions[index] = updatedHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteAction = index => {
|
||||||
|
actions.splice(index, 1)
|
||||||
|
actions = actions
|
||||||
|
}
|
||||||
|
|
||||||
|
const addAction = actionType => () => {
|
||||||
|
const newAction = {
|
||||||
|
parameters: {},
|
||||||
|
[eventTypeKey]: actionType.name,
|
||||||
|
}
|
||||||
|
actions.push(newAction)
|
||||||
|
selectedAction = newAction
|
||||||
|
actions = actions
|
||||||
|
addActionDropdown.hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectAction = action => () => {
|
||||||
|
selectedAction = action
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveEventData = () => {
|
||||||
|
dispatch("change", actions)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="actions-container">
|
||||||
|
<div class="actions-list">
|
||||||
|
<div>
|
||||||
|
<div bind:this={addActionButton}>
|
||||||
|
<TextButton text small blue on:click={addActionDropdown.show}>
|
||||||
|
<div style="height: 20px; width: 20px;">
|
||||||
|
<AddIcon />
|
||||||
|
</div>
|
||||||
|
Add Action
|
||||||
|
</TextButton>
|
||||||
|
</div>
|
||||||
|
<DropdownMenu
|
||||||
|
bind:this={addActionDropdown}
|
||||||
|
anchor={addActionButton}
|
||||||
|
align="right">
|
||||||
|
<div class="available-actions-container">
|
||||||
|
{#each actionTypes as actionType}
|
||||||
|
<div class="available-action" on:click={addAction(actionType)}>
|
||||||
|
<span>{actionType.name}</span>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if actions && actions.length > 0}
|
||||||
|
{#each actions as action, index}
|
||||||
|
<div class="action-container">
|
||||||
|
<div class="action-header" on:click={selectAction(action)}>
|
||||||
|
<span class:selected={action === selectedAction}>
|
||||||
|
{index + 1}. {action[eventTypeKey]}
|
||||||
|
</span>
|
||||||
|
<!-- <Body small lh>{index + 1}. {action[eventTypeKey]}</Body> -->
|
||||||
|
<!-- <div class="row-expander" class:rotate={action !== selectedAction}>
|
||||||
|
<ArrowDownIcon />
|
||||||
|
</div> -->
|
||||||
|
</div>
|
||||||
|
<!-- {#if action === selectedAction}
|
||||||
|
<div class="selected-action-container">
|
||||||
|
<svelte:component
|
||||||
|
this={selectedActionComponent}
|
||||||
|
parameters={selectedAction.parameters} />
|
||||||
|
<div class="delete-action-button">
|
||||||
|
<TextButton text medium on:click={() => deleteAction(index)}>
|
||||||
|
Delete
|
||||||
|
</TextButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if} -->
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="action-config">
|
||||||
|
{#if selectedAction}
|
||||||
|
<div class="selected-action-container">
|
||||||
|
<svelte:component
|
||||||
|
this={selectedActionComponent}
|
||||||
|
parameters={selectedAction.parameters} />
|
||||||
|
<div class="delete-action-button">
|
||||||
|
<!-- <TextButton text medium on:click={() => deleteAction(index)}>
|
||||||
|
Delete
|
||||||
|
</TextButton> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<Button thin blue on:click={saveEventData}>Save</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a href="https://docs.budibase.com">Learn more about Actions</a>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.action-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-header > span {
|
||||||
|
margin-bottom: var(--spacing-m);
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-header > span:hover,
|
||||||
|
.selected {
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions-list {
|
||||||
|
border: var(--border-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.available-action {
|
||||||
|
padding: var(--spacing-s);
|
||||||
|
font-size: var(--font-size-m);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.available-action:hover {
|
||||||
|
background: var(--grey-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions-container {
|
||||||
|
display: grid;
|
||||||
|
grid-gap: var(--spacing-m);
|
||||||
|
grid-template-columns: 15% 1fr;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
min-height: 0;
|
||||||
|
padding-top: 0;
|
||||||
|
border: var(--border-light);
|
||||||
|
border-width: 0 0 1px 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-container {
|
||||||
|
border: var(--border-light);
|
||||||
|
border-width: 1px 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-action-container {
|
||||||
|
padding-bottom: var(--spacing-s);
|
||||||
|
padding-top: var(--spacing-s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-action-button {
|
||||||
|
padding-top: var(--spacing-l);
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
flex: 1;
|
||||||
|
color: var(--grey-5);
|
||||||
|
font-size: var(--font-size-s);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: var(--blue);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,176 +0,0 @@
|
||||||
<script>
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
TextButton,
|
|
||||||
Body,
|
|
||||||
DropdownMenu,
|
|
||||||
ModalContent,
|
|
||||||
} from "@budibase/bbui"
|
|
||||||
import { AddIcon, ArrowDownIcon } from "components/common/Icons/"
|
|
||||||
import actionTypes from "./actions"
|
|
||||||
import { createEventDispatcher } from "svelte"
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
|
||||||
const eventTypeKey = "##eventHandlerType"
|
|
||||||
|
|
||||||
export let event
|
|
||||||
|
|
||||||
let addActionButton
|
|
||||||
let addActionDropdown
|
|
||||||
let selectedAction
|
|
||||||
|
|
||||||
let draftEventHandler = { parameters: [] }
|
|
||||||
|
|
||||||
$: actions = event || []
|
|
||||||
$: selectedActionComponent =
|
|
||||||
selectedAction &&
|
|
||||||
actionTypes.find(t => t.name === selectedAction[eventTypeKey]).component
|
|
||||||
|
|
||||||
const updateEventHandler = (updatedHandler, index) => {
|
|
||||||
actions[index] = updatedHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteAction = index => {
|
|
||||||
actions.splice(index, 1)
|
|
||||||
actions = actions
|
|
||||||
}
|
|
||||||
|
|
||||||
const addAction = actionType => () => {
|
|
||||||
const newAction = {
|
|
||||||
parameters: {},
|
|
||||||
[eventTypeKey]: actionType.name,
|
|
||||||
}
|
|
||||||
actions.push(newAction)
|
|
||||||
selectedAction = newAction
|
|
||||||
actions = actions
|
|
||||||
addActionDropdown.hide()
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectAction = action => () => {
|
|
||||||
selectedAction = action
|
|
||||||
}
|
|
||||||
|
|
||||||
const saveEventData = () => {
|
|
||||||
dispatch("change", actions)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div bind:this={addActionButton}>
|
|
||||||
<TextButton text small blue on:click={addActionDropdown.show}>
|
|
||||||
<div style="height: 20px; width: 20px;">
|
|
||||||
<AddIcon />
|
|
||||||
</div>
|
|
||||||
Add Action
|
|
||||||
</TextButton>
|
|
||||||
</div>
|
|
||||||
<DropdownMenu
|
|
||||||
bind:this={addActionDropdown}
|
|
||||||
anchor={addActionButton}
|
|
||||||
align="right">
|
|
||||||
<div class="available-actions-container">
|
|
||||||
{#each actionTypes as actionType}
|
|
||||||
<div class="available-action" on:click={addAction(actionType)}>
|
|
||||||
<span>{actionType.name}</span>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</DropdownMenu>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="actions-container">
|
|
||||||
{#if actions && actions.length > 0}
|
|
||||||
{#each actions as action, index}
|
|
||||||
<div class="action-container">
|
|
||||||
<div class="action-header" on:click={selectAction(action)}>
|
|
||||||
<Body small lh>{index + 1}. {action[eventTypeKey]}</Body>
|
|
||||||
<div class="row-expander" class:rotate={action !== selectedAction}>
|
|
||||||
<ArrowDownIcon />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{#if action === selectedAction}
|
|
||||||
<div class="selected-action-container">
|
|
||||||
<svelte:component
|
|
||||||
this={selectedActionComponent}
|
|
||||||
parameters={selectedAction.parameters} />
|
|
||||||
<div class="delete-action-button">
|
|
||||||
<TextButton text medium on:click={() => deleteAction(index)}>
|
|
||||||
Delete
|
|
||||||
</TextButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
{/if}
|
|
||||||
<Button thin blue on:click={saveEventData}>Save</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a href="https://docs.budibase.com">Learn more about Actions</a>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.action-header {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-header > p {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row-expander {
|
|
||||||
height: 30px;
|
|
||||||
width: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.available-action {
|
|
||||||
padding: var(--spacing-s);
|
|
||||||
font-size: var(--font-size-m);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.available-action:hover {
|
|
||||||
background: var(--grey-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions-container {
|
|
||||||
flex: 1;
|
|
||||||
min-height: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
border: var(--border-light);
|
|
||||||
border-width: 0 0 1px 0;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-container {
|
|
||||||
border: var(--border-light);
|
|
||||||
border-width: 1px 0 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected-action-container {
|
|
||||||
padding-bottom: var(--spacing-s);
|
|
||||||
padding-top: var(--spacing-s);
|
|
||||||
}
|
|
||||||
|
|
||||||
.delete-action-button {
|
|
||||||
padding-top: var(--spacing-l);
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
flex: 1;
|
|
||||||
color: var(--grey-5);
|
|
||||||
font-size: var(--font-size-s);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
a:hover {
|
|
||||||
color: var(--blue);
|
|
||||||
}
|
|
||||||
|
|
||||||
.rotate :global(svg) {
|
|
||||||
transform: rotate(90deg);
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -2,7 +2,7 @@
|
||||||
import { Button, Modal } from "@budibase/bbui"
|
import { Button, Modal } from "@budibase/bbui"
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import { store } from "builderStore"
|
import { store } from "builderStore"
|
||||||
import EventEditorModal from "./EventEditorModal.svelte"
|
import EventEditor from "./EventEditor.svelte"
|
||||||
import BottomDrawer from "components/common/BottomDrawer.svelte"
|
import BottomDrawer from "components/common/BottomDrawer.svelte"
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
@ -20,7 +20,12 @@
|
||||||
<Button secondary small on:click={showDrawer}>Define Actions</Button>
|
<Button secondary small on:click={showDrawer}>Define Actions</Button>
|
||||||
|
|
||||||
{#if drawerVisible}
|
{#if drawerVisible}
|
||||||
<BottomDrawer>
|
<BottomDrawer title={'Actions'} onClose={() => (drawerVisible = false)}>
|
||||||
<EventEditorModal event={value} eventType={name} on:change />
|
<heading slot="buttons">
|
||||||
|
<Button thin blue>Save</Button>
|
||||||
|
</heading>
|
||||||
|
<div slot="body">
|
||||||
|
<EventEditor event={value} eventType={name} on:change />
|
||||||
|
</div>
|
||||||
</BottomDrawer>
|
</BottomDrawer>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -2,10 +2,14 @@
|
||||||
import { Button, Icon, DropdownMenu, Spacer, Heading } from "@budibase/bbui"
|
import { Button, Icon, DropdownMenu, Spacer, Heading } from "@budibase/bbui"
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import { store, backendUiStore, currentAsset } from "builderStore"
|
import { store, backendUiStore, currentAsset } from "builderStore"
|
||||||
|
// import DataBindingDrawer from "components/userInterface/DataBindingDrawer/index.svelte"
|
||||||
|
import BottomDrawer from "components/common/BottomDrawer.svelte"
|
||||||
|
import ParameterBuilder from "components/integration/QueryParameterBuilder.svelte"
|
||||||
import fetchBindableProperties from "../../builderStore/fetchBindableProperties"
|
import fetchBindableProperties from "../../builderStore/fetchBindableProperties"
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
let anchorRight, dropdownRight
|
let anchorRight, dropdownRight
|
||||||
|
let bindingDrawerOpen
|
||||||
|
|
||||||
export let value = {}
|
export let value = {}
|
||||||
|
|
||||||
|
@ -15,14 +19,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function openBindingDrawer() {
|
function openBindingDrawer() {
|
||||||
backendUiStore.update(state => {
|
bindingDrawerOpen = true
|
||||||
state.selectedQueryId = value._id
|
}
|
||||||
return state
|
|
||||||
})
|
function closeDatabindingDrawer() {
|
||||||
store.update(state => {
|
bindingDrawerOpen = false
|
||||||
state.bottomDrawerVisible = true
|
|
||||||
return state
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$: tables = $backendUiStore.tables.map(m => ({
|
$: tables = $backendUiStore.tables.map(m => ({
|
||||||
|
@ -47,6 +48,7 @@
|
||||||
name: query.name,
|
name: query.name,
|
||||||
...query,
|
...query,
|
||||||
schema: query.schema,
|
schema: query.schema,
|
||||||
|
parameters: query.parameters,
|
||||||
type: "query",
|
type: "query",
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
@ -57,6 +59,15 @@
|
||||||
tables: $backendUiStore.tables,
|
tables: $backendUiStore.tables,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
$: queryBindableProperties = bindableProperties.map(property => ({
|
||||||
|
...property,
|
||||||
|
category: property.type === "instance" ? "Component" : "Table",
|
||||||
|
label: property.readableBinding,
|
||||||
|
path: property.runtimeBinding,
|
||||||
|
}))
|
||||||
|
|
||||||
|
$: console.log("selected", value)
|
||||||
|
|
||||||
$: links = bindableProperties
|
$: links = bindableProperties
|
||||||
.filter(x => x.fieldSchema?.type === "link")
|
.filter(x => x.fieldSchema?.type === "link")
|
||||||
.map(property => {
|
.map(property => {
|
||||||
|
@ -78,9 +89,6 @@
|
||||||
<span>{value.label ? value.label : 'Table / View / Query'}</span>
|
<span>{value.label ? value.label : 'Table / View / Query'}</span>
|
||||||
<Icon name="arrowdown" />
|
<Icon name="arrowdown" />
|
||||||
</div>
|
</div>
|
||||||
{#if value.type === "query"}
|
|
||||||
<i class="ri-settings-3-line" on:click={openBindingDrawer} />
|
|
||||||
{/if}
|
|
||||||
<DropdownMenu bind:this={dropdownRight} anchor={anchorRight}>
|
<DropdownMenu bind:this={dropdownRight} anchor={anchorRight}>
|
||||||
<div class="dropdown">
|
<div class="dropdown">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
|
@ -138,6 +146,25 @@
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|
||||||
|
{#if value.type === "query"}
|
||||||
|
<Button blue on:click={openBindingDrawer}/>
|
||||||
|
{#if bindingDrawerOpen}
|
||||||
|
<BottomDrawer title={'Query'} onClose={closeDatabindingDrawer}>
|
||||||
|
<div slot="buttons">
|
||||||
|
<Button blue thin on:click={() => handleSelected(value)}>Save</Button>
|
||||||
|
</div>
|
||||||
|
<div class="drawer-contents" slot="body">
|
||||||
|
<pre>{value.queryString}</pre>
|
||||||
|
<ParameterBuilder
|
||||||
|
bind:customParams={value.queryParams}
|
||||||
|
parameters={value.parameters || []}
|
||||||
|
bindings={queryBindableProperties} />
|
||||||
|
</div>
|
||||||
|
</BottomDrawer>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.dropdownbutton {
|
.dropdownbutton {
|
||||||
background-color: var(--grey-2);
|
background-color: var(--grey-2);
|
||||||
|
@ -199,4 +226,8 @@
|
||||||
li:hover {
|
li:hover {
|
||||||
background-color: var(--grey-4);
|
background-color: var(--grey-4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.drawer-contents {
|
||||||
|
padding: var(--spacing-xl);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
import ComponentPropertiesPanel from "components/userInterface/ComponentPropertiesPanel.svelte"
|
import ComponentPropertiesPanel from "components/userInterface/ComponentPropertiesPanel.svelte"
|
||||||
import ComponentSelectionList from "components/userInterface/ComponentSelectionList.svelte"
|
import ComponentSelectionList from "components/userInterface/ComponentSelectionList.svelte"
|
||||||
import FrontendNavigatePane from "components/userInterface/FrontendNavigatePane.svelte"
|
import FrontendNavigatePane from "components/userInterface/FrontendNavigatePane.svelte"
|
||||||
import DataBindingDrawer from "components/userInterface/DataBindingDrawer/index.svelte"
|
|
||||||
|
|
||||||
$: instance = $store.appInstance
|
$: instance = $store.appInstance
|
||||||
|
|
||||||
|
@ -48,10 +47,6 @@
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if $store.bottomDrawerVisible}
|
|
||||||
<DataBindingDrawer />
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if $selectedComponent != null}
|
{#if $selectedComponent != null}
|
||||||
<div class="components-pane">
|
<div class="components-pane">
|
||||||
<ComponentPropertiesPanel />
|
<ComponentPropertiesPanel />
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { fetchTableData } from "./tables"
|
import { fetchTableData } from "./tables"
|
||||||
import { fetchViewData } from "./views"
|
import { fetchViewData } from "./views"
|
||||||
import { fetchRelationshipData } from "./relationships"
|
import { fetchRelationshipData } from "./relationships"
|
||||||
import { fetchQueryData } from "./queries"
|
import { executeQuery } from "./queries"
|
||||||
import { enrichRows } from "./rows"
|
import { enrichRows } from "./rows"
|
||||||
|
import { enrichDataBindings } from "../utils/enrichDataBinding"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches all rows for a particular Budibase data source.
|
* Fetches all rows for a particular Budibase data source.
|
||||||
|
@ -20,8 +21,12 @@ export const fetchDatasource = async (datasource, dataContext) => {
|
||||||
} else if (type === "view") {
|
} else if (type === "view") {
|
||||||
rows = await fetchViewData(datasource)
|
rows = await fetchViewData(datasource)
|
||||||
} else if (type === "query") {
|
} else if (type === "query") {
|
||||||
// TODO: map to schema
|
console.log("Query Datasource", datasource)
|
||||||
return await fetchQueryData(datasource)
|
console.log("Data Context", dataContext)
|
||||||
|
// TODO: You left here
|
||||||
|
const parameters = enrichDataBindings(datasource.queryParams, dataContext)
|
||||||
|
console.log("PARSED PARAMS", parameters)
|
||||||
|
return await executeQuery({ _id: datasource._id, parameters })
|
||||||
} else if (type === "link") {
|
} else if (type === "link") {
|
||||||
const row = dataContext[datasource.providerId]
|
const row = dataContext[datasource.providerId]
|
||||||
rows = await fetchRelationshipData({
|
rows = await fetchRelationshipData({
|
||||||
|
|
|
@ -1,23 +1,13 @@
|
||||||
import API from "./api"
|
import API from "./api"
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetches all rows from a query.
|
|
||||||
*/
|
|
||||||
export const fetchQueryData = async ({ _id }) => {
|
|
||||||
const response = await API.get({
|
|
||||||
url: `/api/queries/${_id}`,
|
|
||||||
})
|
|
||||||
return response.rows
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes a query against an external data connector.
|
* Executes a query against an external data connector.
|
||||||
*/
|
*/
|
||||||
export const executeQuery = async ({ queryId, params }) => {
|
export const executeQuery = async ({ queryId, parameters }) => {
|
||||||
const response = await API.post({
|
const response = await API.post({
|
||||||
url: `/api/queries/${queryId}`,
|
url: `/api/queries/${queryId}`,
|
||||||
body: {
|
body: {
|
||||||
params,
|
parameters,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return response.rows
|
return response.rows
|
||||||
|
|
|
@ -28,10 +28,13 @@ const navigationHandler = action => {
|
||||||
const queryExecutionHandler = async (action, context) => {
|
const queryExecutionHandler = async (action, context) => {
|
||||||
const { datasourceId, queryId, queryParams } = action.parameters
|
const { datasourceId, queryId, queryParams } = action.parameters
|
||||||
|
|
||||||
// TODO: allow context based bindings for query params
|
|
||||||
const enrichedQueryParameters = enrichDataBindings(queryParams, context)
|
const enrichedQueryParameters = enrichDataBindings(queryParams, context)
|
||||||
|
|
||||||
await executeQuery({ datasourceId, queryId, params: enrichedQueryParameters })
|
await executeQuery({
|
||||||
|
datasourceId,
|
||||||
|
queryId,
|
||||||
|
parameters: enrichedQueryParameters,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlerMap = {
|
const handlerMap = {
|
||||||
|
|
|
@ -81,7 +81,7 @@ exports.execute = async function(ctx) {
|
||||||
const queryTemplate = handlebars.compile(query.queryString)
|
const queryTemplate = handlebars.compile(query.queryString)
|
||||||
|
|
||||||
// TODO: Take the default params into account
|
// TODO: Take the default params into account
|
||||||
const parsedQuery = queryTemplate(ctx.request.body.params)
|
const parsedQuery = queryTemplate(ctx.request.body.parameters)
|
||||||
|
|
||||||
const Integration = integrations[datasource.source]
|
const Integration = integrations[datasource.source]
|
||||||
|
|
||||||
|
@ -94,31 +94,6 @@ exports.execute = async function(ctx) {
|
||||||
ctx.body = response
|
ctx.body = response
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.fetchQuery = async function(ctx) {
|
|
||||||
const db = new CouchDB(ctx.user.appId)
|
|
||||||
|
|
||||||
const query = await db.get(ctx.params.queryId)
|
|
||||||
|
|
||||||
const datasource = await db.get(query.datasourceId)
|
|
||||||
|
|
||||||
const Integration = integrations[datasource.source]
|
|
||||||
|
|
||||||
if (!Integration) {
|
|
||||||
ctx.throw(400, "Integration type does not exist.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const rows = await new Integration(
|
|
||||||
datasource.config,
|
|
||||||
query.queryString
|
|
||||||
).query()
|
|
||||||
|
|
||||||
ctx.body = {
|
|
||||||
schema: query.schema,
|
|
||||||
rows,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.destroy = async function(ctx) {
|
exports.destroy = async function(ctx) {
|
||||||
const db = new CouchDB(ctx.user.appId)
|
const db = new CouchDB(ctx.user.appId)
|
||||||
await db.destroy(ctx.params.queryId)
|
await db.destroy(ctx.params.queryId)
|
||||||
|
|
|
@ -8,10 +8,9 @@ const router = Router()
|
||||||
// TODO: sort out auth so apps have the right permissions
|
// TODO: sort out auth so apps have the right permissions
|
||||||
router
|
router
|
||||||
.get("/api/queries", authorized(BUILDER), queryController.fetch)
|
.get("/api/queries", authorized(BUILDER), queryController.fetch)
|
||||||
.get("/api/queries/:queryId", authorized(BUILDER), queryController.fetchQuery)
|
|
||||||
.post("/api/queries", authorized(BUILDER), queryController.save)
|
.post("/api/queries", authorized(BUILDER), queryController.save)
|
||||||
.post("/api/queries/preview", authorized(BUILDER), queryController.preview)
|
|
||||||
.post("/api/queries/:queryId", authorized(BUILDER), queryController.execute)
|
.post("/api/queries/:queryId", authorized(BUILDER), queryController.execute)
|
||||||
|
.post("/api/queries/preview", authorized(BUILDER), queryController.preview)
|
||||||
.delete("/api/queries/:queryId", authorized(BUILDER), queryController.destroy)
|
.delete("/api/queries/:queryId", authorized(BUILDER), queryController.destroy)
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
|
Loading…
Reference in New Issue