Skip to content

Commit e4ee30f

Browse files
User metadata (#2796)
* Svelte5 * Add search-attributes tab, memo tab, user metadata tab * add tests * rm console logs * rm inspect * rm unused mock data * add typing to overrideTheme * center divs * add metadata-tab to translate * add markdown override-theme to go * add banner * rm consols * update event display, fix markdown, make summary and details and current details reach bottom when event not displayed * quick tweak to view height * fix errors caused by runes mode/legacy mismatch * add summary back in and remove event id * Do some refactoring, move events into the left side * move memo into component * mv search attr into component * updates from feedback * put usermetadata into single component * clean up metadata events * rm ('Metadata groups:', metadataGroups); * Style updates * Move tab * standardize the time stamp with other time formats --------- Co-authored-by: Your Name <[email protected]>
1 parent cef7bed commit e4ee30f

File tree

23 files changed

+614
-125
lines changed

23 files changed

+614
-125
lines changed

server/server/route/ui.go

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,12 @@ func SetRenderRoute(e *echo.Echo, publicPath string) {
161161
e.GET(renderPath, func(c echo.Context) error {
162162
content := c.QueryParam("content")
163163
theme := c.QueryParam("theme")
164+
overrideTheme := c.QueryParam("overrideTheme")
165+
166+
finalTheme := theme
167+
if theme != "" && overrideTheme != "" {
168+
finalTheme = fmt.Sprintf("%s-%s", theme, overrideTheme)
169+
}
164170

165171
// Process markdown to HTML
166172
renderedHTML := processMarkdown(content)
@@ -175,7 +181,7 @@ func SetRenderRoute(e *echo.Echo, publicPath string) {
175181
}{
176182
Content: template.HTML(renderedHTML),
177183
Nonce: nonce,
178-
Theme: theme,
184+
Theme: finalTheme,
179185
CSS: `*,
180186
body {
181187
margin: 0;
@@ -311,7 +317,43 @@ func SetRenderRoute(e *echo.Echo, publicPath string) {
311317
312318
body[data-theme='dark'] a {
313319
color: #8098f9;
314-
}`,
320+
}
321+
322+
body[data-theme='light-background'] {
323+
background-color: #f8fafc;
324+
color: #121416;
325+
}
326+
327+
body[data-theme='light-background']a {
328+
color: #444ce7;
329+
}
330+
331+
body[data-theme='dark-background'] {
332+
background-color: #141414;
333+
color: #f8fafc;
334+
}
335+
body[data-theme='dark-background'] a {
336+
color: #8098f9;
337+
}
338+
339+
body[data-theme='light-primary'] {
340+
background-color: #fff;
341+
color: #121416;
342+
}
343+
body[data-theme='light-primary']a {
344+
color: #444ce7;
345+
}
346+
347+
body[data-theme='dark-primary'] {
348+
background-color: #000;
349+
color: #f8fafc;
350+
}
351+
352+
body[data-theme='dark-primary']a {
353+
color: #8098f9;
354+
}
355+
`,
356+
315357
}
316358

317359
// Set headers

src/lib/components/bottom-nav-links.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
{#if open}
1010
<div
11-
class="flex h-full flex-col flex-col-reverse justify-start gap-6 overflow-auto px-4 py-8"
11+
class="flex h-full flex-col-reverse justify-start gap-6 overflow-auto px-4 py-8"
1212
>
1313
{#each linkList as item}
1414
{#if item.divider}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<script lang="ts">
2+
import MetadataDecoder from '$lib/components/event/metadata-decoder.svelte';
3+
import WorkflowStatus from '$lib/components/workflow-status.svelte';
4+
import { translate } from '$lib/i18n/translate';
5+
import { groupEvents } from '$lib/models/event-groups';
6+
import { fullEventHistory } from '$lib/stores/events';
7+
import { workflowRun } from '$lib/stores/workflow-run';
8+
const { workflow } = $derived($workflowRun);
9+
const metadataGroups = $derived(
10+
groupEvents(
11+
$fullEventHistory,
12+
'ascending',
13+
workflow?.pendingActivities,
14+
workflow?.pendingNexusOperations,
15+
).filter((group) => group.userMetadata?.summary),
16+
);
17+
</script>
18+
19+
{#if !metadataGroups.length}
20+
<div class="px-6 text-secondary/70">
21+
<p class="text-sm italic">No events with metadata</p>
22+
</div>
23+
{/if}
24+
<div class="flex flex-col gap-2">
25+
{#each metadataGroups as group}
26+
<div
27+
class="flex items-center justify-between gap-4 border-b border-subtle px-3 pb-1 text-lg"
28+
>
29+
<div class="flex items-center gap-2">
30+
<p class="w-32 min-w-32 text-sm font-medium">{group.label}</p>
31+
<MetadataDecoder
32+
value={group.userMetadata.summary}
33+
fallback={translate('events.decode-failed')}
34+
let:decodedValue
35+
>
36+
<span class="text-sm">{decodedValue}</span>
37+
</MetadataDecoder>
38+
</div>
39+
<WorkflowStatus status={group.finalClassification} />
40+
</div>
41+
{/each}
42+
</div>
Lines changed: 63 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,31 @@
11
<script lang="ts">
2-
import { page } from '$app/stores';
2+
import { onMount } from 'svelte';
3+
4+
import { page } from '$app/state';
35
4-
import AccordionLight from '$lib/holocene/accordion/accordion-light.svelte';
56
import Button from '$lib/holocene/button.svelte';
67
import Icon from '$lib/holocene/icon/icon.svelte';
78
import Markdown from '$lib/holocene/monaco/markdown.svelte';
8-
import Tooltip from '$lib/holocene/tooltip.svelte';
99
import { translate } from '$lib/i18n/translate';
1010
import { getWorkflowMetadata } from '$lib/services/query-service';
1111
import { authUser } from '$lib/stores/auth-user';
12+
import { relativeTime, timeFormat } from '$lib/stores/time-format';
1213
import { workflowRun } from '$lib/stores/workflow-run';
14+
import { formatDate } from '$lib/utilities/format-date';
15+
16+
const { namespace } = $derived(page.params);
17+
const { workflow } = $derived($workflowRun);
1318
14-
$: ({ namespace } = $page.params);
15-
$: ({ workflow } = $workflowRun);
16-
$: currentDetails = $workflowRun?.metadata?.currentDetails || '';
17-
$: closedWithoutDetails = !workflow.isRunning && !currentDetails;
19+
const currentDetails = $derived($workflowRun?.metadata?.currentDetails || '');
1820
19-
let loading = false;
21+
let loading = $state(false);
22+
let lastFetched = $state<Date | null>(null);
2023
2124
const fetchCurrentDetails = async () => {
2225
if (loading) return;
2326
loading = true;
2427
try {
25-
const { settings } = $page.data;
28+
const { settings } = page.data;
2629
const metadata = await getWorkflowMetadata(
2730
{
2831
namespace,
@@ -35,40 +38,65 @@
3538
$authUser?.accessToken,
3639
);
3740
$workflowRun.metadata = metadata;
41+
lastFetched = new Date();
3842
} catch (error) {
3943
console.error('Error fetching current details:', error);
4044
} finally {
4145
loading = false;
4246
}
4347
};
48+
49+
onMount(() => {
50+
fetchCurrentDetails();
51+
});
52+
const handleKeydown = (event) => {
53+
if (event.key === 'r' || event.key === 'R') {
54+
event.preventDefault();
55+
fetchCurrentDetails();
56+
}
57+
};
4458
</script>
4559

46-
<AccordionLight
47-
let:open
48-
onToggle={closedWithoutDetails ? fetchCurrentDetails : undefined}
49-
icon={closedWithoutDetails ? 'retry' : undefined}
50-
>
51-
<div slot="title" class="flex w-full items-center gap-2 p-2 text-xl">
52-
<Icon name="flag" class="text-brand" width={32} height={32} />
53-
{translate('workflows.current-details')}
54-
{#if loading}{translate('common.loading')}{/if}
60+
<svelte:window onkeydown={handleKeydown} />
61+
62+
<div class="flex h-full flex-1 flex-col border-l border-subtle">
63+
<div class="surface-information w-full px-6 py-2">
64+
<div class="flex items-center justify-between">
65+
<h3>{translate('workflows.current-details')}</h3>
66+
<div class="flex flex-row items-center gap-2 lg:flex-col xl:flex-row">
67+
<p class="hidden sm:block">
68+
Press the <span
69+
class="mx-1 rounded bg-subtle px-1 text-sm font-medium leading-4"
70+
>R</span
71+
> for freshness
72+
</p>
73+
<div class="flex items-center sm:hidden">
74+
<p>Press for freshness</p>
75+
<Button
76+
variant="ghost"
77+
on:click={fetchCurrentDetails}
78+
disabled={loading}
79+
>
80+
<Icon name="retry" />
81+
</Button>
82+
</div>
83+
{#if lastFetched}
84+
<p class="text-xs text-secondary">
85+
{formatDate(lastFetched, $timeFormat, {
86+
relative: $relativeTime,
87+
})}
88+
</p>
89+
{/if}
90+
</div>
91+
</div>
5592
</div>
56-
{#if open}
93+
<div class="surface-background h-full">
5794
{#key currentDetails}
58-
<Markdown content={currentDetails} />
95+
<Markdown
96+
className="p-3"
97+
overrideTheme="background"
98+
content={currentDetails}
99+
/>
59100
{/key}
60-
{/if}
61-
<svelte:fragment slot="action">
62-
{#if workflow.isRunning}
63-
<Tooltip text={translate('workflows.update-details')} left>
64-
<Button
65-
variant="ghost"
66-
on:click={fetchCurrentDetails}
67-
disabled={loading}
68-
>
69-
<Icon name="retry" />
70-
</Button>
71-
</Tooltip>
72-
{/if}
73-
</svelte:fragment>
74-
</AccordionLight>
101+
</div>
102+
</div>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<script lang="ts">
2+
import PayloadDecoder from '$lib/components/event/payload-decoder.svelte';
3+
import CodeBlock from '$lib/holocene/code-block.svelte';
4+
import { translate } from '$lib/i18n/translate';
5+
import { workflowRun } from '$lib/stores/workflow-run';
6+
7+
const { workflow } = $derived($workflowRun);
8+
</script>
9+
10+
<div class="mt-4 flex flex-col gap-2">
11+
<h3>{translate('common.memo')}</h3>
12+
{#if workflow?.memo}
13+
<PayloadDecoder value={{ memo: workflow.memo }} key="memo">
14+
{#snippet children(decodedValue)}
15+
<CodeBlock
16+
content={decodedValue}
17+
copyIconTitle={translate('common.copy-icon-title')}
18+
copySuccessIconTitle={translate('common.copy-success-icon-title')}
19+
/>
20+
{/snippet}
21+
</PayloadDecoder>
22+
{:else}
23+
<p>{translate('events.empty-memo-attributes')}</p>
24+
{/if}
25+
</div>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<script lang="ts">
2+
import PayloadDecoder from '$lib/components/event/payload-decoder.svelte';
3+
import CodeBlock from '$lib/holocene/code-block.svelte';
4+
import { translate } from '$lib/i18n/translate';
5+
import { workflowRun } from '$lib/stores/workflow-run';
6+
7+
const { workflow } = $derived($workflowRun);
8+
</script>
9+
10+
<div class="mt-4 flex flex-col gap-2">
11+
<h3>
12+
{translate('events.attribute-group.search-attributes')}
13+
</h3>
14+
{#if workflow?.searchAttributes}
15+
<PayloadDecoder
16+
value={{ searchAttributes: workflow.searchAttributes }}
17+
key="searchAttributes"
18+
>
19+
{#snippet children(decodedValue)}
20+
<CodeBlock
21+
content={decodedValue}
22+
copyIconTitle={translate('common.copy-icon-title')}
23+
copySuccessIconTitle={translate('common.copy-success-icon-title')}
24+
/>
25+
{/snippet}
26+
</PayloadDecoder>
27+
{:else}
28+
<p>{translate('events.empty-search-attributes')}</p>
29+
{/if}
30+
</div>
Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,53 @@
11
<script lang="ts">
2-
import AccordionLight from '$lib/holocene/accordion/accordion-light.svelte';
3-
import Icon from '$lib/holocene/icon/icon.svelte';
42
import Markdown from '$lib/holocene/monaco/markdown.svelte';
53
import { translate } from '$lib/i18n/translate';
64
import { workflowRun } from '$lib/stores/workflow-run';
75
8-
$: summary = $workflowRun?.userMetadata?.summary;
9-
$: details = $workflowRun?.userMetadata?.details;
10-
$: hasUserMetadata = summary || details;
6+
import MetadataEvents from './metadata-events.svelte';
7+
8+
const summary = $derived($workflowRun?.userMetadata?.summary);
9+
const details = $derived($workflowRun?.userMetadata?.details);
1110
</script>
1211

13-
{#if hasUserMetadata}
14-
<AccordionLight let:open>
15-
<div slot="title" class="flex w-full items-center gap-2 p-2 text-xl">
16-
<Icon name="info" class="text-brand" width={32} height={32} />{translate(
17-
'workflows.summary-and-details',
18-
)}
12+
<div class="flex h-full flex-1 flex-col bg-primary">
13+
<div>
14+
<div class="surface-information w-full px-6 py-2">
15+
<h3 data-testid="user-metadata-summary-heading">
16+
{translate('workflows.summary')}
17+
</h3>
1918
</div>
20-
{#if open && summary}
21-
<h3>{translate('workflows.summary')}</h3>
22-
<Markdown content={summary} />
19+
{#if summary}
20+
<Markdown className="p-3" overrideTheme="primary" content={summary} />
21+
{:else}
22+
<div class="py-6 pl-6 text-secondary/70">
23+
<p class="text-sm italic">
24+
{translate('workflows.no-summary-available')}
25+
</p>
26+
</div>
2327
{/if}
24-
{#if open && details}
25-
<h3>{translate('workflows.details')}</h3>
26-
<Markdown content={details} />
28+
</div>
29+
<div>
30+
<div class="surface-information w-full px-6 py-2">
31+
<h3 data-testid="user-metadata-summary-heading">
32+
{translate('workflows.details')}
33+
</h3>
34+
</div>
35+
{#if details}
36+
<Markdown className="p-3" overrideTheme="primary" content={details} />
37+
{:else}
38+
<div class="py-6 pl-6 text-secondary/70">
39+
<p class="text-sm italic">
40+
{translate('workflows.no-details-available')}
41+
</p>
42+
</div>
2743
{/if}
28-
</AccordionLight>
29-
{/if}
44+
</div>
45+
<div>
46+
<div class="surface-information w-full px-6 py-2">
47+
<h3 data-testid="user-metadata-summary-heading">Events with Metadata</h3>
48+
</div>
49+
<div class="py-6">
50+
<MetadataEvents />
51+
</div>
52+
</div>
53+
</div>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script>
2+
import WorkflowCurrentDetails from '$lib/components/workflow/metadata/workflow-current-details.svelte';
3+
import WorkflowSummaryAndDetails from '$lib/components/workflow/metadata/workflow-summary-and-details.svelte';
4+
</script>
5+
6+
<div class="flex flex-col bg-primary lg:flex-row">
7+
<WorkflowSummaryAndDetails />
8+
<WorkflowCurrentDetails />
9+
</div>

0 commit comments

Comments
 (0)