diff --git a/server/go.sum b/server/go.sum index 4d644ed0fc..30e0b1f093 100644 --- a/server/go.sum +++ b/server/go.sum @@ -18,8 +18,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= -github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-jose/go-jose/v4 v4.1.6 h1:sNRkftxFJxwsILiQukqp5z83QBSmntVwmgRY7AqBDO4= github.com/go-jose/go-jose/v4 v4.1.6/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= diff --git a/src/lib/components/batch-operations/empty-state.svelte b/src/lib/components/batch-operations/empty-state.svelte new file mode 100644 index 0000000000..55b42b4df4 --- /dev/null +++ b/src/lib/components/batch-operations/empty-state.svelte @@ -0,0 +1,24 @@ + + + + {#snippet description()} +

{translate('batch.empty-state-description')}

+

+ {translate('batch.empty-state-read-docs-prefix')} + + {translate('batch.empty-state-docs-link')} + + {translate('batch.empty-state-read-docs-suffix')} +

+ {/snippet} +
diff --git a/src/lib/components/batch-operations/table.svelte b/src/lib/components/batch-operations/table.svelte index 034cb30590..67ff362bd6 100644 --- a/src/lib/components/batch-operations/table.svelte +++ b/src/lib/components/batch-operations/table.svelte @@ -2,7 +2,6 @@ import BatchOperationStatusBadge from '$lib/components/batch-operations/batch-operation-status-badge.svelte'; import OperationType from '$lib/components/batch-operations/operation-type.svelte'; import Timestamp from '$lib/components/timestamp.svelte'; - import EmptyState from '$lib/holocene/empty-state.svelte'; import Link from '$lib/holocene/link.svelte'; import TableHeaderRow from '$lib/holocene/table/table-header-row.svelte'; import TableRow from '$lib/holocene/table/table-row.svelte'; @@ -51,11 +50,5 @@ - {:else} - - - - - {/each} diff --git a/src/lib/components/deployments/deployments-empty-state.svelte b/src/lib/components/deployments/deployments-empty-state.svelte index 6d574999f6..3fe15e7f66 100644 --- a/src/lib/components/deployments/deployments-empty-state.svelte +++ b/src/lib/components/deployments/deployments-empty-state.svelte @@ -1,12 +1,8 @@ -
- -
-

- {translate('deployments.empty-state-title')} -

-

- {translate('deployments.empty-state-description')} -

-
-
+ + {#snippet actions()} {#if canCreateServerlessDeployment} -
- {#if error} - - {error} - - {/if} -
+ {/snippet} + diff --git a/src/lib/components/empty-states/empty-state-transition.svelte b/src/lib/components/empty-states/empty-state-transition.svelte new file mode 100644 index 0000000000..0a878a4e31 --- /dev/null +++ b/src/lib/components/empty-states/empty-state-transition.svelte @@ -0,0 +1,36 @@ + + +
+ {#if empty} +
+ {@render emptyState()} +
+ {:else} +
+ {@render children()} +
+ {/if} +
diff --git a/src/lib/components/empty-states/nexus-illustration.svelte b/src/lib/components/empty-states/nexus-illustration.svelte new file mode 100644 index 0000000000..d156147f4a --- /dev/null +++ b/src/lib/components/empty-states/nexus-illustration.svelte @@ -0,0 +1,353 @@ + + + + + diff --git a/src/lib/components/empty-states/placeholder-cards.svelte b/src/lib/components/empty-states/placeholder-cards.svelte new file mode 100644 index 0000000000..9fcb717524 --- /dev/null +++ b/src/lib/components/empty-states/placeholder-cards.svelte @@ -0,0 +1,41 @@ + + + diff --git a/src/lib/components/empty-states/resource-empty-state.stories.svelte b/src/lib/components/empty-states/resource-empty-state.stories.svelte new file mode 100644 index 0000000000..a868c669e2 --- /dev/null +++ b/src/lib/components/empty-states/resource-empty-state.stories.svelte @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/lib/components/empty-states/resource-empty-state.svelte b/src/lib/components/empty-states/resource-empty-state.svelte new file mode 100644 index 0000000000..fe7703b577 --- /dev/null +++ b/src/lib/components/empty-states/resource-empty-state.svelte @@ -0,0 +1,144 @@ + + +
+ {#if illustration && !error} + + {:else} + {#if showPlaceholder} + + {/if} + + {/if} + +
+

{title}

+ {#if error} + + {:else if hasDescription} +
+ {#if description} + {@render description()} + {:else} +

{content}

+ {/if} +
+ {/if} +
+ + {#if !error} + {#if actions} +
+ {@render actions()} +
+ {/if} + + {@render children?.()} + + {#if resources} +
+ {@render resources()} +
+ {/if} + {/if} +
diff --git a/src/lib/components/empty-states/sdk-links.svelte b/src/lib/components/empty-states/sdk-links.svelte new file mode 100644 index 0000000000..31a887677d --- /dev/null +++ b/src/lib/components/empty-states/sdk-links.svelte @@ -0,0 +1,32 @@ + + +
+ {#if label} +

+ {label} +

+ {/if} +
+ {#each links as { sdk, href, version } (sdk)} + + {version ? `${sdk} ${version}` : sdk} + + {/each} +
+
diff --git a/src/lib/components/lines-and-dots/sdk-logo.svelte b/src/lib/components/lines-and-dots/sdk-logo.svelte index 4345f85d84..2bb4a9a48b 100644 --- a/src/lib/components/lines-and-dots/sdk-logo.svelte +++ b/src/lib/components/lines-and-dots/sdk-logo.svelte @@ -1,8 +1,5 @@ - + + + +{#if error} + +{:else if query} + +{:else} + + {#snippet description()} +

{translate('schedules.no-schedules-description')}

+

+ {translate('schedules.getting-started-docs-link-preface')} + {translate('schedules.getting-started-docs-link')} + {translate('schedules.getting-started-cli-link-preface')} + Temporal CLI. +

+ {/snippet} + {#snippet resources()} + + {/snippet} +
+{/if} diff --git a/src/lib/components/standalone-activities/activities-summary-configurable-table.svelte b/src/lib/components/standalone-activities/activities-summary-configurable-table.svelte index 9b79c49a29..b0d9b34220 100644 --- a/src/lib/components/standalone-activities/activities-summary-configurable-table.svelte +++ b/src/lib/components/standalone-activities/activities-summary-configurable-table.svelte @@ -43,9 +43,10 @@ interface Props { onClickConfigure: () => void; + countLoaded?: boolean; } - let { onClickConfigure }: Props = $props(); + let { onClickConfigure, countLoaded = true }: Props = $props(); const { allSelected, selectedActivities, selectActivities } = getContext(ACTIVITY_BATCH_OPERATION_CONTEXT); @@ -59,6 +60,7 @@ const tableSort = createTableSort(() => [namespace, query]); const onFetch = $derived(() => fetchPaginatedActivities(namespace, query)); + const awaitingCount = $derived(!query && !countLoaded); const showBatchActions = $derived( standaloneActivityBulkActionsEnabled(page) && $supportsAdvancedVisibility, @@ -122,6 +124,7 @@ bind:sort={tableSort.value} {sortColumns} {onFetch} + loading={awaitingCount} onItemsChange={(items) => { visiblePaginatedItems = items; }} diff --git a/src/lib/components/standalone-activities/activities-summary-configurable-table/table-empty-state.svelte b/src/lib/components/standalone-activities/activities-summary-configurable-table/table-empty-state.svelte index 1581e7c725..77629fa430 100644 --- a/src/lib/components/standalone-activities/activities-summary-configurable-table/table-empty-state.svelte +++ b/src/lib/components/standalone-activities/activities-summary-configurable-table/table-empty-state.svelte @@ -2,13 +2,12 @@ import { page } from '$app/state'; import NoQueryResults from '$lib/components/empty-states/no-query-results.svelte'; - import SdkLogo from '$lib/components/lines-and-dots/sdk-logo.svelte'; - import Alert from '$lib/holocene/alert.svelte'; + import ResourceEmptyState from '$lib/components/empty-states/resource-empty-state.svelte'; + import SdkLinks from '$lib/components/empty-states/sdk-links.svelte'; import Link from '$lib/holocene/link.svelte'; import { translate } from '$lib/i18n/translate'; - import { IconWarning } from '$lib/io/icon'; + import { IconTemporalActivity } from '$lib/io/icon'; import { activityError } from '$lib/stores/activities'; - import noResultsImages from '$lib/vendor/empty-state.svg'; let query = $derived(page.url.searchParams.get('query')); const SDK_LINKS = [ @@ -36,89 +35,62 @@ sdk: 'Ruby', href: 'https://docs.temporal.io/develop/ruby/activities/standalone-activities', }, + { + sdk: 'Rust', + href: 'https://docs.temporal.io/develop/rust/activities/standalone-activities-quickstart', + }, ]; {#if query} -
-
-

- {#if $activityError} - {translate('standalone-activities.activity-query-error-state')} - {:else} - {translate('standalone-activities.empty-state-title')} - {/if} -

-

- {#if $activityError} - {$activityError} - {:else} - {translate('standalone-activities.empty-state-description')} - {/if} -

- -
-
+ {#snippet illustration()} + + {/snippet} + {:else} -
-
- {#if $activityError} -

- {translate('standalone-activities.empty-state-title')} -

- + {translate('standalone-activities.empty-state-value-proposition')} +

+

+ {translate('standalone-activities.empty-state-read-docs-prefix')} + + {translate('standalone-activities.empty-state-activity-docs-link')} + + {translate('standalone-activities.empty-state-read-docs-middle')} + - {$activityError} - - {:else} -

- {translate('standalone-activities.no-activities-title')} -

-

- {translate('standalone-activities.standalone-activities')}{translate( - 'standalone-activities.no-activities-description-part-1-preface', - )}{translate('standalone-activities.job-queue-link')}{translate( - 'standalone-activities.no-activities-description-part-1-postface', - )} -

-

- {translate('standalone-activities.no-activities-description-part-2')} -

-

- {translate('standalone-activities.no-activities-description-part-3')} -

-

- {translate('standalone-activities.get-started-title')} -

- {#each SDK_LINKS as { sdk, href } (sdk)} - - - - {/each} - {/if} -
-
-
- -
-
-
-
+ {translate('standalone-activities.empty-state-job-queue-guide-link')} + + {translate('standalone-activities.empty-state-read-docs-suffix')} +

+ {/snippet} + {#snippet resources()} + + {/snippet} + {/if} diff --git a/src/lib/components/standalone-nexus-operations/nexus-operations-summary-configurable-table.svelte b/src/lib/components/standalone-nexus-operations/nexus-operations-summary-configurable-table.svelte index 658f90dae7..e13195edb3 100644 --- a/src/lib/components/standalone-nexus-operations/nexus-operations-summary-configurable-table.svelte +++ b/src/lib/components/standalone-nexus-operations/nexus-operations-summary-configurable-table.svelte @@ -31,9 +31,10 @@ interface Props { onClickConfigure: () => void; + countLoaded?: boolean; } - let { onClickConfigure }: Props = $props(); + let { onClickConfigure, countLoaded = true }: Props = $props(); const namespace = $derived(page.params.namespace); const columns = $derived( @@ -46,6 +47,7 @@ const onFetch = $derived(() => fetchPaginatedNexusOperations(namespace, query), ); + const awaitingCount = $derived(!query && !countLoaded); const sortColumns = $derived( getNexusOperationSortColumns( columns.map((column) => column.label), @@ -60,6 +62,7 @@ bind:sort={tableSort.value} {sortColumns} {onFetch} + loading={awaitingCount} aria-label={translate( 'standalone-nexus-operations.standalone-nexus-operations', )} diff --git a/src/lib/components/standalone-nexus-operations/nexus-operations-summary-configurable-table/table-empty-state.svelte b/src/lib/components/standalone-nexus-operations/nexus-operations-summary-configurable-table/table-empty-state.svelte index d92e2680ec..e8f41d39d9 100644 --- a/src/lib/components/standalone-nexus-operations/nexus-operations-summary-configurable-table/table-empty-state.svelte +++ b/src/lib/components/standalone-nexus-operations/nexus-operations-summary-configurable-table/table-empty-state.svelte @@ -2,137 +2,85 @@ import { page } from '$app/state'; import NoQueryResults from '$lib/components/empty-states/no-query-results.svelte'; - import Alert from '$lib/holocene/alert.svelte'; + import ResourceEmptyState from '$lib/components/empty-states/resource-empty-state.svelte'; + import SdkLinks from '$lib/components/empty-states/sdk-links.svelte'; import Link from '$lib/holocene/link.svelte'; import { translate } from '$lib/i18n/translate'; - import { IconGithub, IconWarning } from '$lib/io/icon'; + import { IconTemporalNexus } from '$lib/io/icon'; import { nexusOperationError } from '$lib/stores/nexus-operations'; - import noResultsImages from '$lib/vendor/empty-state.svg'; let query = $derived(page.url.searchParams.get('query')); const codeSamples = [ { - label: 'samples-go', + sdk: 'Go', href: 'https://github.com/temporalio/samples-go', }, { - label: 'samples-java', + sdk: 'Java', href: 'https://github.com/temporalio/samples-java', }, { - label: 'samples-typescript', + sdk: 'Typescript', href: 'https://github.com/temporalio/samples-typescript', }, { - label: 'samples-python', + sdk: 'Python', href: 'https://github.com/temporalio/samples-python', }, { - label: 'samples-dotnet', + sdk: '.NET', href: 'https://github.com/temporalio/samples-dotnet', }, ]; {#if query} -
-
-

- {#if $nexusOperationError} + {#snippet illustration()} + + {/snippet} + +{:else} + + {#snippet description()} +

+ {translate('standalone-nexus-operations.empty-state-value-proposition')} +

+

+ {translate('standalone-nexus-operations.empty-state-read-docs-prefix')} + + {translate('standalone-nexus-operations.empty-state-nexus-docs-link')} + + {translate('standalone-nexus-operations.empty-state-read-docs-middle')} + {translate( - 'standalone-nexus-operations.nexus-operation-query-error-state', + 'standalone-nexus-operations.empty-state-nexus-eval-guide-link', )} - {:else} - {translate('standalone-nexus-operations.empty-state-title')} - {/if} -

-

- {#if $nexusOperationError} - {$nexusOperationError} - {:else} - {translate('standalone-nexus-operations.empty-state-description')} - {/if} + + {translate('standalone-nexus-operations.empty-state-read-docs-suffix')}

- -
-
-{:else} -
-
-

- {translate('standalone-nexus-operations.empty-state-no-data-title')} -

- {#if $nexusOperationError} - - {$nexusOperationError} - - {:else} -
-

- {translate( - 'standalone-nexus-operations.empty-state-value-proposition', - )} -

-

- {translate( - 'standalone-nexus-operations.empty-state-read-docs-prefix', - )} - - {translate( - 'standalone-nexus-operations.empty-state-nexus-docs-link', - )} - - {translate( - 'standalone-nexus-operations.empty-state-read-docs-middle', - )} - - {translate( - 'standalone-nexus-operations.empty-state-nexus-eval-guide-link', - )} - - {translate( - 'standalone-nexus-operations.empty-state-read-docs-suffix', - )} -

-
-
-

- {translate( - 'standalone-nexus-operations.empty-state-code-samples-title', - )} -

-
    - {#each codeSamples as sample (sample.label)} -
  • - - - {sample.label} - -
  • - {/each} -
-
- {/if} -
-
-
- -
-
-
-
+ {/snippet} + {#snippet resources()} + + {/snippet} + {/if} diff --git a/src/lib/components/status-count-filters.svelte b/src/lib/components/status-count-filters.svelte index 4831810f77..49026a1d76 100644 --- a/src/lib/components/status-count-filters.svelte +++ b/src/lib/components/status-count-filters.svelte @@ -39,6 +39,7 @@ let { staticQuery = '', refreshTime = $bindable(), + loaded = $bindable(false), countStore = workflowCount, refresh = workflowRefresh, filters = workflowFilters, @@ -69,6 +70,10 @@ refreshTime = new Date(statusCounts.refreshTime); }); + $effect(function propagateLoadedChange() { + loaded = statusCounts.loaded; + }); + const onStatusClick = (status: Status) => { const statusExists = $filters.some( (filter) => diff --git a/src/lib/components/status-counts-state.svelte.ts b/src/lib/components/status-counts-state.svelte.ts index 5f4eac01e4..dab2a0ea79 100644 --- a/src/lib/components/status-counts-state.svelte.ts +++ b/src/lib/components/status-counts-state.svelte.ts @@ -95,6 +95,9 @@ export function createStatusCountsState(options: StatusCountsStateOptions) { get loading(): boolean { return countPoller.loading; }, + get loaded(): boolean { + return countPoller.loaded; + }, get refreshTime(): number { return countPoller.refreshTime; }, @@ -104,6 +107,7 @@ export function createStatusCountsState(options: StatusCountsStateOptions) { export interface StatusCountDataProps { staticQuery?: string; refreshTime?: Date; + loaded?: boolean; countStore?: CountStore; refresh?: Writable; fetchCounts?: StatusCountFetcher; diff --git a/src/lib/components/workers/workers-table/worker-heartbeats-sdk-warning.svelte b/src/lib/components/workers/workers-table/worker-heartbeats-sdk-warning.svelte index 3e72644449..ff7f79c65a 100644 --- a/src/lib/components/workers/workers-table/worker-heartbeats-sdk-warning.svelte +++ b/src/lib/components/workers/workers-table/worker-heartbeats-sdk-warning.svelte @@ -1,4 +1,6 @@ -
- - - -
-
{translate('workers.no-worker-heartbeats')}
- {#if currentSdk} - {@const { href, sdk, version } = currentSdk} - {#if !minimumVersionRequired(version, $sdkInfo.version)} -

- {translate('workers.worker-heartbeats-sdk-link-preface')} - - - - {translate('workers.worker-heartbeats-sdk-link-postface')} -

- {/if} - {:else} -

- {translate('workers.worker-heartbeats-sdk-list-preface')} -

-
- {#each supportedVersions as { href, sdk, version } (`${sdk}:${version}`)} - - - - {/each} -
- {/if} -
-
+{#snippet upgradeNote()} + {#if currentSdk} + {@const { href, sdk, version } = currentSdk} +
+ {translate('workers.worker-heartbeats-sdk-link-preface')} + + + + {translate('workers.worker-heartbeats-sdk-link-postface')} +
+ {/if} +{/snippet} + +{#snippet supportedSdks()} + +{/snippet} + + diff --git a/src/lib/components/workers/workers-table/workers-table.svelte b/src/lib/components/workers/workers-table/workers-table.svelte index f9803640e6..f8caf710e5 100644 --- a/src/lib/components/workers/workers-table/workers-table.svelte +++ b/src/lib/components/workers/workers-table/workers-table.svelte @@ -1,7 +1,7 @@ {#if query} -
-
-

- {#if $workflowError} - {translate('workflows.workflow-query-error-state')} - {:else} - {hasTaskFailuresQuery - ? translate( - 'workflows.workflow-task-failures-query-empty-state-title', - ) - : translate('workflows.workflow-query-empty-state-title')} - {/if} -

-

- {#if $workflowError} - {$workflowError} - {:else} - {hasTaskFailuresQuery - ? translate( - 'workflows.workflow-task-failures-query-empty-state-description', - ) - : translate('workflows.workflow-query-empty-state-description')} - {/if} -

+ {#snippet illustration()} {#if hasTaskFailuresQuery} - + {:else} - + {/if} -
-
+ {/snippet} +
{:else} -
-
-

- {translate('workflows.workflow-empty-state-title')} -

- {#if $workflowError} - - {$workflowError} - - {:else} - {@render cloud?.()} -

- {translate('workflows.workflow-empty-state-description')} - github.com/temporalio. -

-
    - {#each samples as sample (sample)} -
  • - {sample} -
  • - {/each} -
- {/if} -
-
-
- -
-
-
-
+ {#snippet description()} +

+ {translate('workflows.workflow-empty-state-value-proposition')} +

+

+ {translate('workflows.workflow-empty-state-read-docs-prefix')} + + {translate('workflows.workflow-empty-state-docs-link')} + + {translate('workflows.workflow-empty-state-read-docs-suffix')} +

+ {/snippet} + {#snippet resources()} + + {/snippet} + {/if} diff --git a/src/lib/holocene/table/paginated-table/api-paginated.svelte b/src/lib/holocene/table/paginated-table/api-paginated.svelte index 0643dff135..5167693825 100644 --- a/src/lib/holocene/table/paginated-table/api-paginated.svelte +++ b/src/lib/holocene/table/paginated-table/api-paginated.svelte @@ -56,6 +56,7 @@ sort?: TableSort; sortColumns?: Record>; onLoadingChange?: ((loading: boolean) => void) | undefined; + loading?: boolean; onShiftUp?: KeyboardHandler; onShiftDown?: KeyboardHandler; onSpace?: KeyboardHandler; @@ -87,6 +88,7 @@ sort = $bindable(), sortColumns = {}, onLoadingChange, + loading = false, onShiftUp, onShiftDown, onSpace, @@ -318,7 +320,7 @@ + import BatchOperationsEmptyState from '$lib/components/batch-operations/empty-state.svelte'; import BatchOperationsTable from '$lib/components/batch-operations/table.svelte'; import Alert from '$lib/holocene/alert.svelte'; import Loading from '$lib/holocene/loading.svelte'; @@ -31,5 +32,9 @@ {translate('batch.max-concurrent-alert-description')} {/if} - + {#if operations.length} + + {:else} + + {/if} {/await} diff --git a/src/lib/pages/deployments.svelte b/src/lib/pages/deployments.svelte index 4eb412e41c..4cac16742a 100644 --- a/src/lib/pages/deployments.svelte +++ b/src/lib/pages/deployments.svelte @@ -3,6 +3,7 @@ import DeploymentTableRow from '$lib/components/deployments/deployment-table-row.svelte'; import DeploymentsEmptyState from '$lib/components/deployments/deployments-empty-state.svelte'; + import EmptyStateTransition from '$lib/components/empty-states/empty-state-transition.svelte'; import ConfigurableTableHeadersDrawer from '$lib/components/table/configurable-table-headers-drawer.svelte'; import ResetColumnWidthsButton from '$lib/components/table/reset-column-widths-button.svelte'; import Button from '$lib/holocene/button.svelte'; @@ -58,10 +59,18 @@ const createHref = $derived(routeForWorkerDeploymentCreate({ namespace })); + let firstPageEmpty = $state(false); + const showEmptyState = $derived(firstPageEmpty && !error); + const onFetch = $derived.by(() => { - return () => { + return async () => { error = ''; - return fetchPaginatedDeployments(namespace, '', onError); + const fetchPage = await fetchPaginatedDeployments(namespace, '', onError); + return (size: number, token: string) => + fetchPage(size, token).then((result) => { + if (!token) firstPageEmpty = result.items.length === 0; + return result; + }); }; }); @@ -130,8 +139,11 @@ }; -{#key [namespace, $refresh]} -
+ + {#snippet emptyState()} + + {/snippet} + {#key [namespace, $refresh]} + {#if error} + + {/if} {/snippet} {#snippet actionsEndAdditional()} {/snippet} -
-{/key} + {/key} + import type { Snippet } from 'svelte'; - import { useDarkMode } from '$lib/utilities/dark-mode'; - import andromeda from '$lib/vendor/andromeda.png'; + import NexusIllustration from '$lib/components/empty-states/nexus-illustration.svelte'; type Props = { actions?: Snippet; + nexusRegistryDescription?: Snippet; }; - let { actions }: Props = $props(); + let { actions, nexusRegistryDescription = nexusDescription }: Props = + $props();
-
-
-

- {translate('nexus.endpoints')} -

-
+
-

+

{translate('nexus.registry-empty-state-title')}

- - {@render nexusRegistryDescription()} + {@render nexusRegistryDescription?.()} {@render actions?.()}
-
-{#snippet nexusRegistryDescription()} +{#snippet nexusDescription()}

{translate('nexus.registry-empty-state-nexus-link')} @@ -70,9 +64,3 @@ {translate('nexus.registry-empty-state-proxy-postface')}

{/snippet} - - diff --git a/src/lib/pages/schedules.svelte b/src/lib/pages/schedules.svelte index ddd5dda1dc..e5fe1aa07a 100644 --- a/src/lib/pages/schedules.svelte +++ b/src/lib/pages/schedules.svelte @@ -4,25 +4,24 @@ import { page } from '$app/state'; import CountRefreshButton from '$lib/components/count-refresh-button.svelte'; + import EmptyStateTransition from '$lib/components/empty-states/empty-state-transition.svelte'; import SavedQueryViews from '$lib/components/saved-query-views/saved-views.svelte'; import { scheduledActionPresentations } from '$lib/components/schedule/scheduled-action-types/presentation-registry'; import { resolveScheduledActionListEntry } from '$lib/components/schedule/scheduled-action-types/resolve-scheduled-action-list-entry'; + import SchedulesEmptyState from '$lib/components/schedule/schedules-list/schedules-empty-state.svelte'; import SchedulesTableRow from '$lib/components/schedule/schedules-list/schedules-table-row.svelte'; import FilterBar from '$lib/components/search-attribute-filter/filter-bar.svelte'; import ConfigurableTableHeadersDrawer from '$lib/components/table/configurable-table-headers-drawer.svelte'; import ResetColumnWidthsButton from '$lib/components/table/reset-column-widths-button.svelte'; import { timestamp } from '$lib/components/timestamp.svelte'; - import Alert from '$lib/holocene/alert.svelte'; import Button from '$lib/holocene/button.svelte'; - import EmptyState from '$lib/holocene/empty-state.svelte'; - import Link from '$lib/holocene/link.svelte'; import ColumnResizeHandle from '$lib/holocene/table/column-resize-handle.svelte'; import PaginatedTable from '$lib/holocene/table/paginated-table/api-paginated.svelte'; import MaximizableTableView from '$lib/holocene/table/paginated-table/maximizable-view.svelte'; import SortableTableHeader from '$lib/holocene/table/sortable-table-header.svelte'; import Tooltip from '$lib/holocene/tooltip.svelte'; import { translate } from '$lib/i18n/translate'; - import { IconTemporalSettings, IconWarning } from '$lib/io/icon'; + import { IconTemporalSettings } from '$lib/io/icon'; import { createCountPoller } from '$lib/runes/count-poller.svelte'; import { createTableSort } from '$lib/runes/table-sort-state.svelte'; import { fetchPaginatedSchedules } from '$lib/services/schedule-service'; @@ -213,118 +212,105 @@
- - - - {#key [namespace, query, $schedulesRefresh]} - - {#snippet caption()} - {translate('common.schedules')} - {/snippet} - {#snippet headers()} - - {#each columns as { label, width }, i (`${label}:${i}`)} - - - resizeColumn( - label, - newWidth, - namespace, - TABLE_TYPE.SCHEDULES, - )} - /> - + + {#snippet emptyState()} + + {/snippet} + + + + {#key [namespace, query, $schedulesRefresh]} + + {#snippet caption()} + {translate('common.schedules')} + {/snippet} + {#snippet headers()} + + {#each columns as { label, width }, i (`${label}:${i}`)} + + + resizeColumn( + label, + newWidth, + namespace, + TABLE_TYPE.SCHEDULES, + )} + /> + + {/each} + + {/snippet} + {#snippet rows({ visibleItems })} + + {#each visibleItems as schedule (schedule)} + {/each} - - {/snippet} - {#snippet rows({ visibleItems })} - - {#each visibleItems as schedule (schedule)} - - {/each} - {/snippet} + {/snippet} - {#snippet empty()} -
- {#if error} - - - {error} - - - {:else if query} - - {:else} - -

- {translate('schedules.getting-started-docs-link-preface')} - {translate('schedules.getting-started-docs-link')} - {translate('schedules.getting-started-cli-link-preface')} - Temporal CLI. -

-
- {/if} -
- {/snippet} - {#snippet actionsEndAdditional()} - - - - - {/snippet} -
- {/key} -
+ {#snippet empty()} +
+ +
+ {/snippet} + {#snippet actionsEndAdditional()} + + + + + {/snippet} +
+ {/key} +
+ - - - - - + + {#snippet emptyState()} + + {/snippet} + + + + + + { customizationDrawerOpen = true; @@ -110,6 +119,7 @@ {@render releaseStageBadge?.()} - - - - - + + {#snippet emptyState()} + + {/snippet} + + + + + + { + fetchFailed = false; + const fetchPage = await fetchPaginatedWorkers({ namespace, query }); + return (size: number, token: string) => + fetchPage(size, token).then((result) => { + if (!token) firstPageEmpty = result.items.length === 0; + return result; + }); + }; {#if workerHeartbeatsEnabled} - - - - {#key [namespace, query, $refresh]} - fetchPaginatedWorkers({ namespace, query })} - total={$workerCount.count} - filterable + + {#snippet emptyState()} + + {/snippet} + + + - {/key} - + {#key [namespace, query, $refresh]} + (fetchFailed = true)} + showSdkWarning={false} + total={$workerCount.count} + filterable + /> + {/key} + + {:else} {/if} diff --git a/src/lib/pages/workflows-with-new-search.svelte b/src/lib/pages/workflows-with-new-search.svelte index 0d14349665..da20fcdd4f 100644 --- a/src/lib/pages/workflows-with-new-search.svelte +++ b/src/lib/pages/workflows-with-new-search.svelte @@ -27,6 +27,7 @@ import { page } from '$app/state'; import CountRefreshButton from '$lib/components/count-refresh-button.svelte'; + import EmptyStateTransition from '$lib/components/empty-states/empty-state-transition.svelte'; import SavedQueryViews from '$lib/components/saved-query-views/saved-views.svelte'; import StatusCountFilters from '$lib/components/status-count-filters.svelte'; import ConfigurableTableHeadersDrawer from '$lib/components/table/configurable-table-headers-drawer.svelte'; @@ -37,6 +38,7 @@ import CancelConfirmationModal from '$lib/components/workflow/client-actions/cancel-confirmation-modal.svelte'; import TerminateConfirmationModal from '$lib/components/workflow/client-actions/terminate-confirmation-modal.svelte'; import FilterBar from '$lib/components/workflow/filter-bar/index.svelte'; + import WorkflowsEmptyState from '$lib/components/workflow/workflows-summary-configurable-table/table-empty-state.svelte'; import WorkflowsSummaryConfigurableTable from '$lib/components/workflow/workflows-summary-configurable-table.svelte'; import Button from '$lib/holocene/button.svelte'; import MaximizableTableView from '$lib/holocene/table/paginated-table/maximizable-view.svelte'; @@ -135,7 +137,13 @@ }); let customizationDrawerOpen = $state(false); - + let countLoaded = $state(false); + const showEmptyState = $derived( + $supportsAdvancedVisibility && + !query && + countLoaded && + $workflowCount.count === 0, + ); let batchTerminateConfirmationModalOpen = $state(false); let batchCancelConfirmationModalOpen = $state(false); let batchResetConfirmationModalOpen = $state(false); @@ -254,7 +262,7 @@ {refreshTimeFormatted}

- + {#if headerActions || workflowStartEnabled}
@@ -269,21 +277,27 @@
- - - - - + + {#snippet emptyState()} + + {/snippet} + + + + + + (opts: CountPollerOptions) { let attempt = 1; let loading = $state(false); + let loaded = $state(false); let refreshTime = $state(Date.now()); let timeoutId: ReturnType | undefined; @@ -52,6 +53,7 @@ export function createCountPoller(opts: CountPollerOptions) { const count = opts.transform(response); opts.getStore().update((s) => ({ ...s, count })); setRefresh(); + loaded = true; opts.onInitialFetch?.(count, response); } catch (e: unknown) { console.error('Initial count fetch failed', e); @@ -118,6 +120,9 @@ export function createCountPoller(opts: CountPollerOptions) { get loading() { return loading; }, + get loaded() { + return loaded; + }, /** Timestamp of the most recent full fetch. Not updated by poll fetches. */ get refreshTime() { return refreshTime; diff --git a/src/lib/vendor/andromeda.png b/src/lib/vendor/andromeda.png deleted file mode 100644 index ec4328a07c..0000000000 Binary files a/src/lib/vendor/andromeda.png and /dev/null differ diff --git a/tests/integration/schedules-list.spec.ts b/tests/integration/schedules-list.spec.ts index a144578a4d..8a98f5c742 100644 --- a/tests/integration/schedules-list.spec.ts +++ b/tests/integration/schedules-list.spec.ts @@ -35,7 +35,7 @@ test.describe('Schedules List with no schedules', () => { await page.goto(schedulesUrl); await page.waitForResponse(SCHEDULES_COUNT_API); - const emptyState = page.getByText('No Schedules Found'); + const emptyState = page.getByText('No Schedules', { exact: true }); await expect(emptyState).toBeVisible(); }); });