diff --git a/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/ChartHeader.tsx b/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/ChartHeader.tsx
new file mode 100644
index 000000000..1fafd08c8
--- /dev/null
+++ b/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/ChartHeader.tsx
@@ -0,0 +1,68 @@
+import * as React from 'react';
+import * as moment from 'moment-timezone';
+import makeStyles from '@material-ui/core/styles/makeStyles';
+import { COLOR_SPECTRUM } from 'components/Theme/colorSpectrum';
+import { useScaleContext } from './scaleContext';
+import { TimeZone } from './helpers';
+
+interface StyleProps {
+ chartWidth: number;
+ labelInterval: number;
+}
+
+const useStyles = makeStyles((_theme) => ({
+ chartHeader: (props: StyleProps) => ({
+ height: 41,
+ display: 'flex',
+ alignItems: 'center',
+ width: `${props.chartWidth}px`,
+ }),
+ taskDurationsLabelItem: (props: StyleProps) => ({
+ fontSize: 12,
+ fontFamily: 'Open Sans',
+ fontWeight: 'bold',
+ color: COLOR_SPECTRUM.gray40.color,
+ paddingLeft: 10,
+ width: `${props.labelInterval}px`,
+ }),
+}));
+
+interface HeaderProps extends StyleProps {
+ chartTimezone: string;
+ totalDurationSec: number;
+ startedAt: Date;
+}
+
+export const ChartHeader = (props: HeaderProps) => {
+ const styles = useStyles(props);
+
+ const { chartInterval: chartTimeInterval, setMaxValue } = useScaleContext();
+ const { startedAt, chartTimezone, totalDurationSec } = props;
+
+ React.useEffect(() => {
+ setMaxValue(props.totalDurationSec);
+ }, [props.totalDurationSec, setMaxValue]);
+
+ const labels = React.useMemo(() => {
+ const len = Math.ceil(totalDurationSec / chartTimeInterval);
+ const lbs = len > 0 ? new Array(len).fill('') : [];
+ return lbs.map((_, idx) => {
+ const time = moment.utc(new Date(startedAt.getTime() + idx * chartTimeInterval * 1000));
+ return chartTimezone === TimeZone.UTC
+ ? time.format('hh:mm:ss A')
+ : time.local().format('hh:mm:ss A');
+ });
+ }, [chartTimezone, startedAt, chartTimeInterval, totalDurationSec]);
+
+ return (
+
+ {labels.map((label) => {
+ return (
+
+ {label}
+
+ );
+ })}
+
+ );
+};
diff --git a/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/ExecutionTimeline.tsx b/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/ExecutionTimeline.tsx
index 9d686a6f9..14af5b17e 100644
--- a/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/ExecutionTimeline.tsx
+++ b/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/ExecutionTimeline.tsx
@@ -13,7 +13,7 @@ import { createRef, useContext, useEffect, useRef, useState } from 'react';
import { NodeExecutionsByIdContext } from 'components/Executions/contexts';
import { checkForDynamicExecutions } from 'components/common/utils';
import { convertToPlainNodes } from './helpers';
-import { ChartHeader } from './chartHeader';
+import { ChartHeader } from './ChartHeader';
import { useScaleContext } from './scaleContext';
import { TaskNames } from './TaskNames';
import { getChartDurationData } from './TimelineChart/chartData';
diff --git a/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/TaskNames.tsx b/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/TaskNames.tsx
new file mode 100644
index 000000000..a2bcd4187
--- /dev/null
+++ b/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/TaskNames.tsx
@@ -0,0 +1,99 @@
+import * as React from 'react';
+import { makeStyles, Theme, Typography } from '@material-ui/core';
+
+import { RowExpander } from 'components/Executions/Tables/RowExpander';
+import { getNodeTemplateName } from 'components/WorkflowGraph/utils';
+import { dNode } from 'models/Graph/types';
+import { NodeExecutionName } from './NodeExecutionName';
+import { NodeExecutionsTimelineContext } from './context';
+
+const useStyles = makeStyles((theme: Theme) => ({
+ taskNamesList: {
+ overflowY: 'scroll',
+ flex: 1,
+ },
+ namesContainer: {
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'flex-start',
+ justifyContent: 'left',
+ padding: '0 10px',
+ height: 56,
+ width: 256,
+ borderBottom: `1px solid ${theme.palette.divider}`,
+ whiteSpace: 'nowrap',
+ },
+ namesContainerExpander: {
+ display: 'flex',
+ marginTop: 'auto',
+ marginBottom: 'auto',
+ },
+ namesContainerBody: {
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'flex-start',
+ justifyContent: 'center',
+ whiteSpace: 'nowrap',
+ height: '100%',
+ overflow: 'hidden',
+ },
+ displayName: {
+ marginTop: 4,
+ textOverflow: 'ellipsis',
+ width: '100%',
+ overflow: 'hidden',
+ },
+ leaf: {
+ width: 30,
+ },
+}));
+
+interface TaskNamesProps {
+ nodes: dNode[];
+ onScroll: () => void;
+ onToggle: (id: string, scopeId: string, level: number) => void;
+}
+
+export const TaskNames = React.forwardRef((props, ref) => {
+ const state = React.useContext(NodeExecutionsTimelineContext);
+ const { nodes, onScroll, onToggle } = props;
+ const styles = useStyles();
+
+ return (
+
+ {nodes.map((node) => {
+ const templateName = getNodeTemplateName(node);
+ const nodeLevel = node?.level ?? 0;
+ return (
+
+
+ {node.nodes?.length ? (
+
onToggle(node.id, node.scopedId, nodeLevel)}
+ />
+ ) : (
+
+ )}
+
+
+
+
+
+ {templateName}
+
+
+
+ );
+ })}
+
+ );
+});
diff --git a/packages/zapp/console/src/components/WorkflowGraph/utils.ts b/packages/zapp/console/src/components/WorkflowGraph/utils.ts
index afc31968f..79b2e342b 100644
--- a/packages/zapp/console/src/components/WorkflowGraph/utils.ts
+++ b/packages/zapp/console/src/components/WorkflowGraph/utils.ts
@@ -4,6 +4,7 @@ import { CompiledWorkflow, Workflow } from 'models/Workflow/types';
import { CompiledNode, TaskNode } from 'models/Node/types';
import { CompiledTask, TaskTemplate } from 'models/Task/types';
import { dTypes, dNode } from 'models/Graph/types';
+import _ from 'lodash';
import { transformerWorkflowToDag } from './transformerWorkflowToDag';
/**
* TODO FC#393: these are dupes for testing, remove once tests fixed
@@ -35,17 +36,7 @@ export function isExpanded(node: any) {
* @returns boolean
*/
export const checkIfObjectsAreSame = (a, b) => {
- // if one of the objects is null (undefined), objects can't be the same
- if ((!a || !b) && a != b) {
- return false;
- }
-
- for (const k in a) {
- if (a[k] != b[k]) {
- return false;
- }
- }
- return true;
+ return _.isEqual(a, b);
};
/**
@@ -115,11 +106,12 @@ export const getNodeTypeFromCompiledNode = (node: CompiledNode): dTypes => {
};
export const getSubWorkflowFromId = (id, workflow) => {
+ const _ = require('lodash');
const { subWorkflows } = workflow;
/* Find current matching entitity from subWorkflows */
for (const k in subWorkflows) {
const subWorkflowId = subWorkflows[k].template.id;
- if (checkIfObjectsAreSame(subWorkflowId, id)) {
+ if (_.isEqual(subWorkflowId, id)) {
return subWorkflows[k];
}
}
@@ -127,11 +119,12 @@ export const getSubWorkflowFromId = (id, workflow) => {
};
export const getTaskTypeFromCompiledNode = (taskNode: TaskNode, tasks: CompiledTask[]) => {
+ const _ = require('lodash');
for (let i = 0; i < tasks.length; i++) {
const compiledTask: CompiledTask = tasks[i];
const taskTemplate: TaskTemplate = compiledTask.template;
const templateId: Identifier = taskTemplate.id;
- if (checkIfObjectsAreSame(templateId, taskNode.referenceId)) {
+ if (_.isEqual(templateId, taskNode.referenceId)) {
return compiledTask;
}
}