Browse Source

Shifting the title of the flow item header into separate component so that it can be used by the run history flow as well.

master
mike12345567 4 years ago
parent
commit
a7631bbad9
  1. 6
      packages/bbui/src/ActionButton/ActionButton.svelte
  2. 11
      packages/builder/src/builderStore/store/automation/index.js
  3. 73
      packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte
  4. 110
      packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte
  5. 66
      packages/builder/src/components/portal/history/HistoryDetailsPanel.svelte
  6. 25
      packages/builder/src/components/portal/history/StatusRenderer.svelte
  7. 116
      packages/builder/src/pages/builder/portal/history/index.svelte
  8. 949
      packages/builder/yarn.lock
  9. 1
      packages/server/src/db/linkedRows/index.js

6
packages/bbui/src/ActionButton/ActionButton.svelte

@ -13,6 +13,7 @@
export let size = "M" export let size = "M"
export let active = false export let active = false
export let fullWidth = false export let fullWidth = false
export let noPadding = false
function longPress(element) { function longPress(element) {
if (!longPressable) return if (!longPressable) return
@ -41,6 +42,7 @@
class:spectrum-ActionButton--quiet={quiet} class:spectrum-ActionButton--quiet={quiet}
class:spectrum-ActionButton--emphasized={emphasized} class:spectrum-ActionButton--emphasized={emphasized}
class:is-selected={selected} class:is-selected={selected}
class:noPadding
class:fullWidth class:fullWidth
class="spectrum-ActionButton spectrum-ActionButton--size{size}" class="spectrum-ActionButton spectrum-ActionButton--size{size}"
class:active class:active
@ -80,4 +82,8 @@
.active svg { .active svg {
color: var(--spectrum-global-color-blue-600); color: var(--spectrum-global-color-blue-600);
} }
.noPadding {
padding: 0;
min-width: 0;
}
</style> </style>

11
packages/builder/src/builderStore/store/automation/index.js

@ -20,6 +20,17 @@ export const getAutomationStore = () => {
} }
const automationActions = store => ({ const automationActions = store => ({
definitions: async () => {
const response = await API.getAutomationDefinitions()
store.update(state => {
state.blockDefinitions = {
TRIGGER: response.trigger,
ACTION: response.action,
}
return state
})
return response
},
fetch: async () => { fetch: async () => {
const responses = await Promise.all([ const responses = await Promise.all([
API.getAutomations(), API.getAutomations(),

73
packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte

@ -4,27 +4,23 @@
Icon, Icon,
Divider, Divider,
Layout, Layout,
Body,
Detail, Detail,
Modal, Modal,
Button, Button,
StatusLight,
Select, Select,
ActionButton, ActionButton,
notifications, notifications,
} from "@budibase/bbui" } from "@budibase/bbui"
import AutomationBlockSetup from "../../SetupPanel/AutomationBlockSetup.svelte" import AutomationBlockSetup from "../../SetupPanel/AutomationBlockSetup.svelte"
import CreateWebhookModal from "components/automation/Shared/CreateWebhookModal.svelte" import CreateWebhookModal from "components/automation/Shared/CreateWebhookModal.svelte"
import ResultsModal from "./ResultsModal.svelte"
import ActionModal from "./ActionModal.svelte" import ActionModal from "./ActionModal.svelte"
import { externalActions } from "./ExternalActions" import FlowItemHeader from "./FlowItemHeader.svelte"
export let block export let block
export let testDataModal export let testDataModal
let selected let selected
let webhookModal let webhookModal
let actionModal let actionModal
let resultsModal
let blockComplete let blockComplete
let showLooping = false let showLooping = false
@ -182,63 +178,12 @@
{/if} {/if}
{/if} {/if}
<div class="blockSection"> <FlowItemHeader
<div {onSelect}
on:click={() => { {block}
blockComplete = !blockComplete bind:results={testResult}
}} bind:open={blockComplete}
class="splitHeader" />
>
<div class="center-items">
{#if externalActions[block.stepId]}
<img
alt={externalActions[block.stepId].name}
width="28px"
height="28px"
src={externalActions[block.stepId].icon}
/>
{:else}
<svg
width="28px"
height="28px"
class="spectrum-Icon"
style="color:grey;"
focusable="false"
>
<use xlink:href="#spectrum-icon-18-{block.icon}" />
</svg>
{/if}
<div class="iconAlign">
{#if isTrigger}
<Body size="XS">When this happens:</Body>
{:else}
<Body size="XS">Do this:</Body>
{/if}
<Detail size="S">{block?.name?.toUpperCase() || ""}</Detail>
</div>
</div>
<div class="blockTitle">
{#if testResult && testResult[0]}
<div style="float: right;" on:click={() => resultsModal.show()}>
<StatusLight
positive={isTrigger || testResult[0].outputs?.success}
negative={!testResult[0].outputs?.success}
><Body size="XS">View response</Body></StatusLight
>
</div>
{/if}
<div
style="margin-left: 10px;"
on:click={() => {
onSelect(block)
}}
>
<Icon name={blockComplete ? "ChevronDown" : "ChevronUp"} />
</div>
</div>
</div>
</div>
{#if !blockComplete} {#if !blockComplete}
<Divider noMargin /> <Divider noMargin />
<div class="blockSection"> <div class="blockSection">
@ -283,10 +228,6 @@
</div> </div>
{/if} {/if}
<Modal bind:this={resultsModal} width="30%">
<ResultsModal {isTrigger} {testResult} />
</Modal>
<Modal bind:this={actionModal} width="30%"> <Modal bind:this={actionModal} width="30%">
<ActionModal {blockIdx} bind:blockComplete /> <ActionModal {blockIdx} bind:blockComplete />
</Modal> </Modal>

110
packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte

@ -0,0 +1,110 @@
<script>
import { Detail, Icon, Body, StatusLight, Modal } from "@budibase/bbui"
import { externalActions } from "./ExternalActions"
import ResultsModal from "./ResultsModal.svelte"
export let onSelect
export let block
export let results
export let useResultsModal = true
export let open
$: isTrigger = block?.type === "TRIGGER"
let resultsModal
</script>
<div class="blockSection">
<div
on:click={() => {
open = !open
}}
class="splitHeader"
>
<div class="center-items">
{#if externalActions[block.stepId]}
<img
alt={externalActions[block.stepId].name}
width="28px"
height="28px"
src={externalActions[block.stepId].icon}
/>
{:else}
<svg
width="28px"
height="28px"
class="spectrum-Icon"
style="color:grey;"
focusable="false"
>
<use xlink:href="#spectrum-icon-18-{block.icon}" />
</svg>
{/if}
<div class="iconAlign">
{#if isTrigger}
<Body size="XS">When this happens:</Body>
{:else}
<Body size="XS">Do this:</Body>
{/if}
<Detail size="S">{block?.name?.toUpperCase() || ""}</Detail>
</div>
</div>
<div class="blockTitle">
{#if useResultsModal}
{#if results && results[0]}
<div style="float: right;" on:click={() => resultsModal.show()}>
<StatusLight
positive={isTrigger || results[0].outputs?.success}
negative={!results[0].outputs?.success}
><Body size="XS">View response</Body></StatusLight
>
</div>
{/if}
{:else}
<slot />
{/if}
<div
style="margin-left: 10px;"
on:click={() => {
onSelect(block)
}}
>
<Icon name={open ? "ChevronDown" : "ChevronUp"} />
</div>
</div>
</div>
</div>
{#if useResultsModal}
<Modal bind:this={resultsModal} width="30%">
<ResultsModal {isTrigger} bind:testResult={results} />
</Modal>
{/if}
<style>
.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;
}
.blockSection {
padding: var(--spacing-xl);
}
.blockTitle {
display: flex;
align-items: center;
}
</style>

66
packages/builder/src/components/portal/history/HistoryDetailsPanel.svelte

@ -1,19 +1,51 @@
<script> <script>
import { Body, Layout, Icon, ActionButton } from "@budibase/bbui" import { Body, Layout, Icon, ActionButton, Heading } from "@budibase/bbui"
import { capitalise } from "helpers"
import StatusRenderer from "components/portal/history/StatusRenderer.svelte"
import DateTimeRenderer from "components/common/renderers/DateTimeRenderer.svelte"
import FlowItemHeader from "components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte"
export let history export let history
export let close export let close
$: console.log(history)
</script> </script>
<div class="body"> {#if history}
<Layout> <div class="body">
<div class="top"> <div class="top">
<Icon name="Clock" /> <div class="controls">
<Body>Run log details</Body> <Icon name="Clock" />
<ActionButton size="S" icon="Close" quiet on:click={close} /> <Body>Run log details</Body>
<ActionButton noPadding size="S" icon="Close" quiet on:click={close} />
</div>
</div>
<Layout paddingX="XL" gap="S">
<Heading>{capitalise(history.appName || "")}</Heading>
<StatusRenderer value={history.status} />
<div class="icon">
<Icon name="Clock" />
<DateTimeRenderer value={history.timestamp} />
</div>
<div class="icon">
<Icon name="JourneyVoyager" />
<Body>{history.name}</Body>
</div>
<div>
<ActionButton icon="Edit" fullWidth={false}
>Edit automation</ActionButton
>
</div>
</Layout>
<div class="bottom">
{#each history.steps as step}
<FlowItemHeader useResultsModal={false} block={step} />
{/each}
</div> </div>
</Layout> </div>
</div> {:else}
<Body>No details found</Body>
{/if}
<style> <style>
.body { .body {
@ -24,10 +56,24 @@
} }
.top { .top {
padding: var(--spacing-l) 0 var(--spacing-l) 0;
border-bottom: var(--border-light);
}
.bottom {
margin-top: var(--spacing-m);
border-top: var(--border-light);
}
.icon {
display: flex;
gap: var(--spacing-m);
}
.controls {
padding: 0 var(--spacing-l) 0 var(--spacing-l);
display: grid; display: grid;
grid-template-columns: auto 1fr auto; grid-template-columns: auto 1fr auto;
gap: var(--spacing-s); gap: var(--spacing-s);
padding: var(--spacing-s) 0 var(--spacing-l) var(--spacing-s);
border-bottom: var(--border-light);
} }
</style> </style>

25
packages/builder/src/components/portal/history/StatusRenderer.svelte

@ -0,0 +1,25 @@
<script>
import { Icon, Body } from "@budibase/bbui"
export let value
$: isError = value === "Error"
</script>
<div class="cell">
<Icon
color={isError
? "var(--spectrum-semantic-negative-color-background)"
: "var(--green)"}
name={isError ? "Alert" : "CheckmarkCircle"}
/>
<Body>{value}</Body>
</div>
<style>
.cell {
color: var(--spectrum-semantic-negative-color-background);
display: flex;
flex-direction: row;
gap: var(--spacing-m);
}
</style>

116
packages/builder/src/pages/builder/portal/history/index.svelte

@ -9,39 +9,95 @@
Input, Input,
} from "@budibase/bbui" } from "@budibase/bbui"
import DateTimeRenderer from "components/common/renderers/DateTimeRenderer.svelte" import DateTimeRenderer from "components/common/renderers/DateTimeRenderer.svelte"
import StatusRenderer from "components/portal/history/StatusRenderer.svelte"
import HistoryDetailsPanel from "components/portal/history/HistoryDetailsPanel.svelte" import HistoryDetailsPanel from "components/portal/history/HistoryDetailsPanel.svelte"
import { automationStore } from "builderStore"
import { onMount } from "svelte"
let showPanel = false let showPanel = false
let selectedHistory = null let selectedHistory = null
let runHistory = []
const runHistorySchema = { const runHistorySchema = {
status: "", status: { displayName: "Status" },
time: "", timestamp: { displayName: "Time" },
app: "", appName: { displayName: "App" },
automation: "", name: { displayName: "Automation" },
} }
const customRenderers = [{ column: "time", component: DateTimeRenderer }] const customRenderers = [
{ column: "time", component: DateTimeRenderer },
let runHistory = [ { column: "status", component: StatusRenderer },
{
status: "Error",
time: "2022-05-11T16:06:14.438Z",
app: "App name",
automation: "automation name",
},
{
status: "Success",
time: "2022-05-11T16:03:14.438Z",
app: "App name",
automation: "automation name",
},
] ]
function enrichHistory(definitions, runHistory) {
const finalHistory = []
for (let history of runHistory) {
if (!history.steps) {
continue
}
let notFound = false
for (let step of history.steps) {
const trigger = definitions.trigger[step.stepId],
action = definitions.action[step.stepId]
if (!trigger && !action) {
notFound = true
break
}
step.icon = trigger ? trigger.icon : action.icon
step.name = trigger ? trigger.name : action.name
}
if (!notFound) {
finalHistory.push(history)
}
}
return finalHistory
}
function viewDetails({ detail }) { function viewDetails({ detail }) {
selectedHistory = detail selectedHistory = detail
showPanel = true showPanel = true
} }
onMount(async () => {
let definitions = await automationStore.actions.definitions()
runHistory = enrichHistory(definitions, [
{
status: "Error",
timestamp: "2022-05-11T16:06:14.438Z",
appName: "App name",
name: "automation name",
steps: [
{
stepId: "ROW_SAVED",
outputs: {},
},
{
stepId: "SEND_EMAIL_SMTP",
inputs: {},
outputs: {},
},
],
},
{
status: "Success",
timestamp: "2022-05-11T16:03:14.438Z",
appName: "App name",
name: "automation name",
steps: [
{
stepId: "ROW_SAVED",
outputs: {},
},
{
stepId: "SEND_EMAIL_SMTP",
inputs: {},
outputs: {},
},
],
},
])
})
</script> </script>
<div class="root" class:panelOpen={showPanel}> <div class="root" class:panelOpen={showPanel}>
@ -65,15 +121,17 @@
<Input placeholder="Search" /> <Input placeholder="Search" />
</div> </div>
</div> </div>
<Table {#if runHistory}
on:click={viewDetails} <Table
schema={runHistorySchema} on:click={viewDetails}
allowSelectRows={false} schema={runHistorySchema}
allowEditColumns={false} allowSelectRows={false}
allowEditRows={false} allowEditColumns={false}
data={runHistory} allowEditRows={false}
{customRenderers} data={runHistory}
/> {customRenderers}
/>
{/if}
</Layout> </Layout>
</Page> </Page>
<div class="panel" class:panelShow={showPanel}> <div class="panel" class:panelShow={showPanel}>
@ -94,7 +152,7 @@
} }
.panelOpen { .panelOpen {
grid-template-columns: 1fr 390px; grid-template-columns: 1fr 360px;
} }
.search { .search {

949
packages/builder/yarn.lock

File diff suppressed because it is too large

1
packages/server/src/db/linkedRows/index.js

@ -65,6 +65,7 @@ async function getLinksForRows(rows) {
// return duplicates, could be querying for both tables in a relation // return duplicates, could be querying for both tables in a relation
return getUniqueByProp( return getUniqueByProp(
responses responses
.filter(el => el != null)
// create a unique ID which we can use for getting only unique ones // create a unique ID which we can use for getting only unique ones
.map(el => ({ ...el, unique: el.id + el.thisId + el.fieldName })), .map(el => ({ ...el, unique: el.id + el.thisId + el.fieldName })),
"unique" "unique"

Loading…
Cancel
Save