mirror of https://github.com/Budibase/budibase.git
32 changed files with 638 additions and 161 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,136 @@ |
|||
<script> |
|||
import { Select, Label, Input } from "@budibase/bbui" |
|||
import { automationStore } from "builderStore" |
|||
import SaveFields from "./SaveFields.svelte" |
|||
|
|||
const AUTOMATION_STATUS = { |
|||
NEW: "new", |
|||
EXISTING: "existing" |
|||
} |
|||
|
|||
export let parameters = {} |
|||
|
|||
let automationStatus = parameters.automationId ? AUTOMATION_STATUS.EXISTING : AUTOMATION_STATUS.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 = () => { |
|||
automationStatus = AUTOMATION_STATUS.NEW |
|||
parameters.automationId = undefined |
|||
} |
|||
|
|||
const setExisting = () => { |
|||
automationStatus = AUTOMATION_STATUS.EXISTING |
|||
parameters.newAutomationName = "" |
|||
} |
|||
|
|||
</script> |
|||
|
|||
<div class="root"> |
|||
|
|||
<div class="radio-container" on:click={setNew}> |
|||
<input |
|||
type="radio" |
|||
value={AUTOMATION_STATUS.NEW} |
|||
bind:group={automationStatus} |
|||
disabled={!hasAutomations}> |
|||
|
|||
<Label disabled={!hasAutomations}>Create a new automation </Label> |
|||
|
|||
</div> |
|||
|
|||
<div class="radio-container" on:click={setExisting}> |
|||
|
|||
<input |
|||
type="radio" |
|||
value={AUTOMATION_STATUS.EXISTING} |
|||
bind:group={automationStatus} |
|||
disabled={!hasAutomations}> |
|||
|
|||
<Label disabled={!hasAutomations}>Use an existing automation </Label> |
|||
|
|||
</div> |
|||
|
|||
<Label size="m" color="dark">Automation</Label> |
|||
|
|||
{#if automationStatus === AUTOMATION_STATUS.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={automationStatus === AUTOMATION_STATUS.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