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:
parent
3f6e8bf5ea
commit
f86f6c3b1c
|
@ -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>
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
</div>
|
||||||
</Layout>
|
<Layout paddingX="XL" gap="S">
|
||||||
</div>
|
<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>
|
||||||
|
{: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>
|
||||||
|
|
|
@ -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>
|
|
@ -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 {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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…
Reference in New Issue