mirror of https://github.com/Budibase/budibase.git
15 changed files with 428 additions and 40 deletions
@ -0,0 +1,133 @@ |
|||||
|
<script> |
||||
|
import { Input, Select } from "@budibase/bbui" |
||||
|
|
||||
|
export let value = {} |
||||
|
$: fieldsArray = Object.entries(value).map(([name, type]) => ({ |
||||
|
name, |
||||
|
type, |
||||
|
})) |
||||
|
|
||||
|
function addField() { |
||||
|
const newValue = { ...value } |
||||
|
newValue[""] = "string" |
||||
|
value = newValue |
||||
|
} |
||||
|
|
||||
|
function removeField(name) { |
||||
|
const newValues = { ...value } |
||||
|
delete newValues[name] |
||||
|
value = newValues |
||||
|
} |
||||
|
|
||||
|
const fieldNameChanged = originalName => e => { |
||||
|
// reconstruct using fieldsArray, so field order is preserved |
||||
|
let entries = [...fieldsArray] |
||||
|
const newName = e.target.value |
||||
|
if (newName) { |
||||
|
entries.find(f => f.name === originalName).name = newName |
||||
|
} else { |
||||
|
entries = entries.filter(f => f.name !== originalName) |
||||
|
} |
||||
|
value = entries.reduce((newVals, current) => { |
||||
|
newVals[current.name] = current.type |
||||
|
return newVals |
||||
|
}, {}) |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<div class="root"> |
||||
|
<div class="add-field"> |
||||
|
<i class="ri-add-line" on:click={addField} /> |
||||
|
</div> |
||||
|
<div class="spacer" /> |
||||
|
{#each fieldsArray as field} |
||||
|
<div class="field"> |
||||
|
<Input |
||||
|
value={field.name} |
||||
|
secondary |
||||
|
on:change={fieldNameChanged(field.name)} /> |
||||
|
<Select |
||||
|
secondary |
||||
|
extraThin |
||||
|
value={field.type} |
||||
|
on:blur={e => (value[field.name] = e.target.value)}> |
||||
|
<option>string</option> |
||||
|
<option>number</option> |
||||
|
<option>boolean</option> |
||||
|
<option>datetime</option> |
||||
|
</Select> |
||||
|
|
||||
|
<i class="remove-field ri-delete-bin-line" |
||||
|
on:click={() => removeField(field.name)} /> |
||||
|
|
||||
|
|
||||
|
</div> |
||||
|
{/each} |
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
.root { |
||||
|
position: relative; |
||||
|
max-width: 100%; |
||||
|
overflow-x: auto; |
||||
|
/* so we can show the "+" button beside the "fields" label*/ |
||||
|
top: -26px; |
||||
|
} |
||||
|
|
||||
|
.spacer { |
||||
|
height: var(--spacing-s); |
||||
|
} |
||||
|
|
||||
|
.field { |
||||
|
max-width: 100%; |
||||
|
background-color: var(--grey-2); |
||||
|
margin-bottom: var(--spacing-m); |
||||
|
border-style: solid; |
||||
|
border-width: 1px; |
||||
|
border-color: var(--grey-4); |
||||
|
display: grid; |
||||
|
/*grid-template-rows: auto auto; |
||||
|
grid-template-columns: auto;*/ |
||||
|
position: relative; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.field :global(select) { |
||||
|
padding: var(--spacing-xs) 2rem var(--spacing-m) var(--spacing-s) !important; |
||||
|
font-size: var(--font-size-xs); |
||||
|
color: var(--grey-7); |
||||
|
} |
||||
|
|
||||
|
.field :global(.pointer) { |
||||
|
padding-bottom: var(--spacing-m) !important; |
||||
|
color: var(--grey-2); |
||||
|
} |
||||
|
|
||||
|
.field :global(input) { |
||||
|
padding: var(--spacing-m) var(--spacing-xl) var(--spacing-xs) var(--spacing-m); |
||||
|
font-size: var(--font-size-s); |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
.remove-field { |
||||
|
cursor: pointer; |
||||
|
color: var(--grey-6); |
||||
|
position: absolute; |
||||
|
top: var(--spacing-m); |
||||
|
right: 3px; |
||||
|
} |
||||
|
|
||||
|
.remove-field:hover { |
||||
|
color: var(--black); |
||||
|
} |
||||
|
|
||||
|
.add-field { |
||||
|
text-align: right; |
||||
|
} |
||||
|
|
||||
|
.add-field > i { |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
</style> |
||||
@ -0,0 +1,131 @@ |
|||||
|
<script> |
||||
|
import { Select, Label, Input } from "@budibase/bbui" |
||||
|
import { automationStore } from "builderStore" |
||||
|
import SaveFields from "./SaveFields.svelte" |
||||
|
|
||||
|
export let parameters = {} |
||||
|
|
||||
|
let newOrExisting = parameters.automationId ? "existing" : "new" |
||||
|
|
||||
|
$: automations = $automationStore.automations |
||||
|
.filter(a => a.definition.trigger?.stepId === "APP") |
||||
|
.map(automation => { |
||||
|
const schema = Object.entries( |
||||
|
automation.definition.trigger.inputs.fields |
||||
|
).map(([name, type]) => ({ name, type })) |
||||
|
|
||||
|
return { |
||||
|
name: automation.name, |
||||
|
_id: automation._id, |
||||
|
schema, |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
$: hasAutomations = automations && automations.length > 0 |
||||
|
|
||||
|
$: selectedAutomation = |
||||
|
parameters && |
||||
|
parameters.automationId && |
||||
|
automations.find(a => a._id === parameters.automationId) |
||||
|
|
||||
|
const onFieldsChanged = e => { |
||||
|
parameters.fields = e.detail |
||||
|
} |
||||
|
|
||||
|
const setNew = () => { |
||||
|
newOrExisting = "new" |
||||
|
parameters.automationId = undefined |
||||
|
} |
||||
|
|
||||
|
const setExisting = () => { |
||||
|
newOrExisting = "existing" |
||||
|
parameters.newAutomationName = "" |
||||
|
} |
||||
|
|
||||
|
</script> |
||||
|
|
||||
|
<div class="root"> |
||||
|
|
||||
|
<div class="radio-container" on:click={setNew}> |
||||
|
<input |
||||
|
type="radio" |
||||
|
value="new" |
||||
|
bind:group={newOrExisting} |
||||
|
disabled={!hasAutomations}> |
||||
|
|
||||
|
<Label disabled={!hasAutomations}>Create a new automation </Label> |
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
<div class="radio-container" on:click={setExisting}> |
||||
|
|
||||
|
<input |
||||
|
type="radio" |
||||
|
value="existing" |
||||
|
bind:group={newOrExisting} |
||||
|
disabled={!hasAutomations}> |
||||
|
|
||||
|
<Label disabled={!hasAutomations}>Use an existing automation </Label> |
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
<Label size="m" color="dark">Automation</Label> |
||||
|
|
||||
|
{#if newOrExisting=== "existing"} |
||||
|
<Select secondary bind:value={parameters.automationId} placeholder="Choose automation"> |
||||
|
<option value="" /> |
||||
|
{#each automations as automation} |
||||
|
<option value={automation._id}>{automation.name}</option> |
||||
|
{/each} |
||||
|
</Select> |
||||
|
{:else} |
||||
|
<Input secondary bind:value={parameters.newAutomationName} placeholder="Enter automation name" /> |
||||
|
{/if} |
||||
|
|
||||
|
<SaveFields |
||||
|
parameterFields={parameters.fields} |
||||
|
schemaFields={newOrExisting === "existing" && selectedAutomation && selectedAutomation.schema} |
||||
|
fieldLabel="Field" |
||||
|
on:fieldschanged={onFieldsChanged} /> |
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
.root { |
||||
|
display: grid; |
||||
|
column-gap: var(--spacing-s); |
||||
|
row-gap: var(--spacing-s); |
||||
|
grid-template-columns: auto 1fr auto 1fr auto; |
||||
|
align-items: baseline; |
||||
|
} |
||||
|
|
||||
|
.root :global(> div:nth-child(4)) { |
||||
|
grid-column: 2 / span 4; |
||||
|
} |
||||
|
|
||||
|
.radio-container { |
||||
|
display: grid; |
||||
|
grid-template-columns: auto 1fr; |
||||
|
} |
||||
|
|
||||
|
.radio-container:nth-child(1) { |
||||
|
grid-column: 1 / span 2; |
||||
|
} |
||||
|
|
||||
|
.radio-container:nth-child(2) { |
||||
|
grid-column: 3 / span 3; |
||||
|
} |
||||
|
|
||||
|
.radio-container :global(> label) { |
||||
|
margin-left: var(--spacing-m); |
||||
|
} |
||||
|
|
||||
|
.radio-container > input { |
||||
|
margin-bottom: var(--spacing-s); |
||||
|
} |
||||
|
|
||||
|
.radio-container > input:focus { |
||||
|
outline: none; |
||||
|
} |
||||
|
|
||||
|
</style> |
||||
@ -0,0 +1,10 @@ |
|||||
|
import API from "./api" |
||||
|
/** |
||||
|
* Executes an automation. Must have "App Action" trigger. |
||||
|
*/ |
||||
|
export const triggerAutomation = async (automationId, fields) => { |
||||
|
return await API.post({ |
||||
|
url: `/api/automations/${automationId}/trigger`, |
||||
|
body: { fields }, |
||||
|
}) |
||||
|
} |
||||
Loading…
Reference in new issue