458 lines
12 KiB
Svelte
458 lines
12 KiB
Svelte
<script>
|
|
import {
|
|
automationStore,
|
|
permissions,
|
|
selectedAutomation,
|
|
tables,
|
|
} from "stores/builder"
|
|
import {
|
|
Icon,
|
|
Divider,
|
|
Layout,
|
|
Detail,
|
|
Modal,
|
|
Label,
|
|
AbsTooltip,
|
|
} from "@budibase/bbui"
|
|
import { sdk } from "@budibase/shared-core"
|
|
import AutomationBlockSetup from "../../SetupPanel/AutomationBlockSetup.svelte"
|
|
import CreateWebhookModal from "components/automation/Shared/CreateWebhookModal.svelte"
|
|
import FlowItemHeader from "./FlowItemHeader.svelte"
|
|
import RoleSelect from "components/design/settings/controls/RoleSelect.svelte"
|
|
import { ActionStepID, TriggerStepID } from "constants/backend/automations"
|
|
import { AutomationStepType } from "@budibase/types"
|
|
import FlowItemActions from "./FlowItemActions.svelte"
|
|
import DragHandle from "components/design/settings/controls/DraggableList/drag-handle.svelte"
|
|
import { getContext } from "svelte"
|
|
import DragZone from "./DragZone.svelte"
|
|
import InfoDisplay from "pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/InfoDisplay.svelte"
|
|
|
|
export let block
|
|
export let blockRef
|
|
export let testDataModal
|
|
export let idx
|
|
export let automation
|
|
export let bindings
|
|
export let draggable = true
|
|
|
|
const view = getContext("draggableView")
|
|
const pos = getContext("viewPos")
|
|
const contentPos = getContext("contentPos")
|
|
|
|
let webhookModal
|
|
let open = true
|
|
let showLooping = false
|
|
let role
|
|
let blockEle
|
|
let positionStyles
|
|
let blockDims
|
|
|
|
const updateBlockDims = () => {
|
|
if (!blockEle) {
|
|
return
|
|
}
|
|
const { width, height } = blockEle.getBoundingClientRect()
|
|
blockDims = { width: width / $view.scale, height: height / $view.scale }
|
|
}
|
|
|
|
const loadSteps = blockRef => {
|
|
return blockRef
|
|
? automationStore.actions.getPathSteps(blockRef.pathTo, automation)
|
|
: []
|
|
}
|
|
|
|
$: pathSteps = loadSteps(blockRef)
|
|
|
|
$: collectBlockExists = pathSteps.some(
|
|
step => step.stepId === ActionStepID.COLLECT
|
|
)
|
|
$: automationId = automation?._id
|
|
$: isTrigger = block.type === AutomationStepType.TRIGGER
|
|
$: lastStep = blockRef?.terminating
|
|
|
|
$: loopBlock = pathSteps.find(x => x.blockToLoop === block.id)
|
|
$: isAppAction = block?.stepId === TriggerStepID.APP
|
|
$: isAppAction && setPermissions(role)
|
|
$: isAppAction && getPermissions(automationId)
|
|
|
|
$: triggerInfo = sdk.automations.isRowAction($selectedAutomation?.data) && {
|
|
title: "Automation trigger",
|
|
tableName: $tables.list.find(
|
|
x =>
|
|
x._id === $selectedAutomation.data?.definition?.trigger?.inputs?.tableId
|
|
)?.name,
|
|
}
|
|
|
|
$: selected = $view?.moveStep && $view?.moveStep?.id === block.id
|
|
|
|
$: if (selected && blockEle) {
|
|
updateBlockDims()
|
|
}
|
|
|
|
$: placeholderDims = buildPlaceholderStyles(blockDims)
|
|
|
|
// Move the selected item
|
|
// Listen for scrolling in the content. As its scrolled this will be updated
|
|
$: move(
|
|
blockEle,
|
|
$view?.dragSpot,
|
|
selected,
|
|
$contentPos?.scrollX,
|
|
$contentPos?.scrollY
|
|
)
|
|
|
|
const move = (block, dragPos, selected, scrollX, scrollY) => {
|
|
if ((!block && !selected) || !dragPos) {
|
|
return
|
|
}
|
|
positionStyles = `
|
|
--blockPosX: ${Math.round(dragPos.x - scrollX / $view.scale)}px;
|
|
--blockPosY: ${Math.round(dragPos.y - scrollY / $view.scale)}px;
|
|
`
|
|
}
|
|
|
|
const buildPlaceholderStyles = dims => {
|
|
if (!dims) {
|
|
return ""
|
|
}
|
|
const { width, height } = dims
|
|
return `--pswidth: ${Math.round(width)}px;
|
|
--psheight: ${Math.round(height)}px;`
|
|
}
|
|
|
|
async function setPermissions(role) {
|
|
if (!role || !automationId) {
|
|
return
|
|
}
|
|
await permissions.save({
|
|
level: "execute",
|
|
role,
|
|
resource: automationId,
|
|
})
|
|
}
|
|
|
|
async function getPermissions(automationId) {
|
|
if (!automationId) {
|
|
return
|
|
}
|
|
const perms = await permissions.forResource(automationId)
|
|
if (!perms["execute"]) {
|
|
role = "BASIC"
|
|
} else {
|
|
role = perms["execute"].role
|
|
}
|
|
}
|
|
|
|
async function deleteStep() {
|
|
await automationStore.actions.deleteAutomationBlock(blockRef.pathTo)
|
|
}
|
|
|
|
async function removeLooping() {
|
|
let loopBlockRef = $selectedAutomation.blockRefs[blockRef.looped]
|
|
await automationStore.actions.deleteAutomationBlock(loopBlockRef.pathTo)
|
|
}
|
|
|
|
async function addLooping() {
|
|
const loopDefinition = $automationStore.blockDefinitions.ACTION.LOOP
|
|
const loopBlock = automationStore.actions.constructBlock(
|
|
"ACTION",
|
|
"LOOP",
|
|
loopDefinition
|
|
)
|
|
loopBlock.blockToLoop = block.id
|
|
await automationStore.actions.addBlockToAutomation(
|
|
loopBlock,
|
|
blockRef.pathTo
|
|
)
|
|
}
|
|
|
|
const onHandleMouseDown = e => {
|
|
if (isTrigger) {
|
|
e.preventDefault()
|
|
return
|
|
}
|
|
|
|
e.stopPropagation()
|
|
|
|
view.update(state => ({
|
|
...state,
|
|
moveStep: {
|
|
id: block.id,
|
|
offsetX: $pos.x,
|
|
offsetY: $pos.y,
|
|
},
|
|
}))
|
|
}
|
|
</script>
|
|
|
|
{#if block.stepId !== "LOOP"}
|
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
|
<div
|
|
id={`block-${block.id}`}
|
|
class={`block ${block.type} hoverable`}
|
|
class:selected
|
|
class:draggable
|
|
>
|
|
<div class="wrap">
|
|
{#if $view.dragging && selected}
|
|
<div class="drag-placeholder" style={placeholderDims} />
|
|
{/if}
|
|
|
|
<div
|
|
bind:this={blockEle}
|
|
class="block-content"
|
|
class:dragging={$view.dragging && selected}
|
|
style={positionStyles}
|
|
on:mousedown={e => {
|
|
e.stopPropagation()
|
|
}}
|
|
>
|
|
{#if draggable}
|
|
<div
|
|
class="handle"
|
|
class:grabbing={selected}
|
|
on:mousedown={onHandleMouseDown}
|
|
>
|
|
<DragHandle />
|
|
</div>
|
|
{/if}
|
|
<div class="block-core">
|
|
{#if loopBlock}
|
|
<div class="blockSection">
|
|
<div
|
|
on:click={() => {
|
|
showLooping = !showLooping
|
|
}}
|
|
class="splitHeader"
|
|
>
|
|
<div class="center-items">
|
|
<svg
|
|
width="28px"
|
|
height="28px"
|
|
class="spectrum-Icon"
|
|
style="color:var(--spectrum-global-color-gray-700);"
|
|
focusable="false"
|
|
>
|
|
<use xlink:href="#spectrum-icon-18-Reuse" />
|
|
</svg>
|
|
<div class="iconAlign">
|
|
<Detail size="S">Looping</Detail>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="blockTitle">
|
|
<AbsTooltip type="negative" text="Remove looping">
|
|
<Icon
|
|
on:click={removeLooping}
|
|
hoverable
|
|
name="DeleteOutline"
|
|
/>
|
|
</AbsTooltip>
|
|
|
|
<div style="margin-left: 10px;" on:click={() => {}}>
|
|
<Icon
|
|
hoverable
|
|
name={showLooping ? "ChevronDown" : "ChevronUp"}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<Divider noMargin />
|
|
{#if !showLooping}
|
|
<div class="blockSection">
|
|
<Layout noPadding gap="S">
|
|
<AutomationBlockSetup
|
|
schemaProperties={Object.entries(
|
|
$automationStore.blockDefinitions.ACTION.LOOP.schema
|
|
.inputs.properties
|
|
)}
|
|
{webhookModal}
|
|
block={loopBlock}
|
|
{automation}
|
|
{bindings}
|
|
/>
|
|
</Layout>
|
|
</div>
|
|
<Divider noMargin />
|
|
{/if}
|
|
{/if}
|
|
|
|
<FlowItemHeader
|
|
{automation}
|
|
{open}
|
|
{block}
|
|
{testDataModal}
|
|
{idx}
|
|
{addLooping}
|
|
{deleteStep}
|
|
on:toggle={() => (open = !open)}
|
|
on:update={async e => {
|
|
const newName = e.detail
|
|
if (newName.length === 0) {
|
|
await automationStore.actions.deleteAutomationName(block.id)
|
|
} else {
|
|
await automationStore.actions.saveAutomationName(
|
|
block.id,
|
|
newName
|
|
)
|
|
}
|
|
}}
|
|
/>
|
|
{#if open}
|
|
<Divider noMargin />
|
|
<div class="blockSection">
|
|
<Layout noPadding gap="S">
|
|
{#if isAppAction}
|
|
<div>
|
|
<Label>Role</Label>
|
|
<RoleSelect bind:value={role} />
|
|
</div>
|
|
{/if}
|
|
<AutomationBlockSetup
|
|
schemaProperties={Object.entries(
|
|
block?.schema?.inputs?.properties || {}
|
|
)}
|
|
{block}
|
|
{webhookModal}
|
|
{automation}
|
|
{bindings}
|
|
/>
|
|
{#if isTrigger && triggerInfo}
|
|
<InfoDisplay
|
|
title={triggerInfo.title}
|
|
body="This trigger is tied to your '{triggerInfo.tableName}' table"
|
|
icon="InfoOutline"
|
|
/>
|
|
{/if}
|
|
</Layout>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{#if !collectBlockExists || !lastStep}
|
|
<div class="separator" />
|
|
{#if $view.dragging}
|
|
<DragZone path={blockRef?.pathTo} />
|
|
{:else}
|
|
<FlowItemActions
|
|
{block}
|
|
on:branch={() => {
|
|
automationStore.actions.branchAutomation(
|
|
$selectedAutomation.blockRefs[block.id].pathTo,
|
|
automation
|
|
)
|
|
}}
|
|
/>
|
|
{/if}
|
|
{#if !lastStep}
|
|
<div class="separator" />
|
|
{/if}
|
|
{/if}
|
|
{/if}
|
|
|
|
<Modal bind:this={webhookModal} width="30%">
|
|
<CreateWebhookModal />
|
|
</Modal>
|
|
|
|
<style>
|
|
.delete-padding {
|
|
padding-left: 30px;
|
|
}
|
|
.block-options {
|
|
justify-content: flex-end;
|
|
align-items: center;
|
|
display: flex;
|
|
gap: var(--spacing-m);
|
|
}
|
|
.center-items {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
.splitHeader {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
.iconAlign {
|
|
padding: 0 0 0 var(--spacing-m);
|
|
display: inline-block;
|
|
}
|
|
.block {
|
|
width: 480px;
|
|
font-size: 16px;
|
|
border-radius: 4px;
|
|
}
|
|
.block .wrap {
|
|
width: 100%;
|
|
position: relative;
|
|
}
|
|
.block.draggable .wrap {
|
|
display: flex;
|
|
flex-direction: row;
|
|
}
|
|
.block.draggable .wrap .handle {
|
|
height: auto;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
background-color: var(--grey-3);
|
|
padding: 6px;
|
|
color: var(--grey-6);
|
|
cursor: grab;
|
|
}
|
|
.block.draggable .wrap .handle.grabbing {
|
|
cursor: grabbing;
|
|
}
|
|
.block.draggable .wrap .handle :global(.drag-handle) {
|
|
width: 6px;
|
|
}
|
|
.block .wrap .block-content {
|
|
width: 100%;
|
|
display: flex;
|
|
flex-direction: row;
|
|
background-color: var(--background);
|
|
border: 1px solid var(--grey-3);
|
|
border-radius: 4px;
|
|
}
|
|
.blockSection {
|
|
padding: var(--spacing-xl);
|
|
}
|
|
.separator {
|
|
width: 1px;
|
|
height: 25px;
|
|
border-left: 1px dashed var(--grey-4);
|
|
color: var(--grey-4);
|
|
align-self: center;
|
|
}
|
|
.blockTitle {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--spacing-s);
|
|
}
|
|
.drag-placeholder {
|
|
height: calc(var(--psheight) - 2px);
|
|
width: var(--pswidth);
|
|
background-color: rgba(92, 92, 92, 0.1);
|
|
border: 1px dashed #5c5c5c;
|
|
border-radius: 4px;
|
|
display: block;
|
|
}
|
|
.block-core {
|
|
flex: 1;
|
|
}
|
|
.block-core.dragging {
|
|
pointer-events: none;
|
|
}
|
|
.block-content.dragging {
|
|
position: absolute;
|
|
z-index: 3;
|
|
top: var(--blockPosY);
|
|
left: var(--blockPosX);
|
|
}
|
|
</style>
|