Skip to content

Commit 1bce34f

Browse files
base code for grouped event
Signed-off-by: Adhitya Mamallan <[email protected]>
1 parent cb45806 commit 1bce34f

File tree

8 files changed

+396
-2
lines changed

8 files changed

+396
-2
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { useEffect, useState } from 'react';
2+
3+
import { type WorkflowExecutionCloseStatus } from '@/__generated__/proto-ts/uber/cadence/api/v1/WorkflowExecutionCloseStatus';
4+
import getFormattedEventsDuration from '@/views/workflow-history/workflow-history-events-duration-badge/helpers/get-formatted-events-duration';
5+
6+
export default function useEventGroupDuration({
7+
startTime,
8+
closeTime,
9+
workflowIsArchived,
10+
workflowCloseStatus,
11+
eventsCount,
12+
loadingMoreEvents,
13+
hasMissingEvents,
14+
workflowCloseTime,
15+
}: {
16+
startTime: number | null | undefined;
17+
closeTime: number | null | undefined;
18+
workflowIsArchived: boolean;
19+
workflowCloseStatus: WorkflowExecutionCloseStatus | null | undefined;
20+
eventsCount: number;
21+
loadingMoreEvents: boolean;
22+
hasMissingEvents: boolean;
23+
workflowCloseTime: number | null | undefined;
24+
}) {
25+
const endTime = closeTime || workflowCloseTime;
26+
const workflowEnded =
27+
workflowIsArchived ||
28+
workflowCloseStatus !== 'WORKFLOW_EXECUTION_CLOSE_STATUS_INVALID';
29+
const singleEvent = eventsCount === 1 && !hasMissingEvents;
30+
31+
const hideDuration =
32+
loadingMoreEvents || singleEvent || (workflowEnded && !endTime);
33+
const isOngoing = !endTime && !hideDuration;
34+
35+
const [duration, setDuration] = useState<string>(() =>
36+
getFormattedEventsDuration(startTime ?? null, endTime, isOngoing)
37+
);
38+
39+
useEffect(() => {
40+
setDuration(
41+
getFormattedEventsDuration(startTime ?? null, endTime, isOngoing)
42+
);
43+
if (isOngoing) {
44+
const interval = setInterval(() => {
45+
setDuration(
46+
getFormattedEventsDuration(startTime ?? null, endTime, true)
47+
);
48+
}, 1000);
49+
50+
return () => clearInterval(interval);
51+
}
52+
}, [startTime, endTime, isOngoing]);
53+
54+
if (!startTime || hideDuration) {
55+
return null;
56+
}
57+
58+
return duration;
59+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import isChildWorkflowExecutionEvent from '@/views/workflow-history/helpers/check-history-event-group/is-child-workflow-execution-event';
2+
import isExtendedActivityEvent from '@/views/workflow-history/helpers/check-history-event-group/is-extended-activity-event';
3+
import isExtendedDecisionEvent from '@/views/workflow-history/helpers/check-history-event-group/is-extended-decision-event';
4+
import isRequestCancelExternalWorkflowExecutionEvent from '@/views/workflow-history/helpers/check-history-event-group/is-request-cancel-external-workflow-execution-event';
5+
import isSignalExternalWorkflowExecutionEvent from '@/views/workflow-history/helpers/check-history-event-group/is-signal-external-workflow-execution-event';
6+
import isTimerEvent from '@/views/workflow-history/helpers/check-history-event-group/is-timer-event';
7+
import { type WorkflowHistoryEventFilteringType } from '@/views/workflow-history/workflow-history-filters-type/workflow-history-filters-type.types';
8+
import { type ExtendedHistoryEvent } from '@/views/workflow-history/workflow-history.types';
9+
10+
const getEventFilteringType = function (
11+
events: ExtendedHistoryEvent[]
12+
): WorkflowHistoryEventFilteringType {
13+
if (events.length === 0) {
14+
// Default to WORKFLOW if no events
15+
return 'WORKFLOW';
16+
}
17+
18+
// Check if all events are of the same type
19+
const firstEvent = events[0];
20+
21+
// Check for SIGNAL first (has special logic)
22+
if (
23+
events.every(isSignalExternalWorkflowExecutionEvent) ||
24+
('attributes' in firstEvent &&
25+
firstEvent.attributes === 'workflowExecutionSignaledEventAttributes')
26+
) {
27+
return 'SIGNAL';
28+
}
29+
30+
// Check for WORKFLOW (RequestCancelExternalWorkflowExecution or Event type with non-signal)
31+
const isNotSignal = !(
32+
'attributes' in firstEvent &&
33+
firstEvent.attributes === 'workflowExecutionSignaledEventAttributes'
34+
);
35+
if (
36+
events.every(isRequestCancelExternalWorkflowExecutionEvent) ||
37+
(isNotSignal &&
38+
!isExtendedActivityEvent(firstEvent) &&
39+
!isExtendedDecisionEvent(firstEvent) &&
40+
!isTimerEvent(firstEvent) &&
41+
!isChildWorkflowExecutionEvent(firstEvent) &&
42+
!isSignalExternalWorkflowExecutionEvent(firstEvent))
43+
) {
44+
return 'WORKFLOW';
45+
}
46+
47+
// Check other types
48+
if (events.every(isExtendedActivityEvent)) {
49+
return 'ACTIVITY';
50+
}
51+
52+
if (events.every(isExtendedDecisionEvent)) {
53+
return 'DECISION';
54+
}
55+
56+
if (events.every(isTimerEvent)) {
57+
return 'TIMER';
58+
}
59+
60+
if (events.every(isChildWorkflowExecutionEvent)) {
61+
return 'CHILDWORKFLOW';
62+
}
63+
64+
// Default fallback
65+
return 'WORKFLOW';
66+
};
67+
68+
export default getEventFilteringType;
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { styled as createStyled, type Theme } from 'baseui';
2+
import { type PanelOverrides } from 'baseui/accordion';
3+
import { type BadgeOverrides } from 'baseui/badge';
4+
import { type StyleObject } from 'styletron-react';
5+
6+
import { type WorkflowHistoryEventFilteringType } from '@/views/workflow-history/workflow-history-filters-type/workflow-history-filters-type.types';
7+
8+
import workflowHistoryEventFilteringTypeColorsConfig from '../config/workflow-history-event-filtering-type-colors.config';
9+
10+
export const styled = {
11+
HeaderContent: createStyled('div', ({ $theme }: { $theme: Theme }) => ({
12+
display: 'flex',
13+
alignItems: 'center',
14+
flex: 1,
15+
minWidth: 0,
16+
gap: $theme.sizing.scale600,
17+
})),
18+
HeaderLabel: createStyled('div', ({ $theme }: { $theme: Theme }) => ({
19+
...$theme.typography.LabelMedium,
20+
flex: 1,
21+
minWidth: 0,
22+
whiteSpace: 'nowrap',
23+
textOverflow: 'ellipsis',
24+
overflow: 'hidden',
25+
})),
26+
HeaderStatus: createStyled('div', () => ({
27+
flexShrink: 0,
28+
})),
29+
HeaderSecondaryDetails: createStyled(
30+
'div',
31+
({ $theme }: { $theme: Theme }) => ({
32+
display: 'flex',
33+
alignItems: 'center',
34+
gap: $theme.sizing.scale200,
35+
flexWrap: 'wrap',
36+
flexShrink: 0,
37+
})
38+
),
39+
HeaderTime: createStyled('div', ({ $theme }: { $theme: Theme }) => ({
40+
...$theme.typography.LabelXSmall,
41+
color: $theme.colors.contentTertiary,
42+
whiteSpace: 'nowrap',
43+
})),
44+
HeaderDuration: createStyled('div', () => ({
45+
flexShrink: 0,
46+
})),
47+
HeaderDetails: createStyled('div', () => ({
48+
flexShrink: 0,
49+
})),
50+
};
51+
52+
export const overrides = (
53+
eventFilteringType: WorkflowHistoryEventFilteringType
54+
) => ({
55+
panel: {
56+
PanelContainer: {
57+
style: ({ $theme }: { $theme: Theme }): StyleObject => ({
58+
...$theme.borders.border100,
59+
borderRadius: 0,
60+
borderWidth: '0px',
61+
marginTop: $theme.sizing.scale0,
62+
marginBottom: $theme.sizing.scale0,
63+
overflow: 'hidden',
64+
}),
65+
},
66+
Header: {
67+
style: ({ $theme }: { $theme: Theme }): StyleObject => ({
68+
// https://github.com/uber/baseweb/blob/main/src/accordion/styled-components.ts#L50
69+
// Since the original Panel uses longhand properties, we need to use longhand in overrides
70+
paddingTop: $theme.sizing.scale200,
71+
paddingBottom: $theme.sizing.scale200,
72+
paddingLeft: $theme.sizing.scale700,
73+
paddingRight: $theme.sizing.scale700,
74+
backgroundColor: 'inherit',
75+
display: 'flex',
76+
alignItems: 'center',
77+
':hover': {
78+
backgroundColor:
79+
workflowHistoryEventFilteringTypeColorsConfig[eventFilteringType]
80+
.backgroundHighlighted,
81+
},
82+
}),
83+
},
84+
Content: {
85+
style: ({ $theme }: { $theme: Theme }): StyleObject => ({
86+
// https://github.com/uber/baseweb/blob/main/src/accordion/styled-components.ts#L102
87+
// Since the original Panel uses longhand properties, we need to use longhand in overrides
88+
paddingTop: 0,
89+
paddingBottom: $theme.sizing.scale600,
90+
paddingLeft: $theme.sizing.scale700,
91+
paddingRight: $theme.sizing.scale700,
92+
backgroundColor: 'inherit',
93+
}),
94+
},
95+
} satisfies PanelOverrides,
96+
badge: {
97+
Badge: {
98+
style: ({ $theme }: { $theme: Theme }): StyleObject => ({
99+
backgroundColor: $theme.colors.backgroundSecondary,
100+
color: $theme.colors.contentSecondary,
101+
...$theme.typography.LabelXSmall,
102+
whiteSpace: 'nowrap',
103+
marginLeft: $theme.sizing.scale100,
104+
}),
105+
},
106+
} satisfies BadgeOverrides,
107+
});
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { Panel } from 'baseui/accordion';
2+
import { Badge } from 'baseui/badge';
3+
import { MdCircle } from 'react-icons/md';
4+
5+
import WorkflowHistoryEventStatusBadge from '@/views/workflow-history/workflow-history-event-status-badge/workflow-history-event-status-badge';
6+
import WorkflowHistoryEventsDurationBadge from '@/views/workflow-history/workflow-history-events-duration-badge/workflow-history-events-duration-badge';
7+
import WorkflowHistoryGroupLabel from '@/views/workflow-history/workflow-history-group-label/workflow-history-group-label';
8+
import WorkflowHistoryRemainingDurationBadge from '@/views/workflow-history/workflow-history-remaining-duration-badge/workflow-history-remaining-duration-badge';
9+
10+
import workflowHistoryEventFilteringTypeColorsConfig from '../config/workflow-history-event-filtering-type-colors.config';
11+
import useEventGroupDuration from '../hooks/use-event-group-duration';
12+
13+
import getEventFilteringType from './helpers/get-event-filtering-type';
14+
import {
15+
overrides as getOverrides,
16+
styled,
17+
} from './workflow-history-event-group.styles';
18+
import { type Props } from './workflow-history-event-group.types';
19+
20+
export default function WorkflowHistoryEventGroup({
21+
status,
22+
label,
23+
shortLabel,
24+
timeLabel,
25+
startTimeMs,
26+
closeTimeMs,
27+
expectedEndTimeInfo,
28+
workflowCloseTimeMs,
29+
workflowCloseStatus,
30+
workflowIsArchived,
31+
events,
32+
isLastEvent,
33+
eventsMetadata,
34+
hasMissingEvents,
35+
showLoadingMoreEvents,
36+
decodedPageUrlParams,
37+
badges,
38+
resetToDecisionEventId,
39+
onReset,
40+
selected,
41+
}: Props) {
42+
// Check each filter function against the event and return the one that matches, and OTHER otherwise
43+
const eventFilteringType = getEventFilteringType(events);
44+
45+
const overrides = getOverrides(eventFilteringType);
46+
const hasBadges = badges !== undefined && badges.length > 0;
47+
48+
const eventGroupDuration = useEventGroupDuration({
49+
startTime: startTimeMs,
50+
closeTime: closeTimeMs,
51+
workflowIsArchived,
52+
eventsCount: events.length,
53+
workflowCloseStatus,
54+
loadingMoreEvents: showLoadingMoreEvents,
55+
hasMissingEvents,
56+
workflowCloseTime: workflowCloseTimeMs,
57+
});
58+
59+
return (
60+
<Panel
61+
title={
62+
<styled.HeaderContent>
63+
<MdCircle
64+
color={
65+
workflowHistoryEventFilteringTypeColorsConfig[eventFilteringType]
66+
.content
67+
}
68+
/>
69+
<styled.HeaderLabel>
70+
<WorkflowHistoryGroupLabel label={label} shortLabel={shortLabel} />
71+
</styled.HeaderLabel>
72+
<styled.HeaderStatus>
73+
<WorkflowHistoryEventStatusBadge
74+
status={status}
75+
statusReady={!showLoadingMoreEvents}
76+
size="medium"
77+
/>
78+
{eventsMetadata[eventsMetadata.length - 1].label}
79+
</styled.HeaderStatus>
80+
<div>{timeLabel}</div>
81+
<div>{eventGroupDuration}</div>
82+
</styled.HeaderContent>
83+
}
84+
overrides={overrides.panel}
85+
>
86+
<div>TODO</div>
87+
</Panel>
88+
);
89+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { type WorkflowExecutionCloseStatus } from '@/__generated__/proto-ts/uber/cadence/api/v1/WorkflowExecutionCloseStatus';
2+
import { type HistoryEventsGroup } from '@/views/workflow-history/workflow-history.types';
3+
4+
import { type Props as WorkflowHistoryProps } from '../workflow-history-v2.types';
5+
6+
export type Props = Pick<
7+
HistoryEventsGroup,
8+
| 'events'
9+
| 'eventsMetadata'
10+
| 'timeLabel'
11+
| 'label'
12+
| 'hasMissingEvents'
13+
| 'status'
14+
| 'badges'
15+
| 'resetToDecisionEventId'
16+
| 'startTimeMs'
17+
| 'closeTimeMs'
18+
| 'expectedEndTimeInfo'
19+
| 'shortLabel'
20+
> & {
21+
isLastEvent: boolean;
22+
showLoadingMoreEvents: boolean;
23+
decodedPageUrlParams: WorkflowHistoryProps['params'];
24+
onReset?: () => void;
25+
selected?: boolean;
26+
workflowIsArchived: boolean;
27+
workflowCloseStatus?: WorkflowExecutionCloseStatus | null;
28+
workflowCloseTimeMs?: number | null;
29+
};

0 commit comments

Comments
 (0)