diff --git a/src/lib/components/activity/activity-commands.svelte b/src/lib/components/activity/activity-commands.svelte index e55044a081..d73c8ed945 100644 --- a/src/lib/components/activity/activity-commands.svelte +++ b/src/lib/components/activity/activity-commands.svelte @@ -57,7 +57,7 @@ > - + - + + {/snippet} + {#if childWorkflowStartedEvent} +
+
+ {#key group.eventList.length} + + {/key} +
+
+ {/if} + + {/key} + + +
+ + + +
+ diff --git a/src/lib/components/lines-and-dots/svg/group-details-row.svelte b/src/lib/components/lines-and-dots/svg/group-details-row.svelte index 5e769a0727..597300d85b 100644 --- a/src/lib/components/lines-and-dots/svg/group-details-row.svelte +++ b/src/lib/components/lines-and-dots/svg/group-details-row.svelte @@ -1,113 +1,79 @@ - - + +
-
-
- {#if status} - - {/if} - {title} - {#if duration} -
- - {duration} -
- {/if} -
-
+ + {#snippet headerActions()} -
-
-
- -
- {#if childWorkflowStartedEvent} -
-
Child Workflow
- {#key group.eventList.length} - - {/key} -
- {/if} + {/snippet} + {#if childWorkflowStartedEvent} +
+
+ {#key group.eventList.length} + + {/key} +
+
+ {/if} +
diff --git a/src/lib/components/lines-and-dots/svg/line.svelte b/src/lib/components/lines-and-dots/svg/line.svelte index e3ed1ea396..94945542fc 100644 --- a/src/lib/components/lines-and-dots/svg/line.svelte +++ b/src/lib/components/lines-and-dots/svg/line.svelte @@ -49,7 +49,7 @@ cursor: pointer; opacity: 1; outline: none; - stroke: #444ce7; + stroke: currentColor; } .none { @@ -88,16 +88,20 @@ } .child-workflow { - stroke: #67e4f9; + stroke: theme('colors.cyan.600'); } .Completed { stroke: #1ff1a5; } + .ContinuedAsNew { + stroke: #e2d6fe; + } + .Failed, .Terminated { - stroke: #f55; + stroke: #c71607; } .Signaled { diff --git a/src/lib/components/lines-and-dots/svg/text.svelte b/src/lib/components/lines-and-dots/svg/text.svelte index b673523937..5a8e77a6b3 100644 --- a/src/lib/components/lines-and-dots/svg/text.svelte +++ b/src/lib/components/lines-and-dots/svg/text.svelte @@ -101,7 +101,7 @@ } text.child-workflow { - fill: #67e4f9; + fill: theme('colors.cyan.600'); } text.workflow { @@ -109,7 +109,7 @@ } text.Failed { - fill: #f55; + fill: #ff4418; } text.none { diff --git a/src/lib/components/lines-and-dots/svg/timeline-axis.svelte b/src/lib/components/lines-and-dots/svg/timeline-axis.svelte index cb2112ab4a..f6d70fb40c 100644 --- a/src/lib/components/lines-and-dots/svg/timeline-axis.svelte +++ b/src/lib/components/lines-and-dots/svg/timeline-axis.svelte @@ -1,5 +1,6 @@ +{#if hovering} + +
+
+{/if} diff --git a/src/lib/components/lines-and-dots/svg/timeline-graph.svelte b/src/lib/components/lines-and-dots/svg/timeline-graph.svelte index f939a76024..b539ffe98b 100644 --- a/src/lib/components/lines-and-dots/svg/timeline-graph.svelte +++ b/src/lib/components/lines-and-dots/svg/timeline-graph.svelte @@ -1,10 +1,6 @@
-
+

{formatDate(startTime, $timeFormat, { format: 'short' })} @@ -91,17 +73,18 @@ height={canvasHeight} width={canvasWidth} class="-mt-4" - class:error > {#each filteredGroups as group, index (group.id)} - {@const y = (index + 2) * height + activeGroupsHeightAboveGroup(group)} + {@const y = (index + 2) * height} {#if !viewportHeight || (y > scrollY - 2 * height && y < scrollY + viewportHeight * height)} {#key group.eventList.length} {/key} {/if} - {#if !readOnly && $activeGroups.includes(group.id)} - - {/if} {/each} -

- + {#if !readOnly && $activeGroup} + setActiveGroup($activeGroup)} + /> + {/if} +
diff --git a/src/lib/components/lines-and-dots/workflow-error.svelte b/src/lib/components/lines-and-dots/workflow-error.svelte index 2c6dcec7f7..8001b29dce 100644 --- a/src/lib/components/lines-and-dots/workflow-error.svelte +++ b/src/lib/components/lines-and-dots/workflow-error.svelte @@ -1,8 +1,5 @@ {#if cause && cause !== 'ResetWorkflow'}

@@ -55,37 +41,8 @@ >.

{/if} -
-
-
- {error.id} - - {spaceBetweenCapitalLetters(error?.name)} -
- -
-
- {#if timeoutType} -

- Timeout Type - {timeoutType} -

- {/if} - {#if failure || pendingTask} - - {#if failure} - - {/if} - {#if pendingTask} - - {/if} - - {/if} -
+
+
{/if} diff --git a/src/lib/components/workflow-actions.svelte b/src/lib/components/workflow-actions.svelte index 023b47e38b..a41b224f9a 100644 --- a/src/lib/components/workflow-actions.svelte +++ b/src/lib/components/workflow-actions.svelte @@ -8,6 +8,7 @@ import TerminateConfirmationModal from '$lib/components/workflow/client-actions/terminate-confirmation-modal.svelte'; import UpdateConfirmationModal from '$lib/components/workflow/client-actions/update-confirmation-modal.svelte'; import Button from '$lib/holocene/button.svelte'; + import Icon from '$lib/holocene/icon/icon.svelte'; import { MenuDivider, MenuItem } from '$lib/holocene/menu'; import MenuButton from '$lib/holocene/menu/menu-button.svelte'; import MenuContainer from '$lib/holocene/menu/menu-container.svelte'; @@ -28,6 +29,8 @@ import { workflowTerminateEnabled } from '$lib/utilities/workflow-terminate-enabled'; import { workflowUpdateEnabled } from '$lib/utilities/workflow-update-enabled'; + import DownloadEventHistoryModal from './workflow/download-event-history-modal.svelte'; + interface Props { workflow: WorkflowExecution; namespace: string; @@ -45,6 +48,7 @@ let resetConfirmationModalOpen = $state(false); let signalConfirmationModalOpen = $state(false); let updateConfirmationModalOpen = $state(false); + let showDownloadPrompt = $state(false); let coreUser = coreUserStore(); @@ -193,21 +197,12 @@ {/each} {#if !workflowCreateDisabled($page)} - - goto( - routeForWorkflowStart({ - namespace, - workflowId: workflow.id, - runId: workflow.runId, - taskQueue: workflow.taskQueue, - workflowType: workflow.name, - }), - )} - data-testid="start-workflow-button" - > - {translate('workflows.start-workflow-like-this-one')} - + {@render startWorkflowMenuItem()} + + {@render downloadMenuItem()} + {:else} + + {@render downloadMenuItem()} {/if} @@ -229,22 +224,7 @@ {translate('workflows.more-actions')} - - goto( - routeForWorkflowStart({ - namespace, - workflowId: workflow.id, - runId: workflow.runId, - taskQueue: workflow.taskQueue, - workflowType: workflow.name, - }), - )} - disabled={workflowCreateDisabled($page)} - data-testid="start-workflow-button" - > - {translate('workflows.start-workflow-like-this-one')} - + {@render startWorkflowMenuItem()} {#if terminateEnabled && next} {/if} + + + {@render downloadMenuItem()} {/if} @@ -304,3 +287,41 @@ bind:open={terminateConfirmationModalOpen} /> {/if} + + + +{#snippet startWorkflowMenuItem()} + + goto( + routeForWorkflowStart({ + namespace, + workflowId: workflow.id, + runId: workflow.runId, + taskQueue: workflow.taskQueue, + workflowType: workflow.name, + }), + )} + disabled={workflowCreateDisabled($page)} + data-testid="start-workflow-button" + > + + {translate('workflows.start-workflow-like-this-one')} + + + +{/snippet} + +{#snippet downloadMenuItem()} + (showDownloadPrompt = true)} data-testid="download"> + + {translate('workflows.download-history')} + + + +{/snippet} diff --git a/src/lib/components/workflow-status.svelte b/src/lib/components/workflow-status.svelte index 83c171e1e8..9179199f82 100644 --- a/src/lib/components/workflow-status.svelte +++ b/src/lib/components/workflow-status.svelte @@ -26,7 +26,6 @@ export let count: number | undefined = undefined; export let loading = false; export let newCount: number | undefined = undefined; - export let big = false; export let delayed = false; export let taskFailure = false; @@ -64,7 +63,7 @@ Completed: 'bg-green-200', Failed: 'bg-red-200', ContinuedAsNew: 'bg-purple-200', - Canceled: 'bg-slate-100', + Canceled: 'bg-yellow-200', Terminated: 'bg-yellow-200', Paused: 'bg-yellow-200', Unspecified: 'bg-slate-100', @@ -73,7 +72,7 @@ Open: 'bg-green-200', New: 'bg-blue-300', Initiated: 'bg-blue-300', - Fired: 'bg-pink-200', + Fired: 'bg-orange-200', CancelRequested: 'bg-yellow-200', Signaled: 'bg-pink-200', Pending: 'bg-purple-200', @@ -97,10 +96,7 @@ class="block" >
{#if loading} @@ -131,10 +127,11 @@ }), 'rounded-l-none', (newCount || taskFailure) && 'rounded-r-none', - big && 'h-8 px-2', + '[&:not(.text-lg)_svg]:px-0.5', + $$props.class || '', )} > - + {/if} {#if taskFailure} @@ -144,21 +141,16 @@ 'bg-red-200 text-red-900 dark:bg-red-700 dark:text-white', 'rounded-l-none', newCount && 'rounded-r-none', - big && 'h-8 px-2', + '[&:not(.text-lg)_svg]:px-0.5', + $$props.class || '', )} > - + {/if} {#if newCount} - + {#if newCount > 0}+{/if}{newCount} {/if} diff --git a/src/lib/components/workflow/event-history/event-history-card.svelte b/src/lib/components/workflow/event-history/event-history-card.svelte new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/lib/components/workflow/input-and-results-payload.svelte b/src/lib/components/workflow/input-and-results-payload.svelte index 103cccd83d..9459b4769f 100644 --- a/src/lib/components/workflow/input-and-results-payload.svelte +++ b/src/lib/components/workflow/input-and-results-payload.svelte @@ -1,7 +1,7 @@
-

+

{title} -

+ {#if content} - {#key $minimizeEventView} - {#if payloadsSize > 0} - - {#snippet children(decodedValue)} - {#if payloadsSize > 1} - {#each parsePayloads(decodedValue) as decodedContent} - - {/each} - {:else} + {#if payloadsSize > 0} + + {#snippet children(decodedValue)} + {#if payloadsSize > 1} + {#each parsePayloads(decodedValue) as decodedContent} - {/if} - {/snippet} - - {:else} - - {#snippet children(decodedValue)} + {/each} + {:else} - {/snippet} - - {/if} - {/key} + {/if} + {/snippet} + + {:else} + + {#snippet children(decodedValue)} + + {/snippet} + + {/if} {:else} {/if}
diff --git a/src/lib/components/workflow/input-and-results.svelte b/src/lib/components/workflow/input-and-results.svelte index 9c8f5efa6d..4d329e7103 100644 --- a/src/lib/components/workflow/input-and-results.svelte +++ b/src/lib/components/workflow/input-and-results.svelte @@ -1,25 +1,28 @@ -
- - +
+
+ +
+
+ +
diff --git a/src/lib/components/workflow/metadata/workflow-current-details.svelte b/src/lib/components/workflow/metadata/workflow-current-details.svelte index a8d3bb16b8..11260eb742 100644 --- a/src/lib/components/workflow/metadata/workflow-current-details.svelte +++ b/src/lib/components/workflow/metadata/workflow-current-details.svelte @@ -59,7 +59,7 @@
-
+

{translate('workflows.current-details')}

@@ -89,13 +89,9 @@
-
+
{#key currentDetails} - + {/key}
diff --git a/src/lib/components/workflow/metadata/workflow-summary-and-details.svelte b/src/lib/components/workflow/metadata/workflow-summary-and-details.svelte index 3e857215f2..2b8a18f77d 100644 --- a/src/lib/components/workflow/metadata/workflow-summary-and-details.svelte +++ b/src/lib/components/workflow/metadata/workflow-summary-and-details.svelte @@ -11,7 +11,7 @@
-
+

{translate('workflows.summary')}

@@ -27,7 +27,7 @@ {/if}
-
+

{translate('workflows.details')}

@@ -43,7 +43,7 @@ {/if}
-
+

Events with Metadata

diff --git a/src/lib/components/workflow/pending-activity/pending-activity-card.svelte b/src/lib/components/workflow/pending-activity/pending-activity-card.svelte index 37ffc1ec95..ef551814b9 100644 --- a/src/lib/components/workflow/pending-activity/pending-activity-card.svelte +++ b/src/lib/components/workflow/pending-activity/pending-activity-card.svelte @@ -1,11 +1,11 @@
1 ? 'retrying' : 'pending', + })} > -
-
- -

{activity.activityType}

+
+
+

Pending Activity

+ {#if showActivityCommands} + + {/if}
- {#if showActivityCommands} - - {/if}
-
-
- {@render detail(translate('workflows.activity-id'), activity.activityId)} - {#if activity.paused && activity.pauseInfo} - {@render detail( - translate('activities.paused-by'), - activity.pauseInfo?.manual?.identity || '', - )} - {@render detail( - translate('activities.paused-since'), - formatDate(activity.pauseInfo?.pauseTime, $timeFormat, { - relative: $relativeTime, - format: $timestampFormat, - }), - )} - {@render detail( - translate('activities.pause-reason'), - activity.pauseInfo?.manual?.reason || '-', - )} - {/if} - {@render detail(translate('workflows.attempt'), attempts)} - {#if activity.scheduledTime} - {@const timeDifference = toTimeDifference({ - date: activity.scheduledTime, - negativeDefault: '', - })} - {#if timeDifference} - {@render nextRetry(timeDifference)} - {/if} - {/if} - {#if activity.lastAttemptCompleteTime} - {@render detail( - translate('workflows.last-attempt-completed-time'), - formatDate(activity.lastAttemptCompleteTime, $timeFormat, { - relative: $relativeTime, - format: $timestampFormat, - }), - )} + +
+ {@render detail(translate('workflows.activity-id'), activity.activityId)} + {#if activity.paused && activity.pauseInfo} + {@render detail( + translate('activities.paused-by'), + activity.pauseInfo?.manual?.identity || '', + )} + {@render detail( + translate('activities.paused-since'), + formatDate(activity.pauseInfo?.pauseTime, $timeFormat, { + relative: $relativeTime, + }), + )} + {@render detail( + translate('activities.pause-reason'), + activity.pauseInfo?.manual?.reason || '-', + )} + {/if} + {@render detail(translate('workflows.attempt'), attempts)} + {#if activity.scheduledTime} + {@const timeDifference = toTimeDifference({ + date: activity.scheduledTime, + negativeDefault: '', + })} + {#if timeDifference} + {@render nextRetry(timeDifference)} {/if} - {#if activity.expirationTime} - {@render detail( - translate('workflows.retry-expiration'), - formatRetryExpiration( - activity.maximumAttempts, - formatDuration( - getDuration({ - start: Date.now(), - end: activity.expirationTime, - }), - ), + {/if} + {#if activity.lastAttemptCompleteTime} + {@render detail( + translate('workflows.last-attempt-completed-time'), + formatDate(activity.lastAttemptCompleteTime, $timeFormat, { + relative: $relativeTime, + }), + )} + {/if} + {#if activity.expirationTime} + {@render detail( + translate('workflows.retry-expiration'), + formatRetryExpiration( + activity.maximumAttempts, + formatDuration( + getDuration({ + start: Date.now(), + end: activity.expirationTime, + }), ), - )} - {/if} - {#if activity.lastHeartbeatTime} - {@render detail( - translate('workflows.last-heartbeat'), - formatDate(activity.lastHeartbeatTime, $timeFormat, { - relative: $relativeTime, - format: $timestampFormat, - }), - )} - {/if} - {#if activity.lastStartedTime} + ), + )} + {/if} + {#if activity.lastHeartbeatTime} + {@render detail( + translate('workflows.last-heartbeat'), + formatDate(activity.lastHeartbeatTime, $timeFormat, { + relative: $relativeTime, + }), + )} + {/if} + {#if activity.lastStartedTime} + {@render detail( + translate('workflows.last-started-time'), + formatDate(activity.lastStartedTime, $timeFormat, { + relative: $relativeTime, + }), + )} + {/if} + {#if activity.lastWorkerIdentity} + {@render detail( + translate('workflows.last-worker-identity'), + activity.lastWorkerIdentity, + )} + {/if} + {#if activity.priority} + {#if activity.priority.priorityKey} {@render detail( - translate('workflows.last-started-time'), - formatDate(activity.lastStartedTime, $timeFormat, { - relative: $relativeTime, - format: $timestampFormat, - }), + translate('workflows.priority'), + activity.priority.priorityKey, )} {/if} - {#if activity.lastWorkerIdentity} + {#if activity.priority.fairnessKey} {@render detail( - translate('workflows.last-worker-identity'), - activity.lastWorkerIdentity, + translate('workflows.fairness'), + activity.priority.fairnessKey, )} {/if} - {#if activity.priority} - {#if activity.priority.priorityKey} - {@render detail( - translate('workflows.priority'), - activity.priority.priorityKey, - )} - {/if} - {#if activity.priority.fairnessKey} - {@render detail( - translate('workflows.fairness'), - activity.priority.fairnessKey, - )} - {/if} - {/if} -
-
- {#if failed} - {#if totalPending > 20} - {@render failuresAccordion()} - {:else} - {@render failuresCodeBlock()} - {/if} - {/if} - {#if activity.heartbeatDetails} - {@render heartbeat()} + {/if} +
+
+ {#if failed} + {#if totalPending > 20} + {@render failuresAccordion()} + {:else} + {@render failuresCodeBlock()} {/if} -
+ {/if} + {#if activity.heartbeatDetails} + {@render heartbeat()} + {/if}
{#snippet detail(label: string, value: string | number | Snippet)} -
-

+

+

{label}

@@ -182,8 +190,8 @@ {/snippet} {#snippet heartbeat()} -

-

+

+

{translate('workflows.heartbeat-details')}

{#key activity.attempt} @@ -202,10 +210,10 @@ {/snippet} {#snippet failuresCodeBlock()} -
+
{#if activity.lastFailure} -

+

{translate('workflows.last-failure')}

{#key activity.attempt} @@ -226,7 +234,7 @@
{#if activity.lastFailure?.stackTrace}
-

+

{translate('common.stack-trace')}

-

+

+

{translate('workflows.next-retry')}

@@ -279,7 +287,7 @@ )} {#if activity.maximumAttempts} -

+

{formatAttemptsLeft(activity.maximumAttempts, activity.attempt)} remaining

{/if} diff --git a/src/lib/components/workflow/pending-nexus-operation/pending-nexus-operation-card.svelte b/src/lib/components/workflow/pending-nexus-operation/pending-nexus-operation-card.svelte index f44dc804a9..5556ea7051 100644 --- a/src/lib/components/workflow/pending-nexus-operation/pending-nexus-operation-card.svelte +++ b/src/lib/components/workflow/pending-nexus-operation/pending-nexus-operation-card.svelte @@ -1,4 +1,5 @@
1 ? 'retrying' : 'pending', + })} > -
-
+
+
{operation.state} -

{translate('workflows.pending-nexus-operation')}

+

+ {translate('workflows.pending-nexus-operation')} +

-
-
- {#if operation.endpoint} - {@render detail(translate('nexus.endpoint'), operation.endpoint)} - {/if} - {#if operation.service} - {@render detail(translate('nexus.service'), operation.service)} - {/if} - {#if operation.operation} - {@render detail(translate('nexus.operation'), operation.operation)} - {/if} - {#if operation.operationToken} - {@render detail( - translate('nexus.operation-token'), - operation.operationToken, - )} - {/if} - {@render detail(translate('workflows.attempt'), attempts)} - {#if operation.nextAttemptScheduleTime} - {@const timeDifference = toTimeDifference({ - date: operation.nextAttemptScheduleTime, - negativeDefault: '', - })} - {#if timeDifference} - {@render nextRetry(timeDifference)} - {/if} - {/if} - {#if operation.lastAttemptCompleteTime} - {@render detail( - translate('workflows.last-attempt-completed-time'), - formatDate(operation.lastAttemptCompleteTime, $timeFormat, { - relative: $relativeTime, - format: $timestampFormat, - }), - )} - {/if} - {#if operation.scheduledEventId} - {@render detail( - translate('workflows.schedule-event-id'), - String(operation.scheduledEventId), - )} - {/if} - {#if operation.scheduledTime} - {@render detail( - translate('workflows.scheduled-time'), - formatDate(operation.scheduledTime, $timeFormat, { - relative: $relativeTime, - format: $timestampFormat, - }), - )} - {/if} - {#if operation.scheduleToCloseTimeout} - {@render detail( - translate('workflows.schedule-to-close-timeout'), - operation.scheduleToCloseTimeout as string, - )} - {/if} -
-
- {#if failed} - {@render failures()} - {/if} - {#if operation.blockedReason} -
-

- {translate('nexus.blocked-reason')} -

- -
- {/if} - {#if Object.keys(operation.cancellationInfo ?? {}).length > 0} -
-

- {translate('nexus.cancellation-info')} -

- -
+
+ {#if operation.endpoint} + {@render detail(translate('nexus.endpoint'), operation.endpoint)} + {/if} + {#if operation.service} + {@render detail(translate('nexus.service'), operation.service)} + {/if} + {#if operation.operation} + {@render detail(translate('nexus.operation'), operation.operation)} + {/if} + {#if operation.operationToken} + {@render detail( + translate('nexus.operation-token'), + operation.operationToken, + )} + {/if} + {@render detail(translate('workflows.attempt'), attempts)} + {#if operation.nextAttemptScheduleTime} + {@const timeDifference = toTimeDifference({ + date: operation.nextAttemptScheduleTime, + negativeDefault: '', + })} + {#if timeDifference} + {@render nextRetry(timeDifference)} {/if} -
+ {/if} + {#if operation.lastAttemptCompleteTime} + {@render detail( + translate('workflows.last-attempt-completed-time'), + formatDate(operation.lastAttemptCompleteTime, $timeFormat, { + relative: $relativeTime, + }), + )} + {/if} + {#if operation.scheduledEventId} + {@render detail( + translate('workflows.schedule-event-id'), + String(operation.scheduledEventId), + )} + {/if} + {#if operation.scheduledTime} + {@render detail( + translate('workflows.scheduled-time'), + formatDate(operation.scheduledTime, $timeFormat, { + relative: $relativeTime, + }), + )} + {/if} + {#if operation.scheduleToCloseTimeout} + {@render detail( + translate('workflows.schedule-to-close-timeout'), + operation.scheduleToCloseTimeout as string, + )} + {/if} +
+
+ {#if failed} + {@render failures()} + {/if} + {#if operation.blockedReason} +
+

+ {translate('nexus.blocked-reason')} +

+ +
+ {/if} + {#if Object.keys(operation.cancellationInfo ?? {}).length > 0} +
+

+ {translate('nexus.cancellation-info')} +

+ +
+ {/if}
{#snippet nextRetry(timeDifference)} -
-

+

+

{translate('workflows.next-retry')}

@@ -140,8 +153,8 @@ {/snippet} {#snippet detail(label: string, value: string | number | Snippet)} -

-

+

+

{label}

@@ -165,7 +178,7 @@

{#if operation.lastAttemptFailure} -

+

{translate('workflows.last-failure')}

{#if operation.lastAttemptFailure?.stackTrace} -

+

{translate('common.stack-trace')}

{#if runningWithNoWorkers} -
+
{#if $$restProps.leadingIcon} - + {:else} {/if} diff --git a/src/lib/i18n/locales/en/workflows.ts b/src/lib/i18n/locales/en/workflows.ts index 54c9053ee2..764e916338 100644 --- a/src/lib/i18n/locales/en/workflows.ts +++ b/src/lib/i18n/locales/en/workflows.ts @@ -79,6 +79,7 @@ export const Strings = { compact: 'Compact', json: 'JSON', download: 'Download', + 'download-history': 'Download History', 'workflow-actions': 'Workflow Actions', 'more-actions': 'More Actions', 'reset-disabled-unauthorized': @@ -121,6 +122,7 @@ export const Strings = { 'reset-success-alert-title': 'This Workflow has been reset', 'reset-success-alert-description': 'You can find the resulting Workflow Execution <1>here.', + 'timeline-tab': 'Timeline', 'history-tab': 'History', 'workflow-history': 'Workflow History', 'workers-tab': 'Workers', diff --git a/src/lib/layouts/workflow-header.svelte b/src/lib/layouts/workflow-header.svelte index d85823287c..af60866439 100644 --- a/src/lib/layouts/workflow-header.svelte +++ b/src/lib/layouts/workflow-header.svelte @@ -8,16 +8,10 @@ import WorkflowActions from '$lib/components/workflow-actions.svelte'; import WorkflowStatus from '$lib/components/workflow-status.svelte'; import Alert from '$lib/holocene/alert.svelte'; - import Badge from '$lib/holocene/badge.svelte'; import Copyable from '$lib/holocene/copyable/index.svelte'; - import Icon from '$lib/holocene/icon/icon.svelte'; import Link from '$lib/holocene/link.svelte'; - import TabList from '$lib/holocene/tab/tab-list.svelte'; - import Tab from '$lib/holocene/tab/tab.svelte'; - import Tabs from '$lib/holocene/tab/tabs.svelte'; import { translate } from '$lib/i18n/translate'; - import { getInboundNexusLinkEvents } from '$lib/runes/inbound-nexus-links.svelte'; - import { getWorkflowPollersWithVersions } from '$lib/runes/workflow-versions.svelte'; + // import { getInboundNexusLinkEvents } from '$lib/runes/inbound-nexus-links.svelte'; import { fullEventHistory } from '$lib/stores/events'; import { resetWorkflows } from '$lib/stores/reset-workflows'; import { workflowRun } from '$lib/stores/workflow-run'; @@ -25,22 +19,12 @@ import { isCancelInProgress } from '$lib/utilities/cancel-in-progress'; import { isWorkflowDelayed } from '$lib/utilities/delayed-workflows'; import { - getWorkflowNexusLinksFromHistory, + // getWorkflowNexusLinksFromHistory, getWorkflowRelationships, } from '$lib/utilities/get-workflow-relationships'; - import { pathMatches } from '$lib/utilities/path-matches'; import { - routeForCallStack, routeForEventHistory, - routeForNexusLinks, - routeForPendingActivities, - routeForRelationships, - routeForUserMetadata, - routeForWorkers, - routeForWorkflowMemo, - routeForWorkflowQuery, routeForWorkflows, - routeForWorkflowSearchAttributes, } from '$lib/utilities/route-for'; import { isWorkflowTaskFailure } from '$lib/utilities/workflow-task-failures'; @@ -50,7 +34,7 @@ run: runId, id: eventId, } = $derived(page.params); - const { workflow, workers } = $derived($workflowRun); + const { workflow } = $derived($workflowRun); const routeParameters = $derived({ namespace, workflow: workflowId, @@ -58,9 +42,9 @@ }); const isRunning = $derived(workflow?.isRunning); - const activitiesCanceled = $derived( - ['Terminated', 'TimedOut', 'Canceled'].includes(workflow?.status), - ); + // const activitiesCanceled = $derived( + // ['Terminated', 'TimedOut', 'Canceled'].includes(workflow?.status), + // ); const cancelInProgress = $derived( isCancelInProgress(workflow?.status, $fullEventHistory), ); @@ -77,13 +61,13 @@ namespace, })}?${$workflowsSearchParams}`, ); - const outboundLinks = $derived( - getWorkflowNexusLinksFromHistory($fullEventHistory)?.length || 0, - ); - const inboundLinks = $derived( - getInboundNexusLinkEvents($fullEventHistory)?.length || 0, - ); - const linkCount = $derived(outboundLinks + inboundLinks); + // const outboundLinks = $derived( + // getWorkflowNexusLinksFromHistory($fullEventHistory)?.length || 0, + // ); + // const inboundLinks = $derived( + // getInboundNexusLinkEvents($fullEventHistory)?.length || 0, + // ); + // const linkCount = $derived(outboundLinks + inboundLinks);
@@ -110,8 +94,8 @@ {/if}
-
-
+
+
@@ -120,9 +104,9 @@ >

@@ -193,7 +177,7 @@
{/if} - +
diff --git a/src/lib/layouts/workflow-history-layout.svelte b/src/lib/layouts/workflow-history-layout.svelte index e51ce56fe6..9a5616dfa9 100644 --- a/src/lib/layouts/workflow-history-layout.svelte +++ b/src/lib/layouts/workflow-history-layout.svelte @@ -1,71 +1,65 @@ -
+
-
- - {#if workflowTaskFailedError} - - {/if} - {#if workflow?.callbacks?.length} - - {/if} -
+ {#if workflowTaskFailedError} + + {/if} + {#if workflow?.callbacks?.length} + + {/if}
-
+
-

- {translate('workflows.event-history')} -

+
+ +
+
Event Categories
+
+
+ + Workflow +
+
+ + Activity +
+
+ + Child Workflow +
+
+ + Timer +
+
+ + Signal +
+
+ + Update +
+
+ + Nexus +
+
+ + Local Activity +
+
+ + Other +
+
+
+ +
+ {#if $eventViewType !== 'json'} {reverseSort ? 'Descending' : 'Ascending'} {/if} - - ($minimizeEventView = !$minimizeEventView)} - >{$minimizeEventView ? 'Minimized' : 'Expanded'} - - + {$pauseLiveUpdates ? 'Unfreeze' : 'Freeze'} - (showDownloadPrompt = true)} - > - {translate('common.download')} -
-
- + + + + +
+ {:else if timelineView} + + {:else if compactView} + + {:else if historyView} + + {:else if jsonView} + + {:else if relationshipView} + + {:else if workersView} + - -
+ {/if}
- - - diff --git a/src/lib/models/event-groups/create-event-group.ts b/src/lib/models/event-groups/create-event-group.ts index 76bffab75e..c0d3e70a2d 100644 --- a/src/lib/models/event-groups/create-event-group.ts +++ b/src/lib/models/event-groups/create-event-group.ts @@ -100,6 +100,12 @@ const createGroupFor = ( get finalClassification() { return getLastEvent(this).classification; }, + get input() { + return this.initialEvent.attributes?.input; + }, + get result() { + return this.lastEvent.attributes?.result; + }, get isPending() { return ( !!this.pendingActivity || diff --git a/src/lib/models/event-groups/event-groups.d.ts b/src/lib/models/event-groups/event-groups.d.ts index ce5c670fb1..5c33623db9 100644 --- a/src/lib/models/event-groups/event-groups.d.ts +++ b/src/lib/models/event-groups/event-groups.d.ts @@ -1,4 +1,4 @@ -import type { EventLink, Payload } from '$lib/types'; +import type { EventLink, Payload, Payloads } from '$lib/types'; import type { PendingActivity, PendingNexusOperation, @@ -24,6 +24,8 @@ interface EventGroup lastEvent: WorkflowEvent; eventList: WorkflowEvent[]; finalClassification: EventClassification; + input: Payloads | undefined; + result: Payloads | undefined; isPending: boolean; isFailureOrTimedOut: boolean; isCanceled: boolean; diff --git a/src/lib/pages/workflow-history.svelte b/src/lib/pages/workflow-history.svelte index 8581b8f24e..b78b31558f 100644 --- a/src/lib/pages/workflow-history.svelte +++ b/src/lib/pages/workflow-history.svelte @@ -1,29 +1,18 @@ + + diff --git a/src/lib/pages/workflow-pending-activities.svelte b/src/lib/pages/workflow-pending-activities.svelte index 0dc45b3c39..8755fa4ca6 100644 --- a/src/lib/pages/workflow-pending-activities.svelte +++ b/src/lib/pages/workflow-pending-activities.svelte @@ -1,30 +1,29 @@
- {#if pendingActivities.length} -
- {#each pendingActivities as activity (activity.id)} - - {/each} -
+ {#each pendingActivityGroups as group} + {:else} - {/if} + {/each}
diff --git a/src/lib/pages/workflow-timeline.svelte b/src/lib/pages/workflow-timeline.svelte new file mode 100644 index 0000000000..ef9e63a240 --- /dev/null +++ b/src/lib/pages/workflow-timeline.svelte @@ -0,0 +1,18 @@ + + + diff --git a/src/lib/pages/workflow-user-metadata.svelte b/src/lib/pages/workflow-user-metadata.svelte index e002baf02f..8d828b79dd 100644 --- a/src/lib/pages/workflow-user-metadata.svelte +++ b/src/lib/pages/workflow-user-metadata.svelte @@ -3,7 +3,7 @@ import WorkflowSummaryAndDetails from '$lib/components/workflow/metadata/workflow-summary-and-details.svelte'; -
+
diff --git a/src/lib/runes/workflow-history-information.svelte.ts b/src/lib/runes/workflow-history-information.svelte.ts new file mode 100644 index 0000000000..cc72127407 --- /dev/null +++ b/src/lib/runes/workflow-history-information.svelte.ts @@ -0,0 +1,46 @@ +import { get } from 'svelte/store'; + +import { groupEvents } from '$lib/models/event-groups'; +import { type EventSortOrder } from '$lib/stores/event-view'; +import { currentEventHistory } from '$lib/stores/events'; +import type { WorkflowEvents } from '$lib/types/events'; +import type { WorkflowExecution } from '$lib/types/workflows'; +import { getWorkflowTaskFailedEvent } from '$lib/utilities/get-workflow-task-failed-event'; + +export const workflowHistoryInformation = ( + workflow: WorkflowExecution, + filteredHistory: WorkflowEvents, + sortOrder: EventSortOrder, +) => { + const reverseSort = $derived(sortOrder === 'descending'); + const pendingActivities = $derived(workflow?.pendingActivities); + const pendingNexusOperations = $derived(workflow?.pendingNexusOperations); + const ascendingGroups = $derived( + groupEvents( + filteredHistory, + 'ascending', + pendingActivities, + pendingNexusOperations, + ), + ); + + const groups = $derived( + reverseSort ? [...ascendingGroups].reverse() : ascendingGroups, + ); + const history = $derived( + reverseSort ? [...filteredHistory].reverse() : filteredHistory, + ); + const workflowTaskFailedError = $derived( + getWorkflowTaskFailedEvent(get(currentEventHistory), 'ascending'), + ); + + return { + workflow, + reverseSort, + pendingActivities, + pendingNexusOperations, + groups, + history, + workflowTaskFailedError, + }; +}; diff --git a/src/lib/stores/active-events.ts b/src/lib/stores/active-events.ts index 5b4f8bba98..a80e0c8f57 100644 --- a/src/lib/stores/active-events.ts +++ b/src/lib/stores/active-events.ts @@ -7,10 +7,12 @@ export const startIndex = writable(0); export const endIndex = writable(indexPageSize); export const activeGroups = writable([]); +export const activeGroup = writable(null); export const activeGroupHeight = writable(0); export const clearActives = () => { activeGroups.set([]); + activeGroup.set(null); activeGroupHeight.set(0); startIndex.set(0); endIndex.set(indexPageSize); @@ -18,14 +20,17 @@ export const clearActives = () => { export const clearActiveGroups = () => { activeGroups.set([]); + activeGroup.set(null); activeGroupHeight.set(0); }; export const setActiveGroup = (group: EventGroup) => { if (!get(activeGroups).includes(group.id)) { activeGroups.set([group.id]); + activeGroup.set(group); } else { activeGroupHeight.set(0); activeGroups.set([]); + activeGroup.set(null); } }; diff --git a/src/lib/stores/event-view.ts b/src/lib/stores/event-view.ts index 4552ecf353..1691f834c6 100644 --- a/src/lib/stores/event-view.ts +++ b/src/lib/stores/event-view.ts @@ -1,5 +1,5 @@ import { persistStore } from '$lib/stores/persist-store'; -import type { EventView } from '$lib/types/events'; +import type { EventView, HistoryView } from '$lib/types/events'; import type { BooleanString } from '$lib/types/global'; export type EventSortOrder = 'ascending' | 'descending'; @@ -14,8 +14,11 @@ export const autoRefreshWorkflow = persistStore<'on' | 'off'>( ); export const eventViewType = persistStore('eventView', 'feed', true); - -export const minimizeEventView = persistStore('minimizeEventView', true, true); +export const historyViewType = persistStore( + 'historyView', + 'timeline', + true, +); export const eventFilterSort = persistStore( 'eventFilterSort', diff --git a/src/lib/theme/colors.ts b/src/lib/theme/colors.ts index 9f28408c02..54f4a4d35b 100644 --- a/src/lib/theme/colors.ts +++ b/src/lib/theme/colors.ts @@ -151,11 +151,12 @@ export const colors = { 'space-black': '#141414', 'off-black': palette['slate'][950], 'code-black': '#292D3E', + 'code-white': '#F5F7FF', current: 'currentColor', transparent: 'transparent', mint: '#59FDA0', ...palette, } as const satisfies Record< string, - HexColor | 'transparent' | 'currentColor' | Shades + HexColor | RGBA | 'transparent' | 'currentColor' | Shades >; diff --git a/src/lib/theme/plugin.ts b/src/lib/theme/plugin.ts index 9097c73524..abe3f26ad6 100644 --- a/src/lib/theme/plugin.ts +++ b/src/lib/theme/plugin.ts @@ -196,7 +196,6 @@ const temporal = plugin( warning: css('--color-surface-warning'), danger: css('--color-surface-danger'), 'code-block': css('--color-surface-code-block'), - DEFAULT: css('--color-surface-primary'), }), borderColor: ({ theme }) => ({ diff --git a/src/lib/theme/types.d.ts b/src/lib/theme/types.d.ts index aef7222523..5fdb51e549 100644 --- a/src/lib/theme/types.d.ts +++ b/src/lib/theme/types.d.ts @@ -2,6 +2,7 @@ type PaletteColor = import('./colors').PaletteColor; type Palette = import('./colors').Palette; type RGB = `${number} ${number} ${number}`; +type RGBA = `rgba(${number}, ${number}, ${number}, ${number})`; type HexColor = `#${string}`; type CSSVariable = `--${string}`; diff --git a/src/lib/theme/variables.ts b/src/lib/theme/variables.ts index 7ef913c015..4881c9a5f7 100644 --- a/src/lib/theme/variables.ts +++ b/src/lib/theme/variables.ts @@ -117,7 +117,7 @@ export const variables = { dark: 'black', }, '--color-surface-code-block': { - light: 'slate.50', + light: 'code-white', dark: 'code-black', }, // Interactive diff --git a/src/lib/types/events.ts b/src/lib/types/events.ts index 1016906d0c..cf3cddf36d 100644 --- a/src/lib/types/events.ts +++ b/src/lib/types/events.ts @@ -151,6 +151,14 @@ export type ChildEvent = StartChildWorkflowExecutionInitiatedEvent & ChildWorkflowExecutionTimedOutEvent & ChildWorkflowExecutionTerminatedEvent; +export type HistoryView = + | 'summary' + | 'timeline' + | 'compact' + | 'history' + | 'json' + | 'relationship' + | 'workers'; export type EventView = 'compact' | 'feed' | 'json'; export type TaskQueueView = 'workers' | 'versioning'; diff --git a/src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/history/+page.svelte b/src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/history/+page.svelte index 6e4c8ed77d..977a515b0f 100644 --- a/src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/history/+page.svelte +++ b/src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/history/+page.svelte @@ -1,14 +1,15 @@