Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
b3f5e95
Testing out api
Alex-Tideman Oct 10, 2025
6f1b4d2
Add worker-info component
Alex-Tideman Dec 5, 2025
2b15a34
Add worker-info to Workers tab
Alex-Tideman Dec 5, 2025
e992c50
Add worker-table if new api fails
Alex-Tideman Dec 5, 2025
1dfd237
Add workers list page
Alex-Tideman Dec 8, 2025
1eadb99
Make filterable a prop
Alex-Tideman Dec 8, 2025
96bddae
Merge branch 'main' into worker-insights
Alex-Tideman Jan 12, 2026
567aa2d
Refactor workers page to designs
Alex-Tideman Jan 12, 2026
48ef22a
New worker nav items
Alex-Tideman Feb 5, 2026
89985d6
Dont use url params
Alex-Tideman Feb 5, 2026
9e3f91f
Fix merge conflicts
Alex-Tideman Feb 5, 2026
89675ee
Merge branch 'main' into worker-insights
laurakwhit Feb 20, 2026
722bd81
Clean up types and remove Shutdown status
laurakwhit Feb 21, 2026
213c0e3
Merge branch 'main' into worker-insights
laurakwhit Feb 24, 2026
01bf37b
Merge branch 'main' into worker-insights
laurakwhit Feb 25, 2026
618b9c3
Merge branch 'main' into worker-insights
laurakwhit Feb 27, 2026
5fbbbc4
Merge branch 'main' into worker-insights
laurakwhit Mar 3, 2026
004bf4d
Small fixes and improvements
laurakwhit Mar 4, 2026
cddca19
Add filtering to workers table
laurakwhit Mar 4, 2026
cb9bda3
Update no workers polling alert
laurakwhit Mar 5, 2026
abd41a2
Worker details page updates
laurakwhit Mar 6, 2026
ddb1637
Add Go dependency warning for usage stats
laurakwhit Mar 6, 2026
feab31c
Add poll success rate
laurakwhit Mar 13, 2026
57d319b
Merge branch 'main' into worker-insights
laurakwhit Mar 13, 2026
081e4e4
Update nav to workers
laurakwhit Mar 13, 2026
b3d2ce1
Fix type and test
laurakwhit Mar 13, 2026
657db82
Routing, page, and component updates
laurakwhit Mar 14, 2026
42a6a41
UI and accesibility updates
laurakwhit Mar 16, 2026
5d18478
Hide/show alerts
laurakwhit Mar 17, 2026
3588500
Fix strict errors
laurakwhit Mar 18, 2026
871a4d9
Fix routes when switching Namespaces
laurakwhit Mar 18, 2026
9ef56c7
Small fixes
laurakwhit Mar 19, 2026
fc1d01d
Merge branch 'main' into worker-insights
laurakwhit Mar 19, 2026
38e0f61
Add useFallback prop for workers tables
laurakwhit Mar 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ go.work.sum
.claude/*
!.claude/skills/
.sisyphus/*
.omc
.omc
54 changes: 25 additions & 29 deletions src/lib/components/lines-and-dots/workflow-details.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { translate } from '$lib/i18n/translate';
import { fetchWorkflow } from '$lib/services/workflow-service';
import { isCloud } from '$lib/stores/advanced-visibility';
import { fullEventHistory } from '$lib/stores/events';
import { fullEventHistory, sdkInfo } from '$lib/stores/events';
import {
relativeTime,
timeFormat,
Expand All @@ -19,8 +19,6 @@
formatDuration,
} from '$lib/utilities/format-time';
import { getBuildIdFromVersion } from '$lib/utilities/get-deployment-build-id';
import { getSDKandVersion } from '$lib/utilities/get-sdk-version';
import { isWorkflowTaskCompletedEvent } from '$lib/utilities/is-event-type';
import {
routeForSchedule,
routeForTaskQueue,
Expand Down Expand Up @@ -81,16 +79,12 @@
],
);
let totalActions = $derived(
$fullEventHistory.reduce((acc, e) => e.billableActions + acc, 0).toString(),
$fullEventHistory
.reduce((acc, e) => (e?.billableActions ?? 0) + acc, 0)
.toString(),
);

const workflowCompletedTasks = $derived(
$fullEventHistory.filter(isWorkflowTaskCompletedEvent),
);

const { sdk, version: sdkVersion } = $derived(
getSDKandVersion(workflowCompletedTasks),
);
const { sdk, version: sdkVersion } = $derived($sdkInfo);

const fetchLatestRun = async () => {
const result = await fetchWorkflow({
Expand Down Expand Up @@ -166,18 +160,20 @@
href={routeForWorkflowsWithQuery({
namespace,
query: `WorkflowType="${workflow?.name}"`,
})}
}) ?? ''}
iconName="filter"
/>

<DetailListLabel>{translate('common.task-queue')}</DetailListLabel>
<DetailListLinkValue
text={workflow?.taskQueue}
href={routeForTaskQueue({
namespace,
queue: workflow?.taskQueue,
})}
/>
{#if workflow?.taskQueue}
<DetailListLabel>{translate('common.task-queue')}</DetailListLabel>
<DetailListLinkValue
text={workflow.taskQueue}
href={routeForTaskQueue({
namespace,
queue: workflow.taskQueue,
})}
/>
{/if}

{#if workflow?.priority}
{@const { priorityKey, fairnessKey } = workflow.priority}
Expand Down Expand Up @@ -214,11 +210,11 @@
copyableText={versioningBuildId}
text={versioningBuildId}
href={deploymentVersion
? routeForWorkflowsWithQuery({
? (routeForWorkflowsWithQuery({
namespace,
query: `TemporalWorkerDeploymentVersion="${deploymentVersion}"`,
})
: undefined}
}) ?? '')
: ''}
iconName={deploymentVersion ? 'filter' : undefined}
/>
{/if}
Expand All @@ -234,7 +230,7 @@
href={routeForWorkflowsWithQuery({
namespace,
query: `TemporalWorkflowVersioningBehavior="${versioningBehavior}"`,
})}
}) ?? ''}
iconName="filter"
/>
{/if}
Expand All @@ -252,15 +248,15 @@
})}
/>
{/if}
{#if parent}
{#if parent?.workflowId && parent?.runId}
<DetailListLabel>{translate('workflows.parent-workflow')}</DetailListLabel
>
<DetailListLinkValue
text={parent?.workflowId}
text={parent.workflowId}
href={routeForWorkflow({
namespace,
workflow: parent?.workflowId,
run: parent?.runId,
workflow: parent.workflowId,
run: parent.runId,
})}
/>
{/if}
Expand Down Expand Up @@ -305,7 +301,7 @@
{/if}

{#if sdk && sdkVersion}
<DetailListLabel>SDK</DetailListLabel>
<DetailListLabel>{translate('workflows.sdk')}</DetailListLabel>
<DetailListValue>
<SdkLogo {sdk} version={sdkVersion} />
</DetailListValue>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<script lang="ts">
import type { Writable } from 'svelte/store';

import Button from '$lib/holocene/button.svelte';
import Icon from '$lib/holocene/icon/icon.svelte';
import Tooltip from '$lib/holocene/tooltip.svelte';
import type { SearchAttributeFilter } from '$lib/models/search-attribute-filters';
import type { SearchAttributeOption } from '$lib/stores/search-attributes';

import type { StatusAttribute } from './types.ts';

import Filter from './filter.svelte';
import ManualQuery from './manual-query.svelte';

interface Props {
filters: Writable<SearchAttributeFilter[]>;
options: SearchAttributeOption[];
id: string;
statusAttribute?: StatusAttribute;
onManualSearch?: (query: string) => void;
}

let { filters, options, id, statusAttribute, onManualSearch }: Props =
$props();

let viewManualQuery = $state(false);
</script>

<div>
<div
class="flex w-full flex-wrap items-center justify-between gap-2 border border-subtle bg-primary p-1.5"
>
<div class="flex grow items-center justify-start gap-4 px-2">
<Icon name="filter-lines" class="text-primary-text h-4 w-4 shrink-0" />
<Filter {filters} {options} {id} {statusAttribute} />
</div>
<div class="flex items-center gap-1">
<Tooltip
text={viewManualQuery ? 'Hide raw query' : 'View raw query'}
left
>
<Button
variant="ghost"
size="xs"
leadingIcon="json"
active={viewManualQuery}
data-testid="toggle-manual-query"
on:click={() => (viewManualQuery = !viewManualQuery)}
/>
</Tooltip>
</div>
</div>
{#if viewManualQuery}
<ManualQuery {filters} {id} onSearch={onManualSearch} />
{/if}
</div>
131 changes: 131 additions & 0 deletions src/lib/components/shared-search-attribute-filter/filter-list.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<script lang="ts">
import type { Writable } from 'svelte/store';

import { getContext } from 'svelte';

import { page } from '$app/state';

import DropdownFilterChip from '$lib/components/workflow/filter-bar/dropdown-filter-chip.svelte';
import Button from '$lib/holocene/button.svelte';
import { translate } from '$lib/i18n/translate';
import type { SearchAttributeFilter } from '$lib/models/search-attribute-filters';
import { createFilter } from '$lib/utilities/query/to-list-workflow-filters';
import { updateQueryParamsFromFilter } from '$lib/utilities/query/to-list-workflow-filters';

import type { StatusAttribute } from './types.ts';

import {
SEARCH_ATTRIBUTE_FILTER_CONTEXT,
type SearchAttributeFilterContext,
} from './filter.svelte';
import StatusFilterChip from './status-filter-chip.svelte';

interface Props {
filters: Writable<SearchAttributeFilter[]>;
statusAttribute?: StatusAttribute;
}

let { filters, statusAttribute }: Props = $props();

const isStatusFilter = (f: SearchAttributeFilter) =>
statusAttribute ? f.attribute === statusAttribute : false;

const { filter, activeQueryIndex, chipOpenIndex } =
getContext<SearchAttributeFilterContext>(SEARCH_ATTRIBUTE_FILTER_CONTEXT);

let totalFiltersInView = $state(10);

const firstExecutionStatusIndex = $derived(
$filters.findIndex((filter) => isStatusFilter(filter)),
);
const visibleFilters = $derived($filters.slice(0, totalFiltersInView));
const statusFilters = $derived(
statusAttribute
? $filters.filter((filter) => filter.attribute === statusAttribute)
: [],
);
const nonStatusFilters = $derived(
$filters.filter((filter) => !isStatusFilter(filter)),
);
const hasMoreFilters = $derived(totalFiltersInView < $filters.length);

function updateFilter(index: number, updatedFilter: SearchAttributeFilter) {
const next = [...$filters];
next[index] = updatedFilter;
$filters = next;
updateQueryParamsFromFilter(page.url, $filters);
}

function updateStatusFilters(
index: number,
updatedFilters: SearchAttributeFilter[],
) {
if (updatedFilters.length === 0) {
$filters = nonStatusFilters;
updateQueryParamsFromFilter(page.url, $filters);
} else {
const next = [...$filters];
next.splice(index, statusFilters.length, ...updatedFilters);
$filters = next;
updateQueryParamsFromFilter(page.url, $filters);
}
}

function removeFilter(index: number) {
const next = [...$filters];
next.splice(index, 1);
$filters = next;
updateQueryParamsFromFilter(page.url, $filters);

if (index === $filters.length && $filters.length > 0) {
const previousQuery = $filters[$filters.length - 1];
if (previousQuery) {
previousQuery.operator = '';
}
}

if (index === $activeQueryIndex) {
$activeQueryIndex = null;
$filter = createFilter();
} else if ($activeQueryIndex !== null && index < $activeQueryIndex) {
$activeQueryIndex -= 1;
}
}

function viewMoreFilters() {
if (hasMoreFilters) {
totalFiltersInView += 10;
}
}
</script>

{#if visibleFilters.length > 0}
<div class="flex flex-wrap items-center gap-2">
{#each visibleFilters as filterItem, i (filterItem.id)}
{#if statusAttribute && isStatusFilter(filterItem) && i === firstExecutionStatusIndex}
<StatusFilterChip
attribute={statusAttribute}
filters={statusFilters}
index={i}
openIndex={$chipOpenIndex}
onUpdate={(updatedStatusFilters) =>
updateStatusFilters(i, updatedStatusFilters)}
/>
{:else if !isStatusFilter(filterItem) && filterItem.attribute}
<DropdownFilterChip
filter={filterItem}
index={i}
openIndex={$chipOpenIndex}
onUpdate={(updatedFilter) => updateFilter(i, updatedFilter)}
onRemove={() => removeFilter(i)}
/>
{/if}
{/each}

{#if hasMoreFilters}
<Button variant="secondary" size="xs" on:click={viewMoreFilters}>
{translate('common.view-more')}
</Button>
{/if}
</div>
{/if}
Loading
Loading