|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 12 KiB |
@ -1,114 +0,0 @@ |
|||
<script> |
|||
import { sortBy } from "lodash/fp" |
|||
import { automationStore } from "builderStore" |
|||
import { ActionButton, Popover, Modal } from "@budibase/bbui" |
|||
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns" |
|||
import CreateWebhookModal from "../Shared/CreateWebhookModal.svelte" |
|||
|
|||
$: hasTrigger = $automationStore.selectedAutomation.hasTrigger() |
|||
$: tabs = [ |
|||
{ |
|||
label: "Trigger", |
|||
value: "TRIGGER", |
|||
icon: "Algorithm", |
|||
disabled: hasTrigger, |
|||
}, |
|||
{ |
|||
label: "Internal", |
|||
value: "ACTION", |
|||
internal: true, |
|||
icon: "Actions", |
|||
disabled: !hasTrigger, |
|||
}, |
|||
{ |
|||
label: "External", |
|||
value: "ACTION", |
|||
internal: false, |
|||
icon: "Extension", |
|||
disabled: !hasTrigger, |
|||
}, |
|||
] |
|||
|
|||
let selectedIndex |
|||
let anchors = [] |
|||
let popover |
|||
let webhookModal |
|||
$: selectedTab = selectedIndex == null ? null : tabs[selectedIndex].value |
|||
$: selectedInternal = |
|||
selectedIndex == null ? null : tabs[selectedIndex].internal |
|||
$: anchor = selectedIndex === -1 ? null : anchors[selectedIndex] |
|||
$: blocks = sortBy(entry => entry[1].name)( |
|||
Object.entries($automationStore.blockDefinitions[selectedTab] ?? {}) |
|||
).filter( |
|||
entry => selectedInternal == null || entry[1].internal === selectedInternal |
|||
) |
|||
|
|||
function onChangeTab(idx) { |
|||
selectedIndex = idx |
|||
popover.show() |
|||
} |
|||
|
|||
function closePopover() { |
|||
selectedIndex = null |
|||
popover.hide() |
|||
} |
|||
|
|||
function addBlockToAutomation(stepId, blockDefinition) { |
|||
const newBlock = $automationStore.selectedAutomation.constructBlock( |
|||
selectedTab, |
|||
stepId, |
|||
blockDefinition |
|||
) |
|||
automationStore.actions.addBlockToAutomation(newBlock) |
|||
closePopover() |
|||
if (stepId === "WEBHOOK") { |
|||
webhookModal.show() |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<div class="tab-container"> |
|||
{#each tabs as tab, idx} |
|||
<div bind:this={anchors[idx]}> |
|||
<ActionButton |
|||
quiet |
|||
size="S" |
|||
icon={tab.icon} |
|||
disabled={tab.disabled} |
|||
on:click={tab.disabled ? null : () => onChangeTab(idx)} |
|||
> |
|||
{tab.label} |
|||
</ActionButton> |
|||
</div> |
|||
{/each} |
|||
</div> |
|||
<Popover |
|||
on:close={() => (selectedIndex = null)} |
|||
bind:this={popover} |
|||
{anchor} |
|||
align="left" |
|||
> |
|||
<DropdownContainer> |
|||
{#each blocks as [stepId, blockDefinition]} |
|||
<DropdownItem |
|||
icon={blockDefinition.icon} |
|||
title={blockDefinition.name} |
|||
subtitle={blockDefinition.description} |
|||
on:click={() => addBlockToAutomation(stepId, blockDefinition)} |
|||
/> |
|||
{/each} |
|||
</DropdownContainer> |
|||
</Popover> |
|||
<Modal bind:this={webhookModal} width="30%"> |
|||
<CreateWebhookModal /> |
|||
</Modal> |
|||
|
|||
<style> |
|||
.tab-container { |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: flex-start; |
|||
align-items: center; |
|||
min-height: 24px; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,149 @@ |
|||
<script> |
|||
import { ModalContent, Layout, Detail, Body, Icon } from "@budibase/bbui" |
|||
import { automationStore } from "builderStore" |
|||
import DiscordLogo from "assets/discord.svg" |
|||
import ZapierLogo from "assets/zapier.png" |
|||
import IntegromatLogo from "assets/integromat.png" |
|||
import SlackLogo from "assets/slack.svg" |
|||
import n8nlogo from "assets/n8n.png" |
|||
|
|||
import { database } from "stores/backend" |
|||
$: instanceId = $database._id |
|||
|
|||
let selectedAction |
|||
let actionVal |
|||
export let blockComplete |
|||
|
|||
let externalActionsPredicate = [ |
|||
{ name: "zapier", logo: ZapierLogo }, |
|||
{ name: "discord", logo: DiscordLogo }, |
|||
{ name: "slack", logo: SlackLogo }, |
|||
{ name: "integromat", logo: IntegromatLogo }, |
|||
{ name: "n8n", logo: n8nlogo }, |
|||
] |
|||
let actions = Object.entries($automationStore.blockDefinitions.ACTION) |
|||
|
|||
const externalActions = actions.reduce((acc, elm) => { |
|||
const [k, v] = elm |
|||
if (externalActionsPredicate.some(pred => pred.name === k)) { |
|||
acc[k] = v |
|||
} |
|||
return acc |
|||
}, {}) |
|||
|
|||
const internalActions = actions.reduce((acc, elm) => { |
|||
const [k, v] = elm |
|||
if (!externalActionsPredicate.some(pred => pred.name === k)) { |
|||
acc[k] = v |
|||
} |
|||
return acc |
|||
}, {}) |
|||
|
|||
const selectAction = action => { |
|||
actionVal = action |
|||
selectedAction = action.name |
|||
} |
|||
|
|||
async function addBlockToAutomation() { |
|||
const newBlock = $automationStore.selectedAutomation.constructBlock( |
|||
"ACTION", |
|||
actionVal.stepId, |
|||
actionVal |
|||
) |
|||
automationStore.actions.addBlockToAutomation(newBlock) |
|||
await automationStore.actions.save({ |
|||
instanceId, |
|||
automation: $automationStore.selectedAutomation?.automation, |
|||
}) |
|||
} |
|||
</script> |
|||
|
|||
<ModalContent |
|||
title="Create Automation" |
|||
confirmText="Save" |
|||
size="M" |
|||
disabled={!selectedAction} |
|||
onConfirm={() => { |
|||
blockComplete = true |
|||
addBlockToAutomation() |
|||
}} |
|||
> |
|||
<Body size="XS">Select an app or event.</Body> |
|||
<Layout noPadding> |
|||
<Body size="S">Apps</Body> |
|||
|
|||
<div class="item-list"> |
|||
{#each Object.entries(externalActions) as [idx, action]} |
|||
<div |
|||
class="item" |
|||
class:selected={selectedAction === action.name} |
|||
on:click={() => selectAction(action)} |
|||
> |
|||
<div class="item-body"> |
|||
<img |
|||
width="20" |
|||
height="20" |
|||
src={externalActionsPredicate.find(val => val.name === idx).logo} |
|||
alt="zapier" |
|||
/> |
|||
<span class="icon-spacing"> |
|||
<Body size="XS">{idx.charAt(0).toUpperCase() + idx.slice(1)}</Body |
|||
></span |
|||
> |
|||
</div> |
|||
</div> |
|||
{/each} |
|||
</div> |
|||
|
|||
<Detail size="S">Actions</Detail> |
|||
|
|||
<div class="item-list"> |
|||
{#each Object.entries(internalActions) as [idx, action]} |
|||
<div |
|||
class="item" |
|||
class:selected={selectedAction === action.name} |
|||
on:click={() => selectAction(action)} |
|||
> |
|||
<div class="item-body"> |
|||
<Icon name={action.icon} /> |
|||
<span class="icon-spacing"> |
|||
<Body size="XS">{action.name}</Body></span |
|||
> |
|||
</div> |
|||
</div> |
|||
{/each} |
|||
</div> |
|||
</Layout> |
|||
</ModalContent> |
|||
|
|||
<style> |
|||
.icon-spacing { |
|||
margin-left: var(--spacing-m); |
|||
} |
|||
.item-body { |
|||
display: flex; |
|||
margin-left: var(--spacing-m); |
|||
} |
|||
.item-list { |
|||
display: grid; |
|||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); |
|||
grid-gap: var(--spectrum-alias-grid-baseline); |
|||
} |
|||
|
|||
.item { |
|||
cursor: pointer; |
|||
display: grid; |
|||
grid-gap: var(--spectrum-alias-grid-margin-xsmall); |
|||
padding: var(--spectrum-alias-item-padding-s); |
|||
background: var(--spectrum-alias-background-color-secondary); |
|||
transition: 0.3s all; |
|||
border: solid #3b3d3c; |
|||
border-radius: 5px; |
|||
box-sizing: border-box; |
|||
border-width: 2px; |
|||
} |
|||
.item:hover, |
|||
.selected { |
|||
background: var(--spectrum-alias-background-color-tertiary); |
|||
} |
|||
</style> |
|||
|
Before Width: | Height: | Size: 326 B After Width: | Height: | Size: 353 B |
@ -0,0 +1,13 @@ |
|||
import DiscordLogo from "assets/discord.svg" |
|||
import ZapierLogo from "assets/zapier.png" |
|||
import IntegromatLogo from "assets/integromat.png" |
|||
import SlackLogo from "assets/slack.svg" |
|||
import n8nlogo from "assets/n8n.png" |
|||
|
|||
export const externalActions = { |
|||
zapier: { name: "zapier", icon: ZapierLogo }, |
|||
discord: { name: "discord", icon: DiscordLogo }, |
|||
slack: { name: "slack", icon: SlackLogo }, |
|||
integromat: { name: "integromat", icon: IntegromatLogo }, |
|||
n8n: { name: "n8n", icon: n8nlogo }, |
|||
} |
|||
@ -1,86 +1,200 @@ |
|||
<script> |
|||
import { automationStore } from "builderStore" |
|||
import AutomationBlockTagline from "./AutomationBlockTagline.svelte" |
|||
import { Icon } from "@budibase/bbui" |
|||
import { |
|||
Icon, |
|||
Divider, |
|||
Layout, |
|||
Body, |
|||
Detail, |
|||
Modal, |
|||
Button, |
|||
StatusLight, |
|||
} from "@budibase/bbui" |
|||
import AutomationBlockSetup from "../../SetupPanel/AutomationBlockSetup.svelte" |
|||
import CreateWebhookModal from "components/automation/shared/CreateWebhookModal.svelte" |
|||
import ResultsModal from "./ResultsModal.svelte" |
|||
import ActionModal from "./ActionModal.svelte" |
|||
import { database } from "stores/backend" |
|||
import { externalActions } from "./ExternalActions" |
|||
|
|||
export let onSelect |
|||
export let block |
|||
export let testDataModal |
|||
let selected |
|||
let webhookModal |
|||
let actionModal |
|||
let resultsModal |
|||
let setupToggled |
|||
let blockComplete |
|||
$: testResult = $automationStore.selectedAutomation.testResults?.steps.filter( |
|||
step => step.stepId === block.stepId |
|||
) |
|||
$: instanceId = $database._id |
|||
|
|||
$: isTrigger = block.type === "TRIGGER" |
|||
|
|||
$: selected = $automationStore.selectedBlock?.id === block.id |
|||
$: steps = |
|||
$automationStore.selectedAutomation?.automation?.definition?.steps ?? [] |
|||
|
|||
$: blockIdx = steps.findIndex(step => step.id === block.id) |
|||
$: allowDeleteTrigger = !steps.length |
|||
$: lastStep = !isTrigger && blockIdx + 1 === steps.length |
|||
|
|||
// Logic for hiding / showing the add button.first we check if it has a child |
|||
// then we check to see whether its inputs have been commpleted |
|||
$: disableAddButton = isTrigger |
|||
? $automationStore.selectedAutomation?.automation?.definition?.steps |
|||
.length > 0 |
|||
: !isTrigger && steps.length - blockIdx > 1 |
|||
$: hasCompletedInputs = Object.keys( |
|||
block.schema?.inputs?.properties || {} |
|||
).every(x => block?.inputs[x]) |
|||
|
|||
function deleteStep() { |
|||
async function deleteStep() { |
|||
automationStore.actions.deleteAutomationBlock(block) |
|||
await automationStore.actions.save({ |
|||
instanceId, |
|||
automation: $automationStore.selectedAutomation?.automation, |
|||
}) |
|||
} |
|||
</script> |
|||
|
|||
<div |
|||
class={`block ${block.type} hoverable`} |
|||
class:selected |
|||
on:click={() => onSelect(block)} |
|||
on:click={() => { |
|||
onSelect(block) |
|||
}} |
|||
> |
|||
<header> |
|||
{#if block.type === "TRIGGER"} |
|||
<Icon name="Light" /> |
|||
<span>When this happens...</span> |
|||
{:else if block.type === "ACTION"} |
|||
<Icon name="FlashOn" /> |
|||
<span>Do this...</span> |
|||
{:else if block.type === "LOGIC"} |
|||
<Icon name="Branch2" /> |
|||
<span>Only continue if...</span> |
|||
{/if} |
|||
<div class="label"> |
|||
{#if block.type === "TRIGGER"}Trigger{:else}Step {blockIdx + 1}{/if} |
|||
<div class="blockSection"> |
|||
<div |
|||
on:click={() => { |
|||
blockComplete = !blockComplete |
|||
}} |
|||
class="splitHeader" |
|||
> |
|||
<div style="display: flex;"> |
|||
{#if externalActions[block.stepId]} |
|||
<img |
|||
alt={externalActions[block.stepId].name} |
|||
width="35px" |
|||
height="35px" |
|||
src={externalActions[block.stepId].icon} |
|||
/> |
|||
{:else} |
|||
<svg |
|||
width="35px" |
|||
height="35px" |
|||
class="spectrum-Icon" |
|||
style="color:grey;" |
|||
focusable="false" |
|||
> |
|||
<use xlink:href="#spectrum-icon-18-{block.icon}" /> |
|||
</svg> |
|||
{/if} |
|||
<div class="iconAlign"> |
|||
<Body size="XS">When this happens:</Body> |
|||
|
|||
<Detail size="S">{block?.name?.toUpperCase() || ""}</Detail> |
|||
</div> |
|||
</div> |
|||
{#if testResult} |
|||
<span on:click={() => resultsModal.show()}> |
|||
<StatusLight |
|||
positive={isTrigger || testResult[0].outputs?.success} |
|||
negative={!testResult[0].outputs?.success} |
|||
><Body size="XS">View response</Body></StatusLight |
|||
> |
|||
</span> |
|||
{/if} |
|||
</div> |
|||
</div> |
|||
{#if !blockComplete} |
|||
<Divider noMargin /> |
|||
<div class="blockSection"> |
|||
<Layout noPadding gap="S"> |
|||
<div class="splitHeader"> |
|||
<div |
|||
on:click|stopPropagation={() => { |
|||
setupToggled = !setupToggled |
|||
}} |
|||
class="toggle" |
|||
> |
|||
{#if setupToggled} |
|||
<Icon size="M" name="ChevronDown" /> |
|||
{:else} |
|||
<Icon size="M" name="ChevronRight" /> |
|||
{/if} |
|||
<Detail size="S">Setup</Detail> |
|||
</div> |
|||
{#if !isTrigger} |
|||
<div on:click={() => deleteStep()}> |
|||
<Icon name="DeleteOutline" /> |
|||
</div> |
|||
{/if} |
|||
</div> |
|||
|
|||
{#if setupToggled} |
|||
<AutomationBlockSetup |
|||
schemaProperties={Object.entries(block.schema.inputs.properties)} |
|||
{block} |
|||
{webhookModal} |
|||
/> |
|||
{#if lastStep} |
|||
<Button on:click={() => testDataModal.show()} cta |
|||
>Continue and test automation</Button |
|||
> |
|||
{/if} |
|||
<Button |
|||
disabled={disableAddButton ? true : !hasCompletedInputs} |
|||
on:click={() => { |
|||
setupToggled = false |
|||
actionModal.show() |
|||
}} |
|||
primary={!isTrigger} |
|||
cta={isTrigger}>Add Action</Button |
|||
> |
|||
{/if} |
|||
</Layout> |
|||
</div> |
|||
{#if block.type !== "TRIGGER" || allowDeleteTrigger} |
|||
<div on:click|stopPropagation={deleteStep}><Icon name="Close" /></div> |
|||
{/if} |
|||
</header> |
|||
<hr /> |
|||
<p> |
|||
<AutomationBlockTagline {block} /> |
|||
</p> |
|||
{/if} |
|||
|
|||
<Modal bind:this={resultsModal} width="30%"> |
|||
<ResultsModal {isTrigger} {testResult} /> |
|||
</Modal> |
|||
|
|||
<Modal bind:this={actionModal} width="30%"> |
|||
<ActionModal bind:blockComplete /> |
|||
</Modal> |
|||
|
|||
<Modal bind:this={webhookModal} width="30%"> |
|||
<CreateWebhookModal /> |
|||
</Modal> |
|||
</div> |
|||
|
|||
<style> |
|||
.toggle { |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
.splitHeader { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
} |
|||
.iconAlign { |
|||
padding: 0 0 0 var(--spacing-m); |
|||
display: inline-block; |
|||
} |
|||
.block { |
|||
width: 360px; |
|||
padding: 20px; |
|||
border-radius: var(--border-radius-m); |
|||
transition: 0.3s all ease; |
|||
box-shadow: 0 4px 30px 0 rgba(57, 60, 68, 0.08); |
|||
font-size: 16px; |
|||
background-color: var(--spectrum-global-color-gray-50); |
|||
background-color: var(--spectrum-alias-background-color-secondary); |
|||
color: var(--grey-9); |
|||
} |
|||
.block.selected, |
|||
.block:hover { |
|||
transform: scale(1.1); |
|||
box-shadow: 0 4px 30px 0 rgba(57, 60, 68, 0.15); |
|||
border: 1px solid var(--spectrum-global-color-gray-300); |
|||
border-radius: 4px 4px 4px 4px; |
|||
} |
|||
|
|||
header { |
|||
font-size: 16px; |
|||
font-weight: 600; |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: flex-start; |
|||
align-items: center; |
|||
gap: var(--spacing-xs); |
|||
} |
|||
header span { |
|||
flex: 1 1 auto; |
|||
} |
|||
header .label { |
|||
font-size: 14px; |
|||
padding: var(--spacing-s); |
|||
border-radius: var(--border-radius-m); |
|||
background-color: var(--grey-2); |
|||
color: var(--grey-8); |
|||
.blockSection { |
|||
padding: var(--spacing-xl); |
|||
} |
|||
</style> |
|||
|
|||
@ -0,0 +1,88 @@ |
|||
<script> |
|||
import { ModalContent, Icon, Detail, Badge, TextArea } from "@budibase/bbui" |
|||
|
|||
export let testResult |
|||
export let isTrigger |
|||
let inputToggled |
|||
let outputToggled |
|||
</script> |
|||
|
|||
<ModalContent |
|||
showCloseIcon={false} |
|||
showConfirmButton={false} |
|||
title="Test Automation" |
|||
cancelText="Close" |
|||
> |
|||
<div slot="header"> |
|||
<div style="float: right;"> |
|||
{#if isTrigger || testResult[0].outputs.success} |
|||
<Badge green><Icon size="S" name="CheckmarkCircle" /></Badge> |
|||
{:else} |
|||
<Badge red><Icon size="S" name="CloseCircle" /></Badge> |
|||
{/if} |
|||
</div> |
|||
</div> |
|||
|
|||
<div |
|||
on:click={() => { |
|||
inputToggled = !inputToggled |
|||
}} |
|||
class="toggle splitHeader" |
|||
> |
|||
<div> |
|||
<div style="display: flex; align-items: center;"> |
|||
<span style="padding-left: var(--spacing-s);"> |
|||
<Detail size="S">Input</Detail> |
|||
</span> |
|||
</div> |
|||
</div> |
|||
<div> |
|||
{#if inputToggled} |
|||
<Icon size="M" name="ChevronDown" /> |
|||
{:else} |
|||
<Icon size="M" name="ChevronRight" /> |
|||
{/if} |
|||
</div> |
|||
</div> |
|||
{#if inputToggled} |
|||
<TextArea disabled value={JSON.stringify(testResult[0].inputs, null, 2)} /> |
|||
{/if} |
|||
|
|||
<div |
|||
on:click={() => { |
|||
outputToggled = !outputToggled |
|||
}} |
|||
class="toggle splitHeader" |
|||
> |
|||
<div> |
|||
<div style="display: flex; align-items: center;"> |
|||
<span style="padding-left: var(--spacing-s);"> |
|||
<Detail size="S">Output</Detail> |
|||
</span> |
|||
</div> |
|||
</div> |
|||
<div> |
|||
{#if outputToggled} |
|||
<Icon size="M" name="ChevronDown" /> |
|||
{:else} |
|||
<Icon size="M" name="ChevronRight" /> |
|||
{/if} |
|||
</div> |
|||
</div> |
|||
{#if outputToggled} |
|||
<TextArea disabled value={JSON.stringify(testResult[0].outputs, null, 2)} /> |
|||
{/if} |
|||
</ModalContent> |
|||
|
|||
<style> |
|||
.splitHeader { |
|||
cursor: pointer; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
} |
|||
|
|||
.toggle { |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,70 @@ |
|||
<script> |
|||
import { ModalContent, Tabs, Tab, TextArea, Label } from "@budibase/bbui" |
|||
import { automationStore } from "builderStore" |
|||
import AutomationBlockSetup from "../../SetupPanel/AutomationBlockSetup.svelte" |
|||
import { cloneDeep } from "lodash/fp" |
|||
|
|||
let failedParse = null |
|||
// clone the trigger so we're not mutating the reference |
|||
let trigger = cloneDeep( |
|||
$automationStore.selectedAutomation.automation.definition.trigger |
|||
) |
|||
|
|||
if (!$automationStore.selectedAutomation.automation.testData) { |
|||
$automationStore.selectedAutomation.automation.testData = {} |
|||
} |
|||
|
|||
// get the outputs so we can define the fields |
|||
let schemaProperties = Object.entries(trigger.schema.outputs.properties || {}) |
|||
|
|||
// check to see if there is existing test data in the store |
|||
$: testData = $automationStore.selectedAutomation.automation.testData |
|||
|
|||
// Checj the schema to see if required fields have been entered |
|||
$: isError = !trigger.schema.outputs.required.every( |
|||
required => testData[required] |
|||
) |
|||
|
|||
function parseTestJSON(e) { |
|||
try { |
|||
const obj = JSON.parse(e.detail) |
|||
failedParse = null |
|||
automationStore.actions.addTestDataToAutomation(obj) |
|||
} catch (e) { |
|||
failedParse = "Invalid JSON" |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<ModalContent |
|||
title="Add test data" |
|||
confirmText="Test" |
|||
showConfirmButton={true} |
|||
disabled={isError} |
|||
onConfirm={() => { |
|||
automationStore.actions.addTestDataToAutomation(testData) |
|||
automationStore.actions.test($automationStore.selectedAutomation, testData) |
|||
}} |
|||
cancelText="Cancel" |
|||
> |
|||
<Tabs selected="Form" quiet |
|||
><Tab icon="Form" title="Form" |
|||
><AutomationBlockSetup |
|||
bind:testData |
|||
{schemaProperties} |
|||
block={trigger} |
|||
/></Tab |
|||
> |
|||
<Tab icon="FileJson" title="JSON"> |
|||
<Label>JSON</Label><TextArea |
|||
value={JSON.stringify( |
|||
$automationStore.selectedAutomation.automation.testData, |
|||
null, |
|||
2 |
|||
)} |
|||
error={failedParse} |
|||
on:change={e => parseTestJSON(e)} |
|||
/> |
|||
</Tab> |
|||
</Tabs> |
|||
</ModalContent> |
|||
@ -1,96 +0,0 @@ |
|||
<script> |
|||
import { automationStore } from "builderStore" |
|||
import { database } from "stores/backend" |
|||
import { notifications, Button, Modal, Heading, Toggle } from "@budibase/bbui" |
|||
import AutomationBlockSetup from "./AutomationBlockSetup.svelte" |
|||
import CreateWebookModal from "../Shared/CreateWebhookModal.svelte" |
|||
|
|||
let webhookModal |
|||
|
|||
$: instanceId = $database._id |
|||
$: automation = $automationStore.selectedAutomation?.automation |
|||
$: automationLive = automation?.live |
|||
|
|||
function setAutomationLive(live) { |
|||
if (automationLive === live) { |
|||
return |
|||
} |
|||
automation.live = live |
|||
automationStore.actions.save({ instanceId, automation }) |
|||
if (live) { |
|||
notifications.info(`Automation ${automation.name} enabled.`) |
|||
} else { |
|||
notifications.error(`Automation ${automation.name} disabled.`) |
|||
} |
|||
} |
|||
|
|||
async function testAutomation() { |
|||
const result = await automationStore.actions.test({ |
|||
automation: $automationStore.selectedAutomation.automation, |
|||
}) |
|||
if (result.status === 200) { |
|||
notifications.success( |
|||
`Automation ${automation.name} triggered successfully.` |
|||
) |
|||
} else { |
|||
notifications.error(`Failed to trigger automation ${automation.name}.`) |
|||
} |
|||
} |
|||
|
|||
async function saveAutomation() { |
|||
await automationStore.actions.save({ |
|||
instanceId, |
|||
automation, |
|||
}) |
|||
notifications.success(`Automation ${automation.name} saved.`) |
|||
} |
|||
</script> |
|||
|
|||
<div class="title"> |
|||
<Heading size="S">Setup</Heading> |
|||
<Toggle |
|||
value={automationLive} |
|||
on:change={() => setAutomationLive(!automationLive)} |
|||
dataCy="activate-automation" |
|||
text="Live" |
|||
/> |
|||
</div> |
|||
{#if $automationStore.selectedBlock} |
|||
<AutomationBlockSetup |
|||
bind:block={$automationStore.selectedBlock} |
|||
{webhookModal} |
|||
/> |
|||
{:else if automation} |
|||
<div class="block-label">{automation.name}</div> |
|||
<Button secondary on:click={testAutomation}>Test Automation</Button> |
|||
{/if} |
|||
<Button |
|||
secondary |
|||
wide |
|||
data-cy="save-automation-setup" |
|||
on:click={saveAutomation} |
|||
> |
|||
Save Automation |
|||
</Button> |
|||
<Modal bind:this={webhookModal} width="30%"> |
|||
<CreateWebookModal /> |
|||
</Modal> |
|||
|
|||
<style> |
|||
.title { |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
gap: var(--spacing-xs); |
|||
} |
|||
.title :global(h1) { |
|||
flex: 1 1 auto; |
|||
} |
|||
|
|||
.block-label { |
|||
font-size: var(--spectrum-global-dimension-font-size-75); |
|||
font-weight: 600; |
|||
color: var(--grey-7); |
|||
} |
|||
</style> |
|||
@ -0,0 +1,8 @@ |
|||
#!/usr/bin/env node
|
|||
const updateDotEnv = require("update-dotenv") |
|||
|
|||
const arg = process.argv.slice(2)[0] |
|||
|
|||
updateDotEnv({ |
|||
SELF_HOSTED: arg === "enable" ? "1" : "0", |
|||
}).then(() => console.log("Updated server!")) |
|||
@ -0,0 +1,8 @@ |
|||
#!/usr/bin/env node
|
|||
const updateDotEnv = require("update-dotenv") |
|||
|
|||
const arg = process.argv.slice(2)[0] |
|||
|
|||
updateDotEnv({ |
|||
SELF_HOSTED: arg === "enable" ? "1" : "0", |
|||
}).then(() => console.log("Updated worker!")) |
|||
@ -0,0 +1,8 @@ |
|||
const Router = require("@koa/router") |
|||
const controller = require("../../controllers/system/environment") |
|||
|
|||
const router = Router() |
|||
|
|||
router.get("/api/system/environment", controller.fetch) |
|||
|
|||
module.exports = router |
|||
@ -1,8 +0,0 @@ |
|||
const Router = require("@koa/router") |
|||
const controller = require("../../controllers/system/flags") |
|||
|
|||
const router = Router() |
|||
|
|||
router.get("/api/system/flags", controller.fetch) |
|||
|
|||
module.exports = router |
|||