Update styles across entire automations page
This commit is contained in:
parent
d356f8d609
commit
0553da5a03
|
@ -3,14 +3,14 @@ import api from "../../api"
|
||||||
import Automation from "./Automation"
|
import Automation from "./Automation"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
|
|
||||||
const automationActions = store => ({
|
const automationActions = (store) => ({
|
||||||
fetch: async () => {
|
fetch: async () => {
|
||||||
const responses = await Promise.all([
|
const responses = await Promise.all([
|
||||||
api.get(`/api/automations`),
|
api.get(`/api/automations`),
|
||||||
api.get(`/api/automations/definitions/list`),
|
api.get(`/api/automations/definitions/list`),
|
||||||
])
|
])
|
||||||
const jsonResponses = await Promise.all(responses.map(x => x.json()))
|
const jsonResponses = await Promise.all(responses.map((x) => x.json()))
|
||||||
store.update(state => {
|
store.update((state) => {
|
||||||
state.automations = jsonResponses[0]
|
state.automations = jsonResponses[0]
|
||||||
state.blockDefinitions = {
|
state.blockDefinitions = {
|
||||||
TRIGGER: jsonResponses[1].trigger,
|
TRIGGER: jsonResponses[1].trigger,
|
||||||
|
@ -31,7 +31,7 @@ const automationActions = store => ({
|
||||||
const CREATE_AUTOMATION_URL = `/api/automations`
|
const CREATE_AUTOMATION_URL = `/api/automations`
|
||||||
const response = await api.post(CREATE_AUTOMATION_URL, automation)
|
const response = await api.post(CREATE_AUTOMATION_URL, automation)
|
||||||
const json = await response.json()
|
const json = await response.json()
|
||||||
store.update(state => {
|
store.update((state) => {
|
||||||
state.automations = [...state.automations, json.automation]
|
state.automations = [...state.automations, json.automation]
|
||||||
store.actions.select(json.automation)
|
store.actions.select(json.automation)
|
||||||
return state
|
return state
|
||||||
|
@ -41,9 +41,9 @@ const automationActions = store => ({
|
||||||
const UPDATE_AUTOMATION_URL = `/api/automations`
|
const UPDATE_AUTOMATION_URL = `/api/automations`
|
||||||
const response = await api.put(UPDATE_AUTOMATION_URL, automation)
|
const response = await api.put(UPDATE_AUTOMATION_URL, automation)
|
||||||
const json = await response.json()
|
const json = await response.json()
|
||||||
store.update(state => {
|
store.update((state) => {
|
||||||
const existingIdx = state.automations.findIndex(
|
const existingIdx = state.automations.findIndex(
|
||||||
existing => existing._id === automation._id
|
(existing) => existing._id === automation._id
|
||||||
)
|
)
|
||||||
state.automations.splice(existingIdx, 1, json.automation)
|
state.automations.splice(existingIdx, 1, json.automation)
|
||||||
state.automations = state.automations
|
state.automations = state.automations
|
||||||
|
@ -56,9 +56,9 @@ const automationActions = store => ({
|
||||||
const DELETE_AUTOMATION_URL = `/api/automations/${_id}/${_rev}`
|
const DELETE_AUTOMATION_URL = `/api/automations/${_id}/${_rev}`
|
||||||
await api.delete(DELETE_AUTOMATION_URL)
|
await api.delete(DELETE_AUTOMATION_URL)
|
||||||
|
|
||||||
store.update(state => {
|
store.update((state) => {
|
||||||
const existingIdx = state.automations.findIndex(
|
const existingIdx = state.automations.findIndex(
|
||||||
existing => existing._id === _id
|
(existing) => existing._id === _id
|
||||||
)
|
)
|
||||||
state.automations.splice(existingIdx, 1)
|
state.automations.splice(existingIdx, 1)
|
||||||
state.automations = state.automations
|
state.automations = state.automations
|
||||||
|
@ -72,24 +72,24 @@ const automationActions = store => ({
|
||||||
const TRIGGER_AUTOMATION_URL = `/api/automations/${_id}/trigger`
|
const TRIGGER_AUTOMATION_URL = `/api/automations/${_id}/trigger`
|
||||||
return await api.post(TRIGGER_AUTOMATION_URL)
|
return await api.post(TRIGGER_AUTOMATION_URL)
|
||||||
},
|
},
|
||||||
select: automation => {
|
select: (automation) => {
|
||||||
store.update(state => {
|
store.update((state) => {
|
||||||
state.selectedAutomation = new Automation(cloneDeep(automation))
|
state.selectedAutomation = new Automation(cloneDeep(automation))
|
||||||
state.selectedBlock = null
|
state.selectedBlock = null
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
addBlockToAutomation: block => {
|
addBlockToAutomation: (block) => {
|
||||||
store.update(state => {
|
store.update((state) => {
|
||||||
const newBlock = state.selectedAutomation.addBlock(cloneDeep(block))
|
const newBlock = state.selectedAutomation.addBlock(cloneDeep(block))
|
||||||
state.selectedBlock = newBlock
|
state.selectedBlock = newBlock
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
deleteAutomationBlock: block => {
|
deleteAutomationBlock: (block) => {
|
||||||
store.update(state => {
|
store.update((state) => {
|
||||||
const idx = state.selectedAutomation.automation.definition.steps.findIndex(
|
const idx = state.selectedAutomation.automation.definition.steps.findIndex(
|
||||||
x => x.id === block.id
|
(x) => x.id === block.id
|
||||||
)
|
)
|
||||||
state.selectedAutomation.deleteBlock(block.id)
|
state.selectedAutomation.deleteBlock(block.id)
|
||||||
|
|
||||||
|
|
|
@ -1,93 +1,31 @@
|
||||||
<script>
|
<script>
|
||||||
import { afterUpdate } from "svelte"
|
|
||||||
import { automationStore, backendUiStore } from "builderStore"
|
import { automationStore, backendUiStore } from "builderStore"
|
||||||
import { notifier } from "builderStore/store/notifications"
|
|
||||||
import Flowchart from "./flowchart/FlowChart.svelte"
|
import Flowchart from "./flowchart/FlowChart.svelte"
|
||||||
|
import BlockList from "./BlockList.svelte"
|
||||||
|
|
||||||
$: automation = $automationStore.selectedAutomation?.automation
|
$: automation = $automationStore.selectedAutomation?.automation
|
||||||
$: automationLive = automation?.live
|
$: automationLive = automation?.live
|
||||||
$: instanceId = $backendUiStore.selectedDatabase._id
|
$: instanceId = $backendUiStore.selectedDatabase._id
|
||||||
|
$: automationCount = $automationStore.automations?.length ?? 0
|
||||||
|
|
||||||
function onSelect(block) {
|
function onSelect(block) {
|
||||||
automationStore.update(state => {
|
automationStore.update((state) => {
|
||||||
state.selectedBlock = block
|
state.selectedBlock = block
|
||||||
return state
|
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>
|
</script>
|
||||||
|
|
||||||
<section>
|
{#if automation}
|
||||||
|
<BlockList />
|
||||||
<Flowchart {automation} {onSelect} />
|
<Flowchart {automation} {onSelect} />
|
||||||
</section>
|
{:else if automationCount === 0}
|
||||||
<footer>
|
<i>Create your first automation to get started</i>
|
||||||
{#if automation}
|
{:else}<i>Select an automation to edit</i>{/if}
|
||||||
<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>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
section {
|
i {
|
||||||
display: flex;
|
font-size: var(--font-size-m);
|
||||||
flex-direction: column;
|
color: var(--grey-5);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
</style>
|
</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 |
|
@ -18,16 +18,9 @@
|
||||||
blocks = blocks.concat(automation.definition.steps || [])
|
blocks = blocks.concat(automation.definition.steps || [])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$: automationCount = $automationStore.automations?.length ?? 0
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if automationCount === 0}
|
{#if !blocks.length}<i>Add a trigger to your automation to get started</i>{/if}
|
||||||
<i>Create your first automation to get started</i>
|
|
||||||
{:else if automation == null}
|
|
||||||
<i>Select an automation to edit</i>
|
|
||||||
{:else if !blocks.length}
|
|
||||||
<i>Add some steps to your automation to get started</i>
|
|
||||||
{/if}
|
|
||||||
<section class="canvas">
|
<section class="canvas">
|
||||||
{#each blocks as block, idx (block.id)}
|
{#each blocks as block, idx (block.id)}
|
||||||
<div
|
<div
|
||||||
|
@ -44,19 +37,18 @@
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
i {
|
|
||||||
font-size: var(--font-size-xl);
|
|
||||||
color: var(--grey-4);
|
|
||||||
padding: var(--spacing-xl) 40px;
|
|
||||||
align-self: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
section {
|
section {
|
||||||
position: absolute;
|
margin: 0 -40px calc(-1 * var(--spacing-l)) -40px;
|
||||||
padding: 40px;
|
padding: var(--spacing-l) 40px 0 40px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
overflow-y: auto;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
/* Fix for firefox not respecting bottom padding in scrolling containers */
|
||||||
|
section > *:last-child {
|
||||||
|
padding-bottom: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.block {
|
.block {
|
||||||
|
@ -65,4 +57,9 @@
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: var(--font-size-m);
|
||||||
|
color: var(--grey-5);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
|
@ -9,7 +9,12 @@
|
||||||
$: selected = $automationStore.selectedBlock?.id === block.id
|
$: selected = $automationStore.selectedBlock?.id === block.id
|
||||||
$: steps =
|
$: steps =
|
||||||
$automationStore.selectedAutomation?.automation?.definition?.steps ?? []
|
$automationStore.selectedAutomation?.automation?.definition?.steps ?? []
|
||||||
$: blockIdx = steps.findIndex(step => step.id === block.id)
|
$: blockIdx = steps.findIndex((step) => step.id === block.id)
|
||||||
|
$: allowDeleteTrigger = !steps.length
|
||||||
|
|
||||||
|
function deleteStep() {
|
||||||
|
automationStore.actions.deleteAutomationBlock(block)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
@ -30,6 +35,9 @@
|
||||||
<div class="label">
|
<div class="label">
|
||||||
{#if block.type === 'TRIGGER'}Trigger{:else}Step {blockIdx + 1}{/if}
|
{#if block.type === 'TRIGGER'}Trigger{:else}Step {blockIdx + 1}{/if}
|
||||||
</div>
|
</div>
|
||||||
|
{#if block.type !== 'TRIGGER' || allowDeleteTrigger}
|
||||||
|
<i on:click|stopPropagation={deleteStep} class="delete ri-close-line" />
|
||||||
|
{/if}
|
||||||
</header>
|
</header>
|
||||||
<hr />
|
<hr />
|
||||||
<p>
|
<p>
|
||||||
|
@ -61,6 +69,7 @@
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
gap: var(--spacing-xs);
|
||||||
}
|
}
|
||||||
header span {
|
header span {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
|
@ -74,7 +83,13 @@
|
||||||
}
|
}
|
||||||
header i {
|
header i {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
margin-right: 5px;
|
}
|
||||||
|
header i.delete {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
header i.delete:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ACTION {
|
.ACTION {
|
|
@ -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,50 +1,41 @@
|
||||||
<script>
|
<script>
|
||||||
import { automationStore } from "builderStore"
|
import AutomationList from "./AutomationList.svelte"
|
||||||
import AutomationList from "./AutomationList/AutomationList.svelte"
|
import CreateAutomationModal from "./CreateAutomationModal.svelte"
|
||||||
import BlockList from "./BlockList/BlockList.svelte"
|
import { Modal } from "@budibase/bbui"
|
||||||
import { Heading } from "@budibase/bbui"
|
import { automationStore, backendUiStore } from "builderStore"
|
||||||
import { Spacer } from "@budibase/bbui"
|
import { notifier } from "builderStore/store/notifications"
|
||||||
|
|
||||||
let selectedTab = "AUTOMATIONS"
|
let selectedTab = "AUTOMATIONS"
|
||||||
|
let modal
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Heading black small>
|
<div class="title">
|
||||||
<span
|
<h1>Automations</h1>
|
||||||
data-cy="automation-list"
|
<i on:click={modal.show} class="ri-add-circle-fill" />
|
||||||
class="hoverable automation-header"
|
</div>
|
||||||
class:selected={selectedTab === 'AUTOMATIONS'}
|
<AutomationList />
|
||||||
on:click={() => (selectedTab = 'AUTOMATIONS')}>
|
<Modal bind:this={modal}>
|
||||||
Automations
|
<CreateAutomationModal />
|
||||||
</span>
|
</Modal>
|
||||||
{#if $automationStore.selectedAutomation}
|
|
||||||
<span
|
|
||||||
data-cy="add-automation-component"
|
|
||||||
class="hoverable"
|
|
||||||
class:selected={selectedTab === 'ADD'}
|
|
||||||
on:click={() => (selectedTab = 'ADD')}>
|
|
||||||
Steps
|
|
||||||
</span>
|
|
||||||
{/if}
|
|
||||||
</Heading>
|
|
||||||
<Spacer medium />
|
|
||||||
{#if selectedTab === 'AUTOMATIONS'}
|
|
||||||
<AutomationList />
|
|
||||||
{:else if selectedTab === 'ADD'}
|
|
||||||
<BlockList />
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
header {
|
.title {
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 600;
|
|
||||||
background: none;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: var(--spacing-xl);
|
|
||||||
}
|
}
|
||||||
|
.title h1 {
|
||||||
.automation-header {
|
font-size: var(--font-size-m);
|
||||||
margin-right: var(--spacing-xl);
|
font-weight: 500;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.title i {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
.title i:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--blue);
|
||||||
}
|
}
|
||||||
|
|
||||||
span:not(.selected) {
|
span:not(.selected) {
|
||||||
|
|
|
@ -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,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import TableSelector from "./ParamInputs/TableSelector.svelte"
|
import TableSelector from "./TableSelector.svelte"
|
||||||
import RowSelector from "./ParamInputs/RowSelector.svelte"
|
import RowSelector from "./RowSelector.svelte"
|
||||||
import { Input, TextArea, Select, Label } from "@budibase/bbui"
|
import { Input, TextArea, Select, Label } from "@budibase/bbui"
|
||||||
import { automationStore } from "builderStore"
|
import { automationStore } from "builderStore"
|
||||||
import BindableInput from "../../userInterface/BindableInput.svelte"
|
import BindableInput from "../../userInterface/BindableInput.svelte"
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
if (automation.trigger) {
|
if (automation.trigger) {
|
||||||
allSteps = [automation.trigger, ...allSteps]
|
allSteps = [automation.trigger, ...allSteps]
|
||||||
}
|
}
|
||||||
const blockIdx = allSteps.findIndex(step => step.id === block.id)
|
const blockIdx = allSteps.findIndex((step) => step.id === block.id)
|
||||||
|
|
||||||
// Extract all outputs from all previous steps as available bindings
|
// Extract all outputs from all previous steps as available bindings
|
||||||
let bindings = []
|
let bindings = []
|
||||||
|
@ -44,13 +44,12 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="container" data-cy="automation-block-setup">
|
<div class="block-label">{block.name}</div>
|
||||||
<div class="block-label">{block.name}</div>
|
{#each inputs as [key, value]}
|
||||||
{#each inputs as [key, value]}
|
<div class="block-field">
|
||||||
<div class="bb-margin-xl block-field">
|
|
||||||
<Label extraSmall grey>{value.title}</Label>
|
<Label extraSmall grey>{value.title}</Label>
|
||||||
{#if value.type === 'string' && value.enum}
|
{#if value.type === 'string' && value.enum}
|
||||||
<Select bind:value={block.inputs[key]} thin secondary>
|
<Select bind:value={block.inputs[key]} extraThin secondary>
|
||||||
<option value="">Choose an option</option>
|
<option value="">Choose an option</option>
|
||||||
{#each value.enum as option, idx}
|
{#each value.enum as option, idx}
|
||||||
<option value={option}>
|
<option value={option}>
|
||||||
|
@ -59,7 +58,7 @@
|
||||||
{/each}
|
{/each}
|
||||||
</Select>
|
</Select>
|
||||||
{:else if value.customType === 'password'}
|
{:else if value.customType === 'password'}
|
||||||
<Input type="password" thin bind:value={block.inputs[key]} />
|
<Input type="password" extraThin bind:value={block.inputs[key]} />
|
||||||
{:else if value.customType === 'table'}
|
{:else if value.customType === 'table'}
|
||||||
<TableSelector bind:value={block.inputs[key]} />
|
<TableSelector bind:value={block.inputs[key]} />
|
||||||
{:else if value.customType === 'row'}
|
{:else if value.customType === 'row'}
|
||||||
|
@ -67,13 +66,12 @@
|
||||||
{:else if value.type === 'string' || value.type === 'number'}
|
{:else if value.type === 'string' || value.type === 'number'}
|
||||||
<BindableInput
|
<BindableInput
|
||||||
type="string"
|
type="string"
|
||||||
thin
|
extraThin
|
||||||
bind:value={block.inputs[key]}
|
bind:value={block.inputs[key]}
|
||||||
{bindings} />
|
{bindings} />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.block-field {
|
.block-field {
|
||||||
|
@ -82,7 +80,7 @@
|
||||||
|
|
||||||
.block-label {
|
.block-label {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 14px;
|
font-size: var(--font-size-xs);
|
||||||
color: var(--grey-7);
|
color: var(--grey-7);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
<script>
|
<script>
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
import { Input, Select, Label } from "@budibase/bbui"
|
import { Input, Select, Label } from "@budibase/bbui"
|
||||||
import BindableInput from "../../../userInterface/BindableInput.svelte"
|
import BindableInput from "../../userInterface/BindableInput.svelte"
|
||||||
|
|
||||||
export let value
|
export let value
|
||||||
export let bindings
|
export let bindings
|
||||||
|
|
||||||
$: table = $backendUiStore.tables.find(table => table._id === value?.tableId)
|
$: table = $backendUiStore.tables.find(
|
||||||
|
(table) => table._id === value?.tableId
|
||||||
|
)
|
||||||
$: schemaFields = Object.entries(table?.schema ?? {})
|
$: schemaFields = Object.entries(table?.schema ?? {})
|
||||||
|
|
||||||
// Ensure any nullish tableId values get set to empty string so
|
// Ensure any nullish tableId values get set to empty string so
|
||||||
|
@ -19,7 +21,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="block-field">
|
<div class="block-field">
|
||||||
<Select bind:value={value.tableId} thin secondary>
|
<Select bind:value={value.tableId} extraThin secondary>
|
||||||
<option value="">Choose an option</option>
|
<option value="">Choose an option</option>
|
||||||
{#each $backendUiStore.tables as table}
|
{#each $backendUiStore.tables as table}
|
||||||
<option value={table._id}>{table.name}</option>
|
<option value={table._id}>{table.name}</option>
|
||||||
|
@ -31,7 +33,7 @@
|
||||||
<div class="schema-fields">
|
<div class="schema-fields">
|
||||||
{#each schemaFields as [field, schema]}
|
{#each schemaFields as [field, schema]}
|
||||||
{#if schemaHasOptions(schema)}
|
{#if schemaHasOptions(schema)}
|
||||||
<Select label={field} thin secondary bind:value={value[field]}>
|
<Select label={field} extraThin secondary bind:value={value[field]}>
|
||||||
<option value="">Choose an option</option>
|
<option value="">Choose an option</option>
|
||||||
{#each schema.constraints.inclusion as option}
|
{#each schema.constraints.inclusion as option}
|
||||||
<option value={option}>{option}</option>
|
<option value={option}>{option}</option>
|
||||||
|
@ -39,7 +41,7 @@
|
||||||
</Select>
|
</Select>
|
||||||
{:else if schema.type === 'string' || schema.type === 'number'}
|
{:else if schema.type === 'string' || schema.type === 'number'}
|
||||||
<BindableInput
|
<BindableInput
|
||||||
thin
|
extraThin
|
||||||
bind:value={value[field]}
|
bind:value={value[field]}
|
||||||
label={field}
|
label={field}
|
||||||
type="string"
|
type="string"
|
|
@ -2,23 +2,27 @@
|
||||||
import { backendUiStore, automationStore } from "builderStore"
|
import { backendUiStore, automationStore } from "builderStore"
|
||||||
import { notifier } from "builderStore/store/notifications"
|
import { notifier } from "builderStore/store/notifications"
|
||||||
import AutomationBlockSetup from "./AutomationBlockSetup.svelte"
|
import AutomationBlockSetup from "./AutomationBlockSetup.svelte"
|
||||||
import { Button, Input, Label } from "@budibase/bbui"
|
import { Button } from "@budibase/bbui"
|
||||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
|
||||||
|
|
||||||
let selectedTab = "SETUP"
|
let selectedTab = "SETUP"
|
||||||
let confirmDeleteDialog
|
let confirmDeleteDialog
|
||||||
|
|
||||||
$: instanceId = $backendUiStore.selectedDatabase._id
|
$: instanceId = $backendUiStore.selectedDatabase._id
|
||||||
$: automation = $automationStore.selectedAutomation?.automation
|
$: automation = $automationStore.selectedAutomation?.automation
|
||||||
$: allowDeleteBlock =
|
|
||||||
$automationStore.selectedBlock?.type !== "TRIGGER" ||
|
|
||||||
!automation?.definition?.steps?.length
|
|
||||||
$: name = automation?.name ?? ""
|
$: name = automation?.name ?? ""
|
||||||
|
$: automationLive = automation?.live
|
||||||
|
|
||||||
function deleteAutomationBlock() {
|
function setAutomationLive(live) {
|
||||||
automationStore.actions.deleteAutomationBlock(
|
if (automation.live === live) {
|
||||||
$automationStore.selectedBlock
|
return
|
||||||
)
|
}
|
||||||
|
automation.live = live
|
||||||
|
automationStore.actions.save({ instanceId, automation })
|
||||||
|
if (live) {
|
||||||
|
notifier.info(`Automation ${automation.name} enabled.`)
|
||||||
|
} else {
|
||||||
|
notifier.danger(`Automation ${automation.name} disabled.`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testAutomation() {
|
async function testAutomation() {
|
||||||
|
@ -39,110 +43,74 @@
|
||||||
})
|
})
|
||||||
notifier.success(`Automation ${automation.name} saved.`)
|
notifier.success(`Automation ${automation.name} saved.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteAutomation() {
|
|
||||||
await automationStore.actions.delete({
|
|
||||||
instanceId,
|
|
||||||
automation,
|
|
||||||
})
|
|
||||||
notifier.success("Automation deleted.")
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<section>
|
<div class="title">
|
||||||
<header>
|
<h1>Setup</h1>
|
||||||
<span
|
<i
|
||||||
class="hoverable"
|
class:highlighted={automationLive}
|
||||||
class:selected={selectedTab === 'SETUP'}
|
class:hoverable={automationLive}
|
||||||
on:click={() => (selectedTab = 'SETUP')}>
|
on:click={() => setAutomationLive(false)}
|
||||||
Setup
|
class="ri-stop-circle-fill" />
|
||||||
</span>
|
<i
|
||||||
</header>
|
class:highlighted={!automationLive}
|
||||||
<div class="content">
|
class:hoverable={!automationLive}
|
||||||
{#if $automationStore.selectedBlock}
|
data-cy="activate-automation"
|
||||||
|
on:click={() => setAutomationLive(true)}
|
||||||
|
class="ri-play-circle-fill" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if $automationStore.selectedBlock}
|
||||||
<AutomationBlockSetup bind:block={$automationStore.selectedBlock} />
|
<AutomationBlockSetup bind:block={$automationStore.selectedBlock} />
|
||||||
{:else if $automationStore.selectedAutomation}
|
{:else if $automationStore.selectedAutomation}
|
||||||
<div class="block-label">Automation <b>{automation.name}</b></div>
|
<div class="block-label">{automation.name}</div>
|
||||||
<Button secondary wide on:click={testAutomation}>Test Automation</Button>
|
<Button secondary wide on:click={testAutomation}>Test Automation</Button>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
<Button
|
||||||
<div class="buttons">
|
secondary
|
||||||
{#if $automationStore.selectedBlock}
|
|
||||||
<Button
|
|
||||||
green
|
|
||||||
wide
|
wide
|
||||||
data-cy="save-automation-setup"
|
data-cy="save-automation-setup"
|
||||||
on:click={saveAutomation}>
|
on:click={saveAutomation}>
|
||||||
Save Automation
|
Save Automation
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
|
||||||
disabled={!allowDeleteBlock}
|
|
||||||
red
|
|
||||||
wide
|
|
||||||
on:click={deleteAutomationBlock}>
|
|
||||||
Delete Step
|
|
||||||
</Button>
|
|
||||||
{:else if $automationStore.selectedAutomation}
|
|
||||||
<Button
|
|
||||||
green
|
|
||||||
wide
|
|
||||||
data-cy="save-automation-setup"
|
|
||||||
on:click={saveAutomation}>
|
|
||||||
Save Automation
|
|
||||||
</Button>
|
|
||||||
<Button red wide on:click={() => confirmDeleteDialog.show()}>
|
|
||||||
Delete Automation
|
|
||||||
</Button>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<ConfirmDialog
|
|
||||||
bind:this={confirmDeleteDialog}
|
|
||||||
title="Confirm Delete"
|
|
||||||
body={`Are you sure you wish to delete the automation '${name}'?`}
|
|
||||||
okText="Delete Automation"
|
|
||||||
onOk={deleteAutomation} />
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
section {
|
.title {
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100%;
|
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: stretch;
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 600;
|
|
||||||
font-family: inter, sans-serif;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: var(--spacing-xl);
|
gap: var(--spacing-xs);
|
||||||
color: var(--ink);
|
|
||||||
}
|
}
|
||||||
header > span {
|
.title h1 {
|
||||||
|
font-size: var(--font-size-m);
|
||||||
|
font-weight: 500;
|
||||||
|
margin: 0;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
.title i {
|
||||||
|
font-size: 20px;
|
||||||
color: var(--grey-5);
|
color: var(--grey-5);
|
||||||
margin-right: var(--spacing-xl);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
.selected {
|
.title i.highlighted {
|
||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
}
|
}
|
||||||
|
.title i.hoverable:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--blue);
|
||||||
|
}
|
||||||
|
|
||||||
.block-label {
|
.block-label {
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 14px;
|
|
||||||
color: var(--grey-7);
|
color: var(--grey-7);
|
||||||
margin-bottom: var(--spacing-xl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.footer {
|
||||||
flex: 1 0 auto;
|
flex: 1 1 auto;
|
||||||
}
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
.buttons {
|
justify-content: flex-end;
|
||||||
display: grid;
|
align-items: stretch;
|
||||||
gap: var(--spacing-m);
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="block-field">
|
<div class="block-field">
|
||||||
<Select bind:value secondary thin>
|
<Select bind:value secondary extraThin>
|
||||||
<option value="">Choose an option</option>
|
<option value="">Choose an option</option>
|
||||||
{#each $backendUiStore.tables as table}
|
{#each $backendUiStore.tables as table}
|
||||||
<option value={table._id}>{table.name}</option>
|
<option value={table._id}>{table.name}</option>
|
|
@ -1 +0,0 @@
|
||||||
export { default as SetupPanel } from "./SetupPanel.svelte"
|
|
|
@ -43,7 +43,7 @@
|
||||||
width: 24px;
|
width: 24px;
|
||||||
background: var(--grey-4);
|
background: var(--grey-4);
|
||||||
right: var(--spacing-s);
|
right: var(--spacing-s);
|
||||||
bottom: 9px;
|
bottom: 5px;
|
||||||
}
|
}
|
||||||
button:hover {
|
button:hover {
|
||||||
background: var(--grey-5);
|
background: var(--grey-5);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { automationStore } from "builderStore"
|
import { automationStore } from "builderStore"
|
||||||
import { AutomationPanel, SetupPanel } from "components/automation"
|
import AutomationPanel from "components/automation/AutomationPanel/AutomationPanel.svelte"
|
||||||
|
import SetupPanel from "components/automation/SetupPanel/SetupPanel.svelte"
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- routify:options index=3 -->
|
<!-- routify:options index=3 -->
|
||||||
|
@ -19,21 +20,32 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.content {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.root {
|
.root {
|
||||||
height: calc(100% - 60px);
|
height: calc(100% - 60px);
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 300px minmax(510px, 1fr) 300px;
|
grid-template-columns: 260px minmax(510px, 1fr) 260px;
|
||||||
background: var(--grey-1);
|
background: var(--grey-2);
|
||||||
line-height: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav {
|
.nav {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
background: var(--white);
|
background: var(--white);
|
||||||
padding: var(--spacing-xl) var(--spacing-xl);
|
padding: var(--spacing-l) var(--spacing-xl);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: var(--spacing-l);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
position: relative;
|
||||||
|
padding: var(--spacing-l) 40px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: var(--spacing-l);
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { AutomationBuilder } from "components/automation"
|
import AutomationBuilder from "components/automation/AutomationBuilder/AutomationBuilder.svelte"
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AutomationBuilder />
|
<AutomationBuilder />
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
i {
|
i {
|
||||||
font-size: var(--font-size-xl);
|
font-size: var(--font-size-m);
|
||||||
color: var(--grey-4);
|
color: var(--grey-5);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
i {
|
i {
|
||||||
font-size: var(--font-size-xl);
|
font-size: var(--font-size-m);
|
||||||
color: var(--grey-4);
|
color: var(--grey-5);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
i {
|
i {
|
||||||
font-size: var(--font-size-xl);
|
font-size: var(--font-size-m);
|
||||||
color: var(--grey-4);
|
color: var(--grey-4);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue