Skip to content

Commit 0b39a94

Browse files
authored
Add pending activity attempt count to timeline (#2372)
* Make offset variable in timelineTextPosition * Add pending activity attempt count to timeline row * Only show attemps if greater than 1 * Add constants and util func for offsets * Update retry badge color * Add retry label * Move next retry time next to retry attempts
1 parent b0df11c commit 0b39a94

File tree

9 files changed

+103
-64
lines changed

9 files changed

+103
-64
lines changed

Diff for: src/lib/components/event/event-summary-row.svelte

+10-15
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
77
import Icon from '$lib/holocene/icon/icon.svelte';
88
import Link from '$lib/holocene/link.svelte';
9+
import { translate } from '$lib/i18n/translate';
910
import { isEventGroup } from '$lib/models/event-groups';
1011
import type { EventGroup } from '$lib/models/event-groups/event-groups';
1112
import {
@@ -147,12 +148,20 @@
147148
{#if pendingAttempt}
148149
<div
149150
class="flex items-center gap-1 {pendingAttempt > 1 &&
150-
'surface-danger rounded px-1 py-0.5'}"
151+
'surface-retry rounded px-1 py-0.5'}"
151152
>
152153
<Icon class="mr-1.5 inline" name="retry" />
154+
{translate('workflows.retry')}
153155
{pendingAttempt}
154156
{#if hasPendingActivity}
155157
/ {hasPendingActivity.maximumAttempts || ''}
158+
{#if pendingAttempt > 1}
159+
• {translate('workflows.next-retry')}
160+
{toTimeDifference({
161+
date: hasPendingActivity.scheduledTime,
162+
negativeDefault: 'None',
163+
})}
164+
{/if}
156165
{/if}
157166
</div>
158167
{/if}
@@ -200,20 +209,6 @@
200209
</p>
201210
</div>
202211
{/if}
203-
{#if pendingAttempt > 1 && hasPendingActivity}
204-
<div class="flex items-center gap-2 text-sm">
205-
<p class="max-w-fit whitespace-nowrap text-right text-xs">
206-
Next Retry
207-
</p>
208-
<p class="flex items-center gap-0">
209-
<Icon class="mr-1.5 inline" name="clock" />
210-
{toTimeDifference({
211-
date: hasPendingActivity.scheduledTime,
212-
negativeDefault: 'None',
213-
})}
214-
</p>
215-
</div>
216-
{/if}
217212
</div>
218213
{:else}
219214
<div class="flex flex-col gap-0 px-2 text-right">

Diff for: src/lib/components/event/pending-activity-summary-row.svelte

+11-15
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
66
import Icon from '$lib/holocene/icon/icon.svelte';
77
import Link from '$lib/holocene/link.svelte';
8+
import { translate } from '$lib/i18n/translate';
89
import type { EventGroup } from '$lib/models/event-groups/event-groups';
910
import type { PendingActivity } from '$lib/types/events';
1011
import { routeForEventHistoryEvent } from '$lib/utilities/route-for';
@@ -58,10 +59,18 @@
5859
</p>
5960
<div
6061
class="flex items-center gap-1 {event.attempt > 1 &&
61-
'surface-danger rounded px-1 py-0.5'}"
62+
'surface-retry rounded px-1 py-0.5'}"
6263
>
6364
<Icon class="mr-1.5 inline" name="retry" />
65+
{translate('workflows.retry')}
6466
{event.attempt} / {event.maximumAttempts || ''}
67+
{#if event.attempt > 1}
68+
• {translate('workflows.next-retry')}
69+
{toTimeDifference({
70+
date: event.scheduledTime,
71+
negativeDefault: 'None',
72+
})}
73+
{/if}
6574
</div>
6675
<EventDetailsRow
6776
key="activityType"
@@ -71,20 +80,7 @@
7180
/>
7281
</div></td
7382
>
74-
<td>
75-
{#if event.attempt > 1}
76-
<div class="flex items-center gap-2 px-2">
77-
<p class="max-w-fit whitespace-nowrap text-right text-xs">Next Retry</p>
78-
<p class="flex items-center gap-0">
79-
<Icon class="mr-1.5 inline" name="clock" />
80-
{toTimeDifference({
81-
date: event.scheduledTime,
82-
negativeDefault: 'None',
83-
})}
84-
</p>
85-
</div>
86-
{/if}
87-
</td>
83+
<td />
8884
</tr>
8985
{#if expanded}
9086
<tr class="row expanded">

Diff for: src/lib/components/event/pending-nexus-summary-row.svelte

+11-15
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
66
import Icon from '$lib/holocene/icon/icon.svelte';
77
import Link from '$lib/holocene/link.svelte';
8+
import { translate } from '$lib/i18n/translate';
89
import type { EventGroup } from '$lib/models/event-groups/event-groups';
910
import type { PendingNexusOperation } from '$lib/types/events';
1011
import { routeForEventHistoryEvent } from '$lib/utilities/route-for';
@@ -57,29 +58,24 @@
5758
{#if event.attempt}
5859
<div
5960
class="flex items-center gap-1 {event.attempt > 1 &&
60-
'surface-danger rounded px-1 py-0.5'}"
61+
'surface-retry rounded px-1 py-0.5'}"
6162
>
6263
<Icon class="mr-1.5 inline" name="retry" />
64+
{translate('workflows.retry')}
6365
{event.attempt}
66+
{#if event.attempt > 1}
67+
• {translate('workflows.next-retry')}
68+
{toTimeDifference({
69+
date: event.nextAttemptScheduleTime,
70+
negativeDefault: 'None',
71+
})}
72+
{/if}
6473
</div>
6574
{/if}
6675
</div>
6776
</div>
6877
</td>
69-
<td>
70-
{#if event.attempt > 1}
71-
<div class="flex items-center gap-2 px-2">
72-
<p class="max-w-fit whitespace-nowrap text-right text-xs">Next Retry</p>
73-
<p class="flex items-center gap-0">
74-
<Icon class="mr-1.5 inline" name="clock" />
75-
{toTimeDifference({
76-
date: event.nextAttemptScheduleTime,
77-
negativeDefault: 'None',
78-
})}
79-
</p>
80-
</div>
81-
{/if}
82-
</td>
78+
<td />
8379
</tr>
8480
{#if expanded}
8581
<tr class="row expanded">

Diff for: src/lib/components/lines-and-dots/constants.ts

+10-6
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ export const CategoryIcon: Record<EventTypeCategory, IconName> = {
6969
other: 'terminal',
7070
};
7171

72+
export const textBackdropOffsetWithIcon = 36;
73+
export const textBackdropOffset = 12;
74+
75+
export const getTextOffset = (radius: number) => 1.5 * radius;
76+
7277
export const timelineTextPosition = (
7378
points: number[],
7479
y: number,
@@ -90,21 +95,20 @@ export const timelineTextPosition = (
9095
if (textToLeft) textAnchor = 'end';
9196
if (textToRight) textIndex = points.indexOf(lastPoint);
9297

93-
let textX = textToRight
94-
? lastPoint + 1.5 * radius
95-
: firstPoint - 1.5 * radius;
98+
const offset = getTextOffset(radius);
99+
let textX = textToRight ? lastPoint + offset : firstPoint - offset;
96100

97101
// Pending or long events
98102
if (!textToRight && !textToLeft) {
99103
backdrop = true;
100104
textToRight = true;
101-
textX = firstPoint + 1.5 * radius;
105+
textX = firstPoint + offset;
102106

103107
if (points.length === 2 && isPending) {
104108
const gap = points[1] - points[0];
105109
if (gap < width - points[1]) {
106110
textIndex = 1;
107-
textX = points[1] + 1.5 * radius;
111+
textX = points[1] + offset;
108112
}
109113
}
110114

@@ -113,7 +117,7 @@ export const timelineTextPosition = (
113117
const gap2 = points[2] - points[1];
114118
if (gap2 > gap1) {
115119
textIndex = 1;
116-
textX = points[1] + 1.5 * radius;
120+
textX = points[1] + offset;
117121
}
118122
}
119123
}

Diff for: src/lib/components/lines-and-dots/svg/line.svelte

+1-5
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,7 @@
6565
}
6666
6767
.retry {
68-
stroke: #ff4518;
69-
}
70-
71-
.pending.retry {
72-
stroke: #ff9b70;
68+
stroke: theme('colors.red.300');
7369
}
7470
7571
.child-workflow {

Diff for: src/lib/components/lines-and-dots/svg/text.svelte

+28-8
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,18 @@
22
import type { IconName } from '$lib/holocene/icon';
33
import Icon from '$lib/holocene/icon/icon.svelte';
44
5-
import type { GraphConfig } from '../constants';
5+
import {
6+
getTextOffset,
7+
type GraphConfig,
8+
textBackdropOffset,
9+
textBackdropOffsetWithIcon,
10+
} from '../constants';
611
712
import Line from './line.svelte';
813
914
export let point: [number, number] = [0, 0];
1015
export let category: string | undefined = undefined;
16+
export let status: string | undefined = 'none';
1117
export let fontSize = '14px';
1218
export let fontWeight = '400';
1319
export let textAnchor = 'start';
@@ -16,34 +22,47 @@
1622
export let icon: IconName | undefined = undefined;
1723
export let config: GraphConfig | undefined = undefined;
1824
export let label = false;
25+
export let textWidth = 0;
26+
export let noOffset = false;
27+
export let dark = false;
1928
2029
$: [x, y] = point;
2130
2231
let textElement: SVGTextElement;
2332
2433
$: showIcon = icon && config;
2534
$: textWidth = textElement?.getBBox()?.width || 0;
26-
$: backdropWidth = showIcon ? textWidth + 36 : textWidth + 12;
35+
$: backdropWidth =
36+
showIcon && !noOffset
37+
? textWidth + textBackdropOffsetWithIcon
38+
: textWidth + textBackdropOffset;
2739
$: textX = showIcon && textAnchor === 'start' ? x + config.radius * 2 : x;
40+
$: offset = noOffset ? getTextOffset(config.radius || 0) : 0;
2841
</script>
2942

3043
{#if backdrop}
3144
<Line
3245
startPoint={[x - backdropHeight, y]}
3346
endPoint={[x + backdropWidth, y]}
34-
status="none"
47+
{status}
3548
strokeWidth={backdropHeight}
3649
/>
3750
{/if}
3851
{#if showIcon && textAnchor === 'start'}
39-
<Icon name={icon} {x} y={y - 8} class="text-white" />
52+
<Icon
53+
name={icon}
54+
x={x - offset}
55+
y={y - 8}
56+
class={dark ? 'text-black' : 'text-white'}
57+
/>
4058
{/if}
4159
<text
4260
bind:this={textElement}
4361
class="cursor-pointer select-none outline-none {category} text-primary"
4462
class:label
4563
class:backdrop
46-
x={textX}
64+
class:dark
65+
x={textX - offset}
4766
{y}
4867
font-size={fontSize}
4968
font-weight={fontWeight}
@@ -54,9 +73,9 @@
5473
{#if showIcon && textAnchor === 'end'}
5574
<Icon
5675
name={icon}
57-
x={x - textWidth - config.radius * 1.5}
76+
x={x - offset}
5877
y={y - 8}
59-
class="text-white"
78+
class={dark ? 'text-black' : 'text-white'}
6079
/>
6180
{/if}
6281

@@ -112,7 +131,8 @@
112131
fill: #ff4518;
113132
}
114133
134+
text.dark,
115135
text.none {
116-
fill: #141414;
136+
fill: theme('colors.space-black');
117137
}
118138
</style>

Diff for: src/lib/components/lines-and-dots/svg/timeline-graph-row.svelte

+27
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
<script lang="ts">
22
import type { Timestamp } from '@temporalio/common';
33
4+
import { translate } from '$lib/i18n/translate';
45
import type { EventGroup } from '$lib/models/event-groups/event-groups';
56
import { setActiveGroup } from '$lib/stores/active-events';
67
import { getMillisecondDuration } from '$lib/utilities/format-time';
8+
import { isPendingActivity } from '$lib/utilities/is-pending-activity';
79
810
import {
911
CategoryIcon,
12+
textBackdropOffsetWithIcon,
1013
TimelineConfig,
1114
timelineTextPosition,
1215
} from '../constants';
@@ -26,8 +29,12 @@
2629
2730
const { height, gutter, radius } = TimelineConfig;
2831
32+
let textWidth = 0;
33+
2934
$: timelineWidth = canvasWidth - 2 * gutter;
3035
$: active = !activeGroups.length || activeGroups.includes(group.id);
36+
$: ({ pendingActivity } = group);
37+
$: hasPendingActivity = isPendingActivity(pendingActivity);
3138
3239
const getDistancePointsAndPositions = (
3340
endTime: string | Date,
@@ -115,10 +122,30 @@
115122
{backdrop}
116123
backdropHeight={radius * 2}
117124
config={TimelineConfig}
125+
bind:textWidth
118126
>
119127
{group?.displayName}
120128
</Text>
121129
{/if}
130+
{#if hasPendingActivity && pendingActivity.attempt > 1}
131+
<Text
132+
point={[
133+
textPosition[0] + textWidth + textBackdropOffsetWithIcon,
134+
textPosition[1],
135+
]}
136+
textAnchor="start"
137+
{backdrop}
138+
backdropHeight={radius * 2}
139+
config={TimelineConfig}
140+
icon="retry"
141+
status="retry"
142+
noOffset
143+
dark
144+
>
145+
{translate('workflows.retry')}
146+
{pendingActivity.attempt} / {pendingActivity.maximumAttempts || ''}
147+
</Text>
148+
{/if}
122149
<Dot
123150
point={[x, y]}
124151
classification={group.eventList[index]?.classification}

Diff for: src/lib/i18n/locales/en/workflows.ts

+1
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ export const Strings = {
155155
'last-heartbeat': 'Last Heartbeat',
156156
attempt: 'Attempt',
157157
'attempts-left': 'Attempts Left',
158+
retry: 'Retry',
158159
'next-retry': 'Next Retry',
159160
expiration: 'Expiration',
160161
'heartbeat-details': 'Heartbeat Details',

Diff for: src/lib/theme/plugin.ts

+4
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,10 @@ const temporal = plugin(
150150
backgroundColor: css('--color-surface-black'),
151151
color: css('--color-text-white'),
152152
},
153+
'.surface-retry': {
154+
backgroundColor: colors.red[300],
155+
color: css('--color-text-black'),
156+
},
153157
});
154158
},
155159
{

0 commit comments

Comments
 (0)