diff --git a/frontend/src/lib/components/home/ItemsList.svelte b/frontend/src/lib/components/home/ItemsList.svelte index 5983e97c38..c2ed62ff26 100644 --- a/frontend/src/lib/components/home/ItemsList.svelte +++ b/frontend/src/lib/components/home/ItemsList.svelte @@ -1008,6 +1008,30 @@ let hubPick = $state(undefined) let hubPickerOpen = $state(false) + /** + * Whether a workspace the default listing found empty is empty at all, or just has nothing + * unarchived — two different states that want two different things said about them. Asked + * only in that case, and once per workspace: one request for one row, never on a workspace + * with something in it. A failure answers "no archived items", which shows the ordinary + * placeholder rather than promising items that may not be there. + */ + let archivedProbe = $state<{ workspace: string; hasArchived: boolean } | undefined>(undefined) + $effect(() => { + const ws = $workspaceStore + if (!ws || !workspaceEmpty || archivedProbe?.workspace === ws) return + untrack(() => void probeArchived(ws)) + }) + async function probeArchived(workspace: string) { + try { + const res = await ScriptService.listRunnables({ workspace, showArchived: true, perPage: 1 }) + archivedProbe = { workspace, hasArchived: (res.items?.length ?? 0) > 0 } + } catch (error) { + console.error('Could not check for archived items:', error) + archivedProbe = { workspace, hasArchived: false } + } + } + let emptyStateAnswered = $derived(archivedProbe?.workspace === $workspaceStore) + // The workspace itself holds nothing — no filter is narrowing the list away. It stays // false until the first load resolves: a skeleton already means "loading", and the // empty state must not be mistaken for one. The controls it dims stay mounted, so @@ -1689,11 +1713,10 @@ > {#if !contentActive} -
+ lands; `inert` takes it out of the tab order and off the pointer meanwhile. A + workspace with nothing but archived items reaches them from its own placeholder, + so these controls are not the way there. --> +
{ @@ -1792,11 +1815,10 @@ {/if}
-
{#if workspaceEmpty && !$userStore?.operator && showEditButtons} - (hubPick = project)} /> + + {#if emptyStateAnswered} + (hubPick = project)} + onShowArchived={() => (filterValues.val = { ...filterValues.val, archived: true })} + /> + {/if} {:else} {/if} diff --git a/frontend/src/lib/components/home/WorkspaceEmptyState.svelte b/frontend/src/lib/components/home/WorkspaceEmptyState.svelte index 0222ab28a1..7513c659d3 100644 --- a/frontend/src/lib/components/home/WorkspaceEmptyState.svelte +++ b/frontend/src/lib/components/home/WorkspaceEmptyState.svelte @@ -10,9 +10,16 @@ interface Props { /** A project was chosen here. The list owns the import dialog, and opens it on this. */ onPick: (project: HubProjectPick) => void + /** + * The workspace holds items, all of them archived. It is not empty and must not be + * told it is: the caption names what is there and offers the way to it instead. + */ + archivedOnly?: boolean + /** Switches the list to the archived view. */ + onShowArchived: () => void } - let { onPick }: Props = $props() + let { onPick, archivedOnly = false, onShowArchived }: Props = $props() // Row opacities: the list fading out of existence. Static on purpose — motion is what // makes a skeleton mean "loading", and this state means "empty". @@ -38,7 +45,7 @@
{#each rowOpacities as opacity, i (i)}
- Your scripts, flows and apps will show up here. + {#if archivedOnly} + + + Everything in this workspace is archived. + . + + {:else} + Your scripts, flows and apps will show up here. + {/if} {#if !$disableHubStore}