mirror of https://github.com/Budibase/budibase.git
27 changed files with 467 additions and 496 deletions
@ -1,93 +1,31 @@ |
|||
<script> |
|||
import { afterUpdate } from "svelte" |
|||
import { automationStore, backendUiStore } from "builderStore" |
|||
import { notifier } from "builderStore/store/notifications" |
|||
import Flowchart from "./flowchart/FlowChart.svelte" |
|||
import BlockList from "./BlockList.svelte" |
|||
|
|||
$: automation = $automationStore.selectedAutomation?.automation |
|||
$: automationLive = automation?.live |
|||
$: instanceId = $backendUiStore.selectedDatabase._id |
|||
$: automationCount = $automationStore.automations?.length ?? 0 |
|||
|
|||
function onSelect(block) { |
|||
automationStore.update(state => { |
|||
automationStore.update((state) => { |
|||
state.selectedBlock = block |
|||
return state |
|||
}) |
|||
} |
|||
|
|||
function setAutomationLive(live) { |
|||
automation.live = live |
|||
automationStore.actions.save({ instanceId, automation }) |
|||
if (live) { |
|||
notifier.info(`Automation ${automation.name} enabled.`) |
|||
} else { |
|||
notifier.danger(`Automation ${automation.name} disabled.`) |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<section> |
|||
{#if automation} |
|||
<BlockList /> |
|||
<Flowchart {automation} {onSelect} /> |
|||
</section> |
|||
<footer> |
|||
{#if automation} |
|||
<button |
|||
class:highlighted={automationLive} |
|||
class:hoverable={automationLive} |
|||
class="stop-button hoverable"> |
|||
<i class="ri-stop-fill" on:click={() => setAutomationLive(false)} /> |
|||
</button> |
|||
<button |
|||
class:highlighted={!automationLive} |
|||
class:hoverable={!automationLive} |
|||
class="play-button hoverable" |
|||
data-cy="activate-automation" |
|||
on:click={() => setAutomationLive(true)}> |
|||
<i class="ri-play-fill" /> |
|||
</button> |
|||
{/if} |
|||
</footer> |
|||
{:else if automationCount === 0} |
|||
<i>Create your first automation to get started</i> |
|||
{:else}<i>Select an automation to edit</i>{/if} |
|||
|
|||
<style> |
|||
section { |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: flex-start; |
|||
align-items: center; |
|||
overflow: auto; |
|||
height: 100%; |
|||
position: relative; |
|||
} |
|||
|
|||
footer { |
|||
position: absolute; |
|||
bottom: var(--spacing-xl); |
|||
right: 30px; |
|||
display: flex; |
|||
align-items: flex-end; |
|||
} |
|||
|
|||
footer > button { |
|||
border-radius: 100%; |
|||
color: var(--white); |
|||
width: 76px; |
|||
height: 76px; |
|||
border: none; |
|||
background: #adaec4; |
|||
font-size: 45px; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
footer > button:first-child { |
|||
margin-right: var(--spacing-m); |
|||
} |
|||
|
|||
.play-button.highlighted { |
|||
background: var(--purple); |
|||
} |
|||
|
|||
.stop-button.highlighted { |
|||
background: var(--red); |
|||
i { |
|||
font-size: var(--font-size-m); |
|||
color: var(--grey-5); |
|||
} |
|||
</style> |
|||
|
|||
@ -0,0 +1,129 @@ |
|||
<script> |
|||
import { sortBy } from "lodash/fp" |
|||
import { automationStore } from "builderStore" |
|||
import { DropdownMenu } from "@budibase/bbui" |
|||
import { DropdownContainer, DropdownItem } from "../../common/Dropdowns" |
|||
import analytics from "analytics" |
|||
|
|||
$: hasTrigger = $automationStore.selectedAutomation.hasTrigger() |
|||
$: tabs = [ |
|||
{ |
|||
label: "Trigger", |
|||
value: "TRIGGER", |
|||
icon: "ri-organization-chart", |
|||
disabled: hasTrigger, |
|||
}, |
|||
{ |
|||
label: "Action", |
|||
value: "ACTION", |
|||
icon: "ri-flow-chart", |
|||
disabled: !hasTrigger, |
|||
}, |
|||
{ |
|||
label: "Logic", |
|||
value: "LOGIC", |
|||
icon: "ri-filter-line", |
|||
disabled: !hasTrigger, |
|||
}, |
|||
] |
|||
|
|||
let buttonProps = [] |
|||
let selectedIndex |
|||
let anchors = [] |
|||
let popover |
|||
$: selectedTab = selectedIndex == null ? null : tabs[selectedIndex].value |
|||
$: anchor = selectedIndex === -1 ? null : anchors[selectedIndex] |
|||
$: blocks = sortBy((entry) => entry[1].name)( |
|||
Object.entries($automationStore.blockDefinitions[selectedTab] ?? {}) |
|||
) |
|||
|
|||
function onChangeTab(idx) { |
|||
selectedIndex = idx |
|||
popover.show() |
|||
} |
|||
|
|||
function closePopover() { |
|||
selectedIndex = null |
|||
popover.hide() |
|||
} |
|||
|
|||
function addBlockToAutomation(stepId, blockDefinition) { |
|||
const newBlock = { |
|||
...blockDefinition, |
|||
inputs: blockDefinition.inputs || {}, |
|||
stepId, |
|||
type: selectedTab, |
|||
} |
|||
automationStore.actions.addBlockToAutomation(newBlock) |
|||
analytics.captureEvent("Added Automation Block", { |
|||
name: blockDefinition.name, |
|||
}) |
|||
closePopover() |
|||
} |
|||
</script> |
|||
|
|||
<div class="tab-container"> |
|||
{#each tabs as tab, idx} |
|||
<div |
|||
bind:this={anchors[idx]} |
|||
class="tab" |
|||
class:disabled={tab.disabled} |
|||
on:click={tab.disabled ? null : () => onChangeTab(idx)} |
|||
class:active={idx === selectedIndex}> |
|||
{#if tab.icon}<i class={tab.icon} />{/if} |
|||
<span>{tab.label}</span> |
|||
<i class="ri-arrow-down-s-line arrow" /> |
|||
</div> |
|||
{/each} |
|||
</div> |
|||
<DropdownMenu |
|||
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> |
|||
</DropdownMenu> |
|||
|
|||
<style> |
|||
.tab-container { |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: flex-start; |
|||
align-items: center; |
|||
gap: var(--spacing-l); |
|||
min-height: 24px; |
|||
} |
|||
|
|||
.tab { |
|||
color: var(--grey-7); |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: flex-start; |
|||
align-items: center; |
|||
gap: var(--spacing-xs); |
|||
font-size: var(--font-size-xs); |
|||
} |
|||
.tab span { |
|||
font-weight: 500; |
|||
user-select: none; |
|||
} |
|||
.tab.active, |
|||
.tab:not(.disabled):hover { |
|||
color: var(--ink); |
|||
cursor: pointer; |
|||
} |
|||
.tab.disabled { |
|||
color: var(--grey-5); |
|||
} |
|||
.tab i:not(:last-child) { |
|||
font-size: 16px; |
|||
} |
|||
</style> |
|||
|
Before Width: | Height: | Size: 303 B After Width: | Height: | Size: 303 B |
@ -0,0 +1,34 @@ |
|||
<script> |
|||
import { onMount } from "svelte" |
|||
import { automationStore } from "builderStore" |
|||
import NavItem from "components/common/NavItem.svelte" |
|||
import EditAutomationPopover from "./EditAutomationPopover.svelte" |
|||
|
|||
$: selectedAutomationId = $automationStore.selectedAutomation?.automation?._id |
|||
|
|||
onMount(() => { |
|||
automationStore.actions.fetch() |
|||
}) |
|||
</script> |
|||
|
|||
<div class="automations-list"> |
|||
{#each $automationStore.automations as automation, idx} |
|||
<NavItem |
|||
border={idx > 0} |
|||
icon="ri-stackshare-line" |
|||
text={automation.name} |
|||
selected={automation._id === selectedAutomationId} |
|||
on:click={() => automationStore.actions.select(automation)}> |
|||
<EditAutomationPopover {automation} /> |
|||
</NavItem> |
|||
{/each} |
|||
</div> |
|||
|
|||
<style> |
|||
.automations-list { |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: flex-start; |
|||
align-items: stretch; |
|||
} |
|||
</style> |
|||
@ -1,79 +0,0 @@ |
|||
<script> |
|||
import { onMount } from "svelte" |
|||
import { automationStore } from "builderStore" |
|||
import CreateAutomationModal from "./CreateAutomationModal.svelte" |
|||
import { Button, Modal } from "@budibase/bbui" |
|||
|
|||
let modal |
|||
|
|||
$: selectedAutomationId = $automationStore.selectedAutomation?.automation?._id |
|||
|
|||
onMount(() => { |
|||
automationStore.actions.fetch() |
|||
}) |
|||
</script> |
|||
|
|||
<section> |
|||
<Button primary wide on:click={modal.show}>Create New Automation</Button> |
|||
<ul> |
|||
{#each $automationStore.automations as automation} |
|||
<li |
|||
class="automation-item" |
|||
class:selected={automation._id === selectedAutomationId} |
|||
on:click={() => automationStore.actions.select(automation)}> |
|||
<i class="ri-stackshare-line" class:live={automation.live} /> |
|||
{automation.name} |
|||
</li> |
|||
{/each} |
|||
</ul> |
|||
</section> |
|||
<Modal bind:this={modal}> |
|||
<CreateAutomationModal /> |
|||
</Modal> |
|||
|
|||
<style> |
|||
section { |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
ul { |
|||
list-style-type: none; |
|||
padding: 0; |
|||
margin: var(--spacing-xl) 0 0 0; |
|||
flex: 1; |
|||
} |
|||
|
|||
i { |
|||
color: var(--grey-6); |
|||
} |
|||
i.live { |
|||
color: var(--ink); |
|||
} |
|||
|
|||
li { |
|||
font-size: 14px; |
|||
} |
|||
|
|||
.automation-item { |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: flex-start; |
|||
align-items: center; |
|||
border-radius: var(--border-radius-m); |
|||
padding: var(--spacing-s) var(--spacing-m); |
|||
margin-bottom: var(--spacing-xs); |
|||
color: var(--ink); |
|||
} |
|||
.automation-item i { |
|||
font-size: 24px; |
|||
margin-right: var(--spacing-m); |
|||
} |
|||
.automation-item:hover { |
|||
cursor: pointer; |
|||
background: var(--grey-1); |
|||
} |
|||
.automation-item.selected { |
|||
background: var(--grey-2); |
|||
} |
|||
</style> |
|||
@ -1,68 +0,0 @@ |
|||
<script> |
|||
import { automationStore } from "builderStore" |
|||
import analytics from "analytics" |
|||
|
|||
export let blockDefinition |
|||
export let stepId |
|||
export let blockType |
|||
|
|||
function addBlockToAutomation() { |
|||
automationStore.actions.addBlockToAutomation({ |
|||
...blockDefinition, |
|||
inputs: blockDefinition.inputs || {}, |
|||
stepId, |
|||
type: blockType, |
|||
}) |
|||
analytics.captureEvent("Added Automation Block", { |
|||
name: blockDefinition.name, |
|||
}) |
|||
} |
|||
</script> |
|||
|
|||
<div |
|||
class="automation-block hoverable" |
|||
on:click={addBlockToAutomation} |
|||
data-cy={stepId}> |
|||
<div><i class={blockDefinition.icon} /></div> |
|||
<div class="automation-text"> |
|||
<h4>{blockDefinition.name}</h4> |
|||
<p>{blockDefinition.description}</p> |
|||
</div> |
|||
</div> |
|||
|
|||
<style> |
|||
.automation-block { |
|||
display: grid; |
|||
grid-template-columns: 20px auto; |
|||
align-items: center; |
|||
margin-top: var(--spacing-s); |
|||
padding: var(--spacing-m); |
|||
border-radius: var(--border-radius-m); |
|||
} |
|||
.automation-block:hover { |
|||
background-color: var(--grey-1); |
|||
} |
|||
.automation-block:first-child { |
|||
margin-top: 0; |
|||
} |
|||
|
|||
i { |
|||
color: var(--grey-7); |
|||
font-size: 20px; |
|||
} |
|||
|
|||
.automation-text { |
|||
margin-left: 16px; |
|||
} |
|||
.automation-text h4 { |
|||
font-size: 14px; |
|||
font-weight: 500; |
|||
margin-bottom: 5px; |
|||
margin-top: 0; |
|||
} |
|||
.automation-text p { |
|||
font-size: 12px; |
|||
color: var(--grey-7); |
|||
margin: 0; |
|||
} |
|||
</style> |
|||
@ -1,48 +0,0 @@ |
|||
<script> |
|||
import { sortBy } from "lodash/fp" |
|||
import { automationStore } from "builderStore" |
|||
import AutomationBlock from "./AutomationBlock.svelte" |
|||
import FlatButtonGroup from "components/userInterface/FlatButtonGroup.svelte" |
|||
|
|||
let selectedTab = "TRIGGER" |
|||
let buttonProps = [] |
|||
$: blocks = sortBy(entry => entry[1].name)( |
|||
Object.entries($automationStore.blockDefinitions[selectedTab]) |
|||
) |
|||
|
|||
$: { |
|||
if ($automationStore.selectedAutomation.hasTrigger()) { |
|||
buttonProps = [ |
|||
{ value: "ACTION", text: "Action" }, |
|||
{ value: "LOGIC", text: "Logic" }, |
|||
] |
|||
if (selectedTab === "TRIGGER") { |
|||
selectedTab = "ACTION" |
|||
} |
|||
} else { |
|||
buttonProps = [{ value: "TRIGGER", text: "Trigger" }] |
|||
if (selectedTab !== "TRIGGER") { |
|||
selectedTab = "TRIGGER" |
|||
} |
|||
} |
|||
} |
|||
|
|||
function onChangeTab(tab) { |
|||
selectedTab = tab |
|||
} |
|||
</script> |
|||
|
|||
<section> |
|||
<FlatButtonGroup value={selectedTab} {buttonProps} onChange={onChangeTab} /> |
|||
<div id="blocklist"> |
|||
{#each blocks as [stepId, blockDefinition]} |
|||
<AutomationBlock {blockDefinition} {stepId} blockType={selectedTab} /> |
|||
{/each} |
|||
</div> |
|||
</section> |
|||
|
|||
<style> |
|||
#blocklist { |
|||
margin-top: var(--spacing-xl); |
|||
} |
|||
</style> |
|||
@ -0,0 +1,86 @@ |
|||
<script> |
|||
import { automationStore, backendUiStore } from "builderStore" |
|||
import { notifier } from "builderStore/store/notifications" |
|||
import { DropdownMenu } from "@budibase/bbui" |
|||
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns" |
|||
import ConfirmDialog from "components/common/ConfirmDialog.svelte" |
|||
|
|||
export let automation |
|||
|
|||
let anchor |
|||
let dropdown |
|||
let confirmDeleteDialog |
|||
$: instanceId = $backendUiStore.selectedDatabase._id |
|||
|
|||
function showModal() { |
|||
dropdown.hide() |
|||
confirmDeleteDialog.show() |
|||
} |
|||
|
|||
async function deleteAutomation() { |
|||
await automationStore.actions.delete({ |
|||
instanceId, |
|||
automation, |
|||
}) |
|||
notifier.success("Automation deleted.") |
|||
} |
|||
</script> |
|||
|
|||
<div bind:this={anchor} class="icon" on:click={dropdown.show}> |
|||
<i class="ri-more-line" /> |
|||
</div> |
|||
<DropdownMenu align="left" {anchor} bind:this={dropdown}> |
|||
<DropdownContainer> |
|||
<DropdownItem |
|||
icon="ri-delete-bin-line" |
|||
title="Delete" |
|||
on:click={showModal} /> |
|||
</DropdownContainer> |
|||
</DropdownMenu> |
|||
<ConfirmDialog |
|||
bind:this={confirmDeleteDialog} |
|||
okText="Delete Automation" |
|||
onOk={deleteAutomation} |
|||
title="Confirm Delete"> |
|||
Are you sure you wish to delete the automation |
|||
<i>{automation.name}?</i> |
|||
This action cannot be undone. |
|||
</ConfirmDialog> |
|||
|
|||
<style> |
|||
div.icon { |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: flex-end; |
|||
align-items: center; |
|||
} |
|||
|
|||
div.icon i { |
|||
font-size: 16px; |
|||
} |
|||
|
|||
ul { |
|||
list-style: none; |
|||
margin: 0; |
|||
padding: var(--spacing-s) 0; |
|||
} |
|||
|
|||
li { |
|||
display: flex; |
|||
font-family: var(--font-sans); |
|||
font-size: var(--font-size-xs); |
|||
color: var(--ink); |
|||
padding: var(--spacing-s) var(--spacing-m); |
|||
margin: auto 0; |
|||
align-items: center; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
li:hover { |
|||
background-color: var(--grey-2); |
|||
} |
|||
|
|||
li:active { |
|||
color: var(--blue); |
|||
} |
|||
</style> |
|||
@ -1,3 +0,0 @@ |
|||
export { default as AutomationPanel } from "./AutomationPanel.svelte" |
|||
export { default as BlockList } from "./BlockList/BlockList.svelte" |
|||
export { default as AutomationList } from "./AutomationList/AutomationList.svelte" |
|||
@ -1 +0,0 @@ |
|||
export { default as SetupPanel } from "./SetupPanel.svelte" |
|||
@ -1,5 +1,5 @@ |
|||
<script> |
|||
import { AutomationBuilder } from "components/automation" |
|||
import AutomationBuilder from "components/automation/AutomationBuilder/AutomationBuilder.svelte" |
|||
</script> |
|||
|
|||
<AutomationBuilder /> |
|||
|
|||
Loading…
Reference in new issue