Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@
import { goto } from '$app/navigation';
import { page } from '$app/state';

import SearchAttributeValue from '$lib/components/table/search-attribute-value.svelte';
import Timestamp from '$lib/components/timestamp.svelte';
import WorkflowStatusBadge from '$lib/components/workflow/workflow-status-badge.svelte';
import Link from '$lib/holocene/link.svelte';
import TableCellWithFilterOrCopyButtons from '$lib/holocene/table/table-cell-with-filter-or-copy-buttons.svelte';
import { translate } from '$lib/i18n/translate';
import type { ConfigurableTableHeader } from '$lib/stores/configurable-table-columns';
import { customSearchAttributes } from '$lib/stores/search-attributes';
import {
COLUMN_WIDTH_CLAMP_CLASSES,
columnWidthStyle,
} from '$lib/utilities/column-width';
import { parsePayloadAttributes } from '$lib/utilities/decode-payload';
import { parseSearchAttributes } from '$lib/utilities/decode-payload';
import {
routeForSchedule,
routeForWorkflow,
Expand All @@ -36,9 +38,8 @@

const status = $derived(schedule?.info?.paused ? 'Paused' : 'Running');
const spec = $derived(schedule?.info?.spec);
const searchAttributes = $derived(schedule?.searchAttributes ?? {});
const decodedAttributes = $derived(
parsePayloadAttributes({ searchAttributes }),
const decodedSearchAttributes = $derived(
parseSearchAttributes(schedule?.searchAttributes ?? {}).indexedFields ?? {},
);

const sortRecentActions = (recentActions: ScheduleActionResult[]) => {
Expand Down Expand Up @@ -142,7 +143,12 @@
</td>
{:else}
<td class={twMerge('cell', clampToWidth)} style={widthStyle}>
{decodedAttributes?.searchAttributes?.indexedFields?.[label] ?? ''}
{#if decodedSearchAttributes[label] !== undefined}
<SearchAttributeValue
value={decodedSearchAttributes[label]}
type={$customSearchAttributes[label]}
/>
{/if}
</td>
{/if}
{/each}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
resizeColumn,
TABLE_TYPE,
} from '$lib/stores/configurable-table-columns';
import type { ActivityExecutionInfo } from '$lib/types/activity-execution';
import type { ActivityExecutionListInfo } from '$lib/types/activity-execution';
import {
getBatchSelectionTargets,
getPageSelectionStatus,
Expand Down Expand Up @@ -58,7 +58,7 @@
standaloneActivityBulkActionsEnabled(page) && $supportsAdvancedVisibility,
);

let visiblePaginatedItems: ActivityExecutionInfo[] = $state([]);
let visiblePaginatedItems: ActivityExecutionListInfo[] = $state([]);

const pageSelectionStatus: PageSelectionStatus = $derived(
getPageSelectionStatus(
Expand All @@ -72,7 +72,7 @@

const handleSelectPage = (
isSelected: boolean,
activities: ActivityExecutionInfo[],
activities: ActivityExecutionListInfo[],
) => {
selectActivities(isSelected, activities);
prevClickedIndex = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
type ActivityBatchOperationContext,
} from '$lib/pages/standalone-activities.svelte';
import { activitiesQuery, activityCount } from '$lib/stores/activities';
import type { ActivityExecutionInfo } from '$lib/types/activity-execution';
import type { ActivityExecutionListInfo } from '$lib/types/activity-execution';
import { standaloneActivityBulkActionsEnabled } from '$lib/utilities/standalone-activities-commands-disabled';

type Props = {
activities: ActivityExecutionInfo[];
activities: ActivityExecutionListInfo[];
};

let { activities }: Props = $props();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@
import { page } from '$app/state';

import ActivityStatusBadge from '$lib/components/standalone-activities/activity-status-badge.svelte';
import SearchAttributeValue from '$lib/components/table/search-attribute-value.svelte';
import Timestamp from '$lib/components/timestamp.svelte';
import type { ConfigurableTableHeader } from '$lib/stores/configurable-table-columns';
import type { ActivityExecutionInfo } from '$lib/types/activity-execution';
import {
customSearchAttributes,
isFilterableCustomSearchAttribute,
} from '$lib/stores/search-attributes';
import type { ActivityExecutionListInfo } from '$lib/types/activity-execution';
import {
COLUMN_WIDTH_CLAMP_CLASSES,
columnWidthStyle,
Expand All @@ -21,7 +26,7 @@

type Props = {
column: ConfigurableTableHeader;
activity: ActivityExecutionInfo;
activity: ActivityExecutionListInfo;
};
let { column, activity }: Props = $props();

Expand All @@ -30,6 +35,17 @@

const filterableLabels = ['Activity ID', 'Run ID', 'Type', 'Task Queue'];

const customSearchAttributeValue = $derived(
activity.searchAttributes?.indexedFields?.[label],
);
const isCustomKeywordOrTextAttribute = $derived(
isFilterableCustomSearchAttribute(
$customSearchAttributes,
label,
customSearchAttributeValue,
),
);

const className = $derived(
twMerge(
'h-8 whitespace-nowrap',
Expand All @@ -43,7 +59,7 @@
{#snippet renderFilterableTableCell(
filterableCellProps: Pick<
ComponentProps<typeof FilterableTableCell>,
'attribute' | 'value' | 'href'
'attribute' | 'value' | 'href' | 'type'
>,
)}
<FilterableTableCell
Expand All @@ -54,7 +70,7 @@
/>
{/snippet}

{#if filterableLabels.includes(label)}
{#if filterableLabels.includes(label) || isCustomKeywordOrTextAttribute}
{#if label === 'Activity ID'}
{@render renderFilterableTableCell({
attribute: 'ActivityId',
Expand Down Expand Up @@ -85,6 +101,12 @@
attribute: 'TaskQueue',
value: activity.taskQueue ?? '',
})}
{:else if isCustomKeywordOrTextAttribute}
{@render renderFilterableTableCell({
attribute: label,
value: customSearchAttributeValue ?? '',
type: $customSearchAttributes[label],
})}
{/if}
{:else}
<td class={className} style={widthStyle} data-testid={testId}>
Expand All @@ -105,6 +127,11 @@
{activity.stateTransitionCount ?? ''}
{:else if label === 'Execution Time'}
<Timestamp dateTime={activity.executionTime} />
{:else if label in $customSearchAttributes && customSearchAttributeValue !== undefined}
<SearchAttributeValue
value={customSearchAttributeValue}
type={$customSearchAttributes[label]}
/>
{/if}
</td>
{/if}
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@
ACTIVITY_BATCH_OPERATION_CONTEXT,
type ActivityBatchOperationContext,
} from '$lib/pages/standalone-activities.svelte';
import type { ActivityExecutionInfo } from '$lib/types/activity-execution';
import type { ActivityExecutionListInfo } from '$lib/types/activity-execution';
import type { PageSelectionStatus } from '$lib/utilities/batch-selection';

import BatchActions from './batch-actions.svelte';

type Props = {
activities?: ActivityExecutionInfo[];
activities?: ActivityExecutionListInfo[];
empty?: boolean;
columnsCount?: number;
showBatchActions?: boolean;
pageSelectionStatus?: PageSelectionStatus;
onSelectPage?: (
selected: boolean,
activities: ActivityExecutionInfo[],
activities: ActivityExecutionListInfo[],
) => void;
children?: Snippet;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
type ActivityBatchOperationContext,
activityKey,
} from '$lib/pages/standalone-activities.svelte';
import type { ActivityExecutionInfo } from '$lib/types/activity-execution';
import type { ActivityExecutionListInfo } from '$lib/types/activity-execution';
import { standaloneActivityWriteActionsDisabled } from '$lib/utilities/standalone-activities-commands-disabled';

interface Props {
activity?: ActivityExecutionInfo;
activity?: ActivityExecutionListInfo;
empty?: boolean;
showBatchActions?: boolean;
onClickBatchSelect?: (e: MouseEvent) => void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@

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

import SearchAttributeValue from '$lib/components/table/search-attribute-value.svelte';
import Timestamp from '$lib/components/timestamp.svelte';
import type { ConfigurableTableHeader } from '$lib/stores/configurable-table-columns';
import {
customSearchAttributes,
isFilterableCustomSearchAttribute,
} from '$lib/stores/search-attributes';
import type { NexusOperationExecutionListInfo } from '$lib/types/nexus-operation-execution';
import {
COLUMN_WIDTH_CLAMP_CLASSES,
Expand Down Expand Up @@ -51,9 +56,20 @@
'Service',
'Operation',
];

const customSearchAttributeValue = $derived(
operation.searchAttributes?.indexedFields?.[label],
);
const isCustomKeywordOrTextAttribute = $derived(
isFilterableCustomSearchAttribute(
$customSearchAttributes,
label,
customSearchAttributeValue,
),
);
</script>

{#if filterableLabels.includes(label)}
{#if filterableLabels.includes(label) || isCustomKeywordOrTextAttribute}
<td
class={twMerge('relative h-8 whitespace-nowrap pr-24', clampToWidth)}
style={widthStyle}
Expand Down Expand Up @@ -105,6 +121,13 @@
attribute="Operation"
value={operation.operation ?? ''}
/>
{:else if isCustomKeywordOrTextAttribute}
<FilterableTableCell
{filterOrCopyButtonsVisible}
attribute={label}
value={customSearchAttributeValue ?? ''}
type={$customSearchAttributes[label]}
/>
{/if}
</td>
{:else}
Expand All @@ -129,6 +152,11 @@
{/if}
{:else if label === 'State Transitions'}
{operation.stateTransitionCount ?? ''}
{:else if label in $customSearchAttributes && customSearchAttributeValue !== undefined}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What was the root issue here since I thought all filterable tables shared the same components?

They share the wrapper of the table, but not the code that turns a row into cells. The custom search attribute support lived in the part that isn't shared.

What was different about the SAA SAs from workflow SAs?

The workflows list has a conversion layer that decodes search attributes along with the rest of each row. The standalone activities list used the API response directly and its table cell never had the custom search attribute logic like the workflows did.

Do we need to check SANO / Schedule SAs?

Yup, updated in c1c352d.

<SearchAttributeValue
value={customSearchAttributeValue}
type={$customSearchAttributes[label]}
/>
{/if}
</td>
{/if}
46 changes: 46 additions & 0 deletions src/lib/components/table/search-attribute-value.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<script lang="ts">
import Timestamp from '$lib/components/timestamp.svelte';
import Tooltip from '$lib/holocene/tooltip.svelte';
import { Badge } from '$lib/io/badge';
import {
SEARCH_ATTRIBUTE_TYPE,
type SearchAttributeType,
} from '$lib/types/workflows';
import {
TRUNCATE_LENGTH,
truncateValue,
} from '$lib/utilities/truncate-value';

type Props = {
value: unknown;
type?: SearchAttributeType;
truncate?: boolean;
};

let { value, type, truncate = false }: Props = $props();

const displayValue = $derived(
Array.isArray(value) ? value.join(', ') : String(value),
);
const hideTooltip = $derived(
!truncate || truncateValue(displayValue).length <= TRUNCATE_LENGTH,
);
</script>

{#if value != null}
{#if type === SEARCH_ATTRIBUTE_TYPE.DATETIME && typeof value === 'string'}
<Timestamp dateTime={value} />
{:else if type === SEARCH_ATTRIBUTE_TYPE.BOOL}
<Badge text={displayValue} />
{:else}
<Tooltip
usePortal
text={displayValue}
top
class="min-w-0"
hide={hideTooltip}
>
{truncate ? truncateValue(displayValue) : displayValue}
</Tooltip>
{/if}
{/if}
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,17 @@

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

import SearchAttributeValue from '$lib/components/table/search-attribute-value.svelte';
import Timestamp from '$lib/components/timestamp.svelte';
import WorkflowStatusBadge from '$lib/components/workflow/workflow-status-badge.svelte';
import Tooltip from '$lib/holocene/tooltip.svelte';
import { Badge } from '$lib/io/badge';
import type { ConfigurableTableHeader } from '$lib/stores/configurable-table-columns';
import {
customSearchAttributes,
isCustomSearchAttribute,
isFilterableCustomSearchAttribute,
workflowIncludesSearchAttribute,
} from '$lib/stores/search-attributes';
import {
SEARCH_ATTRIBUTE_TYPE,
type WorkflowExecution,
} from '$lib/types/workflows';
import type { WorkflowExecution } from '$lib/types/workflows';
import {
COLUMN_WIDTH_CLAMP_CLASSES,
columnWidthStyle,
Expand Down Expand Up @@ -54,10 +51,11 @@
const { label, width } = $derived(column);
const namespace = $derived(page.params.namespace);
const isCustomKeywordOrTextAttribute = $derived(
isCustomSearchAttribute(label) &&
($customSearchAttributes[label] === SEARCH_ATTRIBUTE_TYPE.KEYWORD ||
$customSearchAttributes[label] === SEARCH_ATTRIBUTE_TYPE.TEXT) &&
typeof workflow.searchAttributes?.indexedFields?.[label] === 'string',
isFilterableCustomSearchAttribute(
$customSearchAttributes,
label,
workflow.searchAttributes?.indexedFields?.[label],
),
);

const filterableLabels = [
Expand Down Expand Up @@ -245,23 +243,12 @@
{/if}
{:else if label === 'Change Version'}
{workflow.searchAttributes?.indexedFields?.TemporalChangeVersion}
{:else if isCustomSearchAttribute(label) && workflowIncludesSearchAttribute(workflow, label)}
{@const content = workflow.searchAttributes?.indexedFields?.[label]}
{#if $customSearchAttributes[label] === SEARCH_ATTRIBUTE_TYPE.DATETIME && typeof content === 'string'}
<Timestamp dateTime={content} />
{:else if $customSearchAttributes[label] === SEARCH_ATTRIBUTE_TYPE.BOOL}
<Badge text={content ?? ''} />
{:else}
<Tooltip
usePortal
text={content}
top
class="min-w-0"
hide={hideTooltip(content)}
>
{truncate ? truncateValue(content) : content}
</Tooltip>
{/if}
{:else if label in $customSearchAttributes && workflowIncludesSearchAttribute(workflow, label)}
<SearchAttributeValue
value={workflow.searchAttributes?.indexedFields?.[label]}
type={$customSearchAttributes[label]}
{truncate}
/>
{/if}
</td>
{/if}
Loading
Loading