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

This commit is contained in:
mike12345567 2022-05-12 18:14:38 +01:00
parent 3f6e8bf5ea
commit f86f6c3b1c
9 changed files with 314 additions and 1043 deletions

View File

@ -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>

View File

@ -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(),

View File

@ -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>

View File

@ -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>

View File

@ -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>
{#if history}
<div class="body"> <div class="body">
<Layout>
<div class="top"> <div class="top">
<div class="controls">
<Icon name="Clock" /> <Icon name="Clock" />
<Body>Run log details</Body> <Body>Run log details</Body>
<ActionButton size="S" icon="Close" quiet on:click={close} /> <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> </div>
</Layout> </Layout>
<div class="bottom">
{#each history.steps as step}
<FlowItemHeader useResultsModal={false} block={step} />
{/each}
</div> </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>

View File

@ -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>

View File

@ -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,6 +121,7 @@
<Input placeholder="Search" /> <Input placeholder="Search" />
</div> </div>
</div> </div>
{#if runHistory}
<Table <Table
on:click={viewDetails} on:click={viewDetails}
schema={runHistorySchema} schema={runHistorySchema}
@ -74,6 +131,7 @@
data={runHistory} data={runHistory}
{customRenderers} {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 {

File diff suppressed because it is too large Load Diff

View File

@ -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"