Adding in main portal page automation error notification.
This commit is contained in:
parent
e9d3a1e92f
commit
2ff853c917
|
@ -1,15 +1,20 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { ActionButton } from "../"
|
||||||
|
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
|
|
||||||
export let type = "info"
|
export let type = "info"
|
||||||
export let icon = "Info"
|
export let icon = "Info"
|
||||||
export let message = ""
|
export let message = ""
|
||||||
export let dismissable = false
|
export let dismissable = false
|
||||||
|
export let actionMessage = null
|
||||||
|
export let action = null
|
||||||
|
export let wide = false
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="spectrum-Toast spectrum-Toast--{type}">
|
<div class="spectrum-Toast spectrum-Toast--{type}" class:wide>
|
||||||
{#if icon}
|
{#if icon}
|
||||||
<svg
|
<svg
|
||||||
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Toast-typeIcon"
|
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Toast-typeIcon"
|
||||||
|
@ -19,8 +24,13 @@
|
||||||
<use xlink:href="#spectrum-icon-18-{icon}" />
|
<use xlink:href="#spectrum-icon-18-{icon}" />
|
||||||
</svg>
|
</svg>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="spectrum-Toast-body">
|
<div class="spectrum-Toast-body" class:actionBody={!!action}>
|
||||||
<div class="spectrum-Toast-content">{message || ""}</div>
|
<div class="spectrum-Toast-content">{message || ""}</div>
|
||||||
|
{#if action}
|
||||||
|
<ActionButton quiet emphasized on:click={action}>
|
||||||
|
<div style="color: white; font-weight: 600;">{actionMessage}</div>
|
||||||
|
</ActionButton>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{#if dismissable}
|
{#if dismissable}
|
||||||
<div class="spectrum-Toast-buttons">
|
<div class="spectrum-Toast-buttons">
|
||||||
|
@ -46,4 +56,15 @@
|
||||||
.spectrum-Toast {
|
.spectrum-Toast {
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wide {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actionBody {
|
||||||
|
justify-content: space-between;
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -8,13 +8,15 @@
|
||||||
|
|
||||||
<Portal target=".modal-container">
|
<Portal target=".modal-container">
|
||||||
<div class="notifications">
|
<div class="notifications">
|
||||||
{#each $notifications as { type, icon, message, id, dismissable } (id)}
|
{#each $notifications as { type, icon, message, id, dismissable, action, wide } (id)}
|
||||||
<div transition:fly={{ y: -30 }}>
|
<div transition:fly={{ y: -30 }}>
|
||||||
<Notification
|
<Notification
|
||||||
{type}
|
{type}
|
||||||
{icon}
|
{icon}
|
||||||
{message}
|
{message}
|
||||||
{dismissable}
|
{dismissable}
|
||||||
|
{action}
|
||||||
|
{wide}
|
||||||
on:dismiss={() => notifications.dismiss(id)}
|
on:dismiss={() => notifications.dismiss(id)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -20,7 +20,16 @@ export const createNotificationStore = () => {
|
||||||
setTimeout(() => (block = false), timeout)
|
setTimeout(() => (block = false), timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
const send = (message, type = "default", icon = "", autoDismiss = true) => {
|
const send = (
|
||||||
|
message,
|
||||||
|
{
|
||||||
|
type = "default",
|
||||||
|
icon = "",
|
||||||
|
autoDismiss = true,
|
||||||
|
action = null,
|
||||||
|
wide = false,
|
||||||
|
}
|
||||||
|
) => {
|
||||||
if (block) {
|
if (block) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -28,7 +37,15 @@ export const createNotificationStore = () => {
|
||||||
_notifications.update(state => {
|
_notifications.update(state => {
|
||||||
return [
|
return [
|
||||||
...state,
|
...state,
|
||||||
{ id: _id, type, message, icon, dismissable: !autoDismiss },
|
{
|
||||||
|
id: _id,
|
||||||
|
type,
|
||||||
|
message,
|
||||||
|
icon,
|
||||||
|
dismissable: !autoDismiss,
|
||||||
|
action,
|
||||||
|
wide,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
if (autoDismiss) {
|
if (autoDismiss) {
|
||||||
|
@ -50,10 +67,11 @@ export const createNotificationStore = () => {
|
||||||
return {
|
return {
|
||||||
subscribe,
|
subscribe,
|
||||||
send,
|
send,
|
||||||
info: msg => send(msg, "info", "Info"),
|
info: msg => send(msg, { type: "info", icon: "Info" }),
|
||||||
error: msg => send(msg, "error", "Alert", false),
|
error: msg =>
|
||||||
warning: msg => send(msg, "warning", "Alert"),
|
send(msg, { type: "error", icon: "Alert", autoDismiss: false }),
|
||||||
success: msg => send(msg, "success", "CheckmarkCircle"),
|
warning: msg => send(msg, { type: "warning", icon: "Alert" }),
|
||||||
|
success: msg => send(msg, { type: "success", icon: "CheckmarkCircle" }),
|
||||||
blockNotifications,
|
blockNotifications,
|
||||||
dismiss: dismissNotification,
|
dismiss: dismissNotification,
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,6 +129,7 @@ const automationActions = store => ({
|
||||||
page,
|
page,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
clearLogErrors: async () => {},
|
||||||
addTestDataToAutomation: data => {
|
addTestDataToAutomation: data => {
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
state.selectedAutomation.addTestData(data)
|
state.selectedAutomation.addTestData(data)
|
||||||
|
|
|
@ -4,10 +4,15 @@
|
||||||
import DateTimeRenderer from "components/common/renderers/DateTimeRenderer.svelte"
|
import DateTimeRenderer from "components/common/renderers/DateTimeRenderer.svelte"
|
||||||
import TestDisplay from "components/automation/AutomationBuilder/TestDisplay.svelte"
|
import TestDisplay from "components/automation/AutomationBuilder/TestDisplay.svelte"
|
||||||
import { goto } from "@roxi/routify"
|
import { goto } from "@roxi/routify"
|
||||||
|
import { automationStore } from "builderStore"
|
||||||
|
|
||||||
export let history
|
export let history
|
||||||
export let appId
|
export let appId
|
||||||
export let close
|
export let close
|
||||||
|
|
||||||
|
$: exists = $automationStore.automations?.find(
|
||||||
|
auto => auto._id === history?.automationId
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if history}
|
{#if history}
|
||||||
|
@ -28,13 +33,15 @@
|
||||||
<div>{history.automationName}</div>
|
<div>{history.automationName}</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<ActionButton
|
{#if exists}
|
||||||
icon="Edit"
|
<ActionButton
|
||||||
fullWidth={false}
|
icon="Edit"
|
||||||
on:click={() =>
|
fullWidth={false}
|
||||||
$goto(`../../../app/${appId}/automate/${history.automationId}`)}
|
on:click={() =>
|
||||||
>Edit automation</ActionButton
|
$goto(`../../../app/${appId}/automate/${history.automationId}`)}
|
||||||
>
|
>Edit automation</ActionButton
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
<div class="bottom">
|
<div class="bottom">
|
||||||
|
|
|
@ -126,7 +126,7 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if runHistory}
|
{#if runHistory && runHistory.length}
|
||||||
<Table
|
<Table
|
||||||
on:click={viewDetails}
|
on:click={viewDetails}
|
||||||
schema={runHistorySchema}
|
schema={runHistorySchema}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
Modal,
|
Modal,
|
||||||
Page,
|
Page,
|
||||||
notifications,
|
notifications,
|
||||||
|
Notification,
|
||||||
Body,
|
Body,
|
||||||
Search,
|
Search,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
|
@ -37,6 +38,7 @@
|
||||||
let searchTerm = ""
|
let searchTerm = ""
|
||||||
let cloud = $admin.cloud
|
let cloud = $admin.cloud
|
||||||
let creatingFromTemplate = false
|
let creatingFromTemplate = false
|
||||||
|
let automationErrors
|
||||||
|
|
||||||
const resolveWelcomeMessage = (auth, apps) => {
|
const resolveWelcomeMessage = (auth, apps) => {
|
||||||
const userWelcome = auth?.user?.firstName
|
const userWelcome = auth?.user?.firstName
|
||||||
|
@ -59,7 +61,8 @@
|
||||||
)
|
)
|
||||||
|
|
||||||
$: lockedApps = filteredApps.filter(app => app?.lockedYou || app?.lockedOther)
|
$: lockedApps = filteredApps.filter(app => app?.lockedYou || app?.lockedOther)
|
||||||
$: unlocked = lockedApps?.length == 0
|
$: unlocked = lockedApps?.length === 0
|
||||||
|
$: automationErrors = getAutomationErrors(enrichedApps)
|
||||||
|
|
||||||
const enrichApps = (apps, user, sortBy) => {
|
const enrichApps = (apps, user, sortBy) => {
|
||||||
const enrichedApps = apps.map(app => ({
|
const enrichedApps = apps.map(app => ({
|
||||||
|
@ -89,6 +92,33 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getAutomationErrors = apps => {
|
||||||
|
const automationErrors = {}
|
||||||
|
for (let app of apps) {
|
||||||
|
if (app.automationErrors) {
|
||||||
|
automationErrors[app.devId] = app.automationErrors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return automationErrors
|
||||||
|
}
|
||||||
|
|
||||||
|
const goToAutomationError = appId => {
|
||||||
|
const params = new URLSearchParams({ tab: "Automation History" })
|
||||||
|
$goto(`../overview/${appId}?${params.toString()}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const errorCount = appId => {
|
||||||
|
return Object.values(automationErrors[appId]).reduce(
|
||||||
|
(prev, next) => prev + next,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const automationErrorMessage = appId => {
|
||||||
|
const app = enrichedApps.find(app => app.devId === appId)
|
||||||
|
return `${app.name} - Automation error (${errorCount(appId)})`
|
||||||
|
}
|
||||||
|
|
||||||
const initiateAppCreation = () => {
|
const initiateAppCreation = () => {
|
||||||
if ($apps?.length) {
|
if ($apps?.length) {
|
||||||
$goto("/builder/portal/apps/create")
|
$goto("/builder/portal/apps/create")
|
||||||
|
@ -208,6 +238,19 @@
|
||||||
<Page wide>
|
<Page wide>
|
||||||
<Layout noPadding gap="M">
|
<Layout noPadding gap="M">
|
||||||
{#if loaded}
|
{#if loaded}
|
||||||
|
{#if automationErrors}
|
||||||
|
{#each Object.keys(automationErrors) as appId}
|
||||||
|
<Notification
|
||||||
|
wide
|
||||||
|
dismissable
|
||||||
|
action={() => goToAutomationError(appId)}
|
||||||
|
type="error"
|
||||||
|
icon="Alert"
|
||||||
|
actionMessage={errorCount(appId) > 1 ? "View errors" : "View error"}
|
||||||
|
message={automationErrorMessage(appId)}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<div class="welcome">
|
<div class="welcome">
|
||||||
<Layout noPadding gap="XS">
|
<Layout noPadding gap="XS">
|
||||||
|
|
|
@ -188,6 +188,10 @@
|
||||||
})
|
})
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
|
const params = new URLSearchParams(window.location.search)
|
||||||
|
if (params.get("tab")) {
|
||||||
|
selectedTab = params.get("tab")
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if (!apps.length) {
|
if (!apps.length) {
|
||||||
await apps.load()
|
await apps.load()
|
||||||
|
|
Loading…
Reference in New Issue