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 active = false
|
||||
export let fullWidth = false
|
||||
export let noPadding = false
|
||||
|
||||
function longPress(element) {
|
||||
if (!longPressable) return
|
||||
|
@ -41,6 +42,7 @@
|
|||
class:spectrum-ActionButton--quiet={quiet}
|
||||
class:spectrum-ActionButton--emphasized={emphasized}
|
||||
class:is-selected={selected}
|
||||
class:noPadding
|
||||
class:fullWidth
|
||||
class="spectrum-ActionButton spectrum-ActionButton--size{size}"
|
||||
class:active
|
||||
|
@ -80,4 +82,8 @@
|
|||
.active svg {
|
||||
color: var(--spectrum-global-color-blue-600);
|
||||
}
|
||||
.noPadding {
|
||||
padding: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -20,6 +20,17 @@ export const getAutomationStore = () => {
|
|||
}
|
||||
|
||||
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 () => {
|
||||
const responses = await Promise.all([
|
||||
API.getAutomations(),
|
||||
|
|
|
@ -4,27 +4,23 @@
|
|||
Icon,
|
||||
Divider,
|
||||
Layout,
|
||||
Body,
|
||||
Detail,
|
||||
Modal,
|
||||
Button,
|
||||
StatusLight,
|
||||
Select,
|
||||
ActionButton,
|
||||
notifications,
|
||||
} from "@budibase/bbui"
|
||||
import AutomationBlockSetup from "../../SetupPanel/AutomationBlockSetup.svelte"
|
||||
import CreateWebhookModal from "components/automation/Shared/CreateWebhookModal.svelte"
|
||||
import ResultsModal from "./ResultsModal.svelte"
|
||||
import ActionModal from "./ActionModal.svelte"
|
||||
import { externalActions } from "./ExternalActions"
|
||||
import FlowItemHeader from "./FlowItemHeader.svelte"
|
||||
|
||||
export let block
|
||||
export let testDataModal
|
||||
let selected
|
||||
let webhookModal
|
||||
let actionModal
|
||||
let resultsModal
|
||||
let blockComplete
|
||||
let showLooping = false
|
||||
|
||||
|
@ -182,63 +178,12 @@
|
|||
{/if}
|
||||
{/if}
|
||||
|
||||
<div class="blockSection">
|
||||
<div
|
||||
on:click={() => {
|
||||
blockComplete = !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>
|
||||
<FlowItemHeader
|
||||
{onSelect}
|
||||
{block}
|
||||
bind:results={testResult}
|
||||
bind:open={blockComplete}
|
||||
/>
|
||||
{#if !blockComplete}
|
||||
<Divider noMargin />
|
||||
<div class="blockSection">
|
||||
|
@ -283,10 +228,6 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
<Modal bind:this={resultsModal} width="30%">
|
||||
<ResultsModal {isTrigger} {testResult} />
|
||||
</Modal>
|
||||
|
||||
<Modal bind:this={actionModal} width="30%">
|
||||
<ActionModal {blockIdx} bind:blockComplete />
|
||||
</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>
|
||||
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 close
|
||||
|
||||
$: console.log(history)
|
||||
</script>
|
||||
|
||||
<div class="body">
|
||||
<Layout>
|
||||
{#if history}
|
||||
<div class="body">
|
||||
<div class="top">
|
||||
<Icon name="Clock" />
|
||||
<Body>Run log details</Body>
|
||||
<ActionButton size="S" icon="Close" quiet on:click={close} />
|
||||
<div class="controls">
|
||||
<Icon name="Clock" />
|
||||
<Body>Run log details</Body>
|
||||
<ActionButton noPadding size="S" icon="Close" quiet on:click={close} />
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
</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>
|
||||
{:else}
|
||||
<Body>No details found</Body>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.body {
|
||||
|
@ -24,10 +56,24 @@
|
|||
}
|
||||
|
||||
.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;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
gap: var(--spacing-s);
|
||||
padding: var(--spacing-s) 0 var(--spacing-l) var(--spacing-s);
|
||||
border-bottom: var(--border-light);
|
||||
}
|
||||
</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,
|
||||
} from "@budibase/bbui"
|
||||
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 { automationStore } from "builderStore"
|
||||
import { onMount } from "svelte"
|
||||
|
||||
let showPanel = false
|
||||
let selectedHistory = null
|
||||
let runHistory = []
|
||||
|
||||
const runHistorySchema = {
|
||||
status: "",
|
||||
time: "",
|
||||
app: "",
|
||||
automation: "",
|
||||
status: { displayName: "Status" },
|
||||
timestamp: { displayName: "Time" },
|
||||
appName: { displayName: "App" },
|
||||
name: { displayName: "Automation" },
|
||||
}
|
||||
|
||||
const customRenderers = [{ column: "time", component: DateTimeRenderer }]
|
||||
|
||||
let runHistory = [
|
||||
{
|
||||
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",
|
||||
},
|
||||
const customRenderers = [
|
||||
{ column: "time", component: DateTimeRenderer },
|
||||
{ column: "status", component: StatusRenderer },
|
||||
]
|
||||
|
||||
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 }) {
|
||||
selectedHistory = detail
|
||||
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>
|
||||
|
||||
<div class="root" class:panelOpen={showPanel}>
|
||||
|
@ -65,15 +121,17 @@
|
|||
<Input placeholder="Search" />
|
||||
</div>
|
||||
</div>
|
||||
<Table
|
||||
on:click={viewDetails}
|
||||
schema={runHistorySchema}
|
||||
allowSelectRows={false}
|
||||
allowEditColumns={false}
|
||||
allowEditRows={false}
|
||||
data={runHistory}
|
||||
{customRenderers}
|
||||
/>
|
||||
{#if runHistory}
|
||||
<Table
|
||||
on:click={viewDetails}
|
||||
schema={runHistorySchema}
|
||||
allowSelectRows={false}
|
||||
allowEditColumns={false}
|
||||
allowEditRows={false}
|
||||
data={runHistory}
|
||||
{customRenderers}
|
||||
/>
|
||||
{/if}
|
||||
</Layout>
|
||||
</Page>
|
||||
<div class="panel" class:panelShow={showPanel}>
|
||||
|
@ -94,7 +152,7 @@
|
|||
}
|
||||
|
||||
.panelOpen {
|
||||
grid-template-columns: 1fr 390px;
|
||||
grid-template-columns: 1fr 360px;
|
||||
}
|
||||
|
||||
.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 getUniqueByProp(
|
||||
responses
|
||||
.filter(el => el != null)
|
||||
// create a unique ID which we can use for getting only unique ones
|
||||
.map(el => ({ ...el, unique: el.id + el.thisId + el.fieldName })),
|
||||
"unique"
|
||||
|
|
Loading…
Reference in New Issue