From 1f0cd66386f7218aaa137cee03aea1e86613bd20 Mon Sep 17 00:00:00 2001 From: Bill Leoutsakos Date: Tue, 22 Sep 2026 14:00:55 -0700 Subject: [PATCH] improvement(ui): share missing resource states --- .../[workspaceId]/components/index.ts | 1 - .../resource/resource-not-found.tsx | 27 -------- .../resource-content/resource-content.tsx | 68 ++++++++----------- .../knowledge/[id]/[documentId]/document.tsx | 12 +++- .../[workspaceId]/knowledge/[id]/base.tsx | 4 +- .../components/table-grid/table-grid.tsx | 18 ++--- packages/emcn/src/components/index.ts | 4 ++ .../resource-empty-state.tsx | 24 +++++++ 8 files changed, 74 insertions(+), 84 deletions(-) delete mode 100644 apps/sim/app/workspace/[workspaceId]/components/resource/resource-not-found.tsx create mode 100644 packages/emcn/src/components/resource-empty-state/resource-empty-state.tsx diff --git a/apps/sim/app/workspace/[workspaceId]/components/index.ts b/apps/sim/app/workspace/[workspaceId]/components/index.ts index 9791199d107..17df9766567 100644 --- a/apps/sim/app/workspace/[workspaceId]/components/index.ts +++ b/apps/sim/app/workspace/[workspaceId]/components/index.ts @@ -2,7 +2,6 @@ export { isResourceListEmpty, resourceListState, } from '@/app/workspace/[workspaceId]/components/resource/is-resource-list-empty' -export { ResourceNotFound } from '@/app/workspace/[workspaceId]/components/resource/resource-not-found' export { ConversationListItem } from './conversation-list-item' export type { ErrorBoundaryProps, ErrorStateProps } from './error' export { ErrorShell, ErrorState } from './error' diff --git a/apps/sim/app/workspace/[workspaceId]/components/resource/resource-not-found.tsx b/apps/sim/app/workspace/[workspaceId]/components/resource/resource-not-found.tsx deleted file mode 100644 index bf99da51fa5..00000000000 --- a/apps/sim/app/workspace/[workspaceId]/components/resource/resource-not-found.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import type { ComponentType } from 'react' - -interface ResourceNotFoundProps { - icon: ComponentType<{ className?: string }> - title: string - description: string -} - -/** - * Full-page screen for a resource that could not be loaded and has no shell left to - * draw — a knowledge base or a document that was deleted or moved. - * - * Distinct from the `emptyState` slot on {@link Resource.Table}: that one keeps the - * chrome and reports a failure *within* a page that still exists. This replaces the - * page, so it is only right when the thing the page is about is the thing that is gone. - */ -export function ResourceNotFound({ icon: Icon, title, description }: ResourceNotFoundProps) { - return ( -
- -
-

{title}

-

{description}

-
-
- ) -} diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx index ed9ac8e2711..78753a4ae69 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx @@ -1,7 +1,15 @@ 'use client' import { lazy, memo, Suspense, useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { OverflowText, PlayOutline, Skeleton, TabStripAction, Tooltip, toast } from '@sim/emcn' +import { + OverflowText, + PlayOutline, + ResourceEmptyState, + Skeleton, + TabStripAction, + Tooltip, + toast, +} from '@sim/emcn' import { Download, FileX, @@ -666,17 +674,11 @@ function EmbeddedWorkflow({ workspaceId, workflowId }: EmbeddedWorkflowProps) { if (!workflowExists || hasLoadError) { return ( -
- -
-

- Workflow not found -

-

- This workflow may have been deleted or moved -

-
-
+ ) } @@ -731,15 +733,11 @@ function EmbeddedFile({ if (!file) { return ( -
- -
-

File not found

-

- This file may have been deleted or moved -

-
-
+ ) } @@ -782,15 +780,11 @@ function EmbeddedFolder({ workspaceId, folderId }: EmbeddedFolderProps) { if (!folder) { return ( -
- -
-

Folder not found

-

- This folder may have been deleted or moved -

-
-
+ ) } @@ -840,15 +834,11 @@ function EmbeddedLog({ workspaceId, logId, onNotFound }: EmbeddedLogProps) { if (!log) { return ( -
- -
-

Log not found

-

- This log may have been deleted or is no longer available -

-
-
+ ) } diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/document.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/document.tsx index eb262824d69..b3202525bda 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/document.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/document.tsx @@ -1,7 +1,14 @@ 'use client' import { useCallback, useEffect, useEffectEvent, useMemo, useRef, useState } from 'react' -import { Badge, ChipCombobox, ChipConfirmModal, chipContentLabelClass, cn } from '@sim/emcn' +import { + Badge, + ChipCombobox, + ChipConfirmModal, + chipContentLabelClass, + cn, + ResourceEmptyState, +} from '@sim/emcn' import { ChevronDown, ChevronUp, @@ -38,7 +45,6 @@ import type { import { EMPTY_CELL_PLACEHOLDER, Resource, - ResourceNotFound, SearchHighlight, } from '@/app/workspace/[workspaceId]/components' import { @@ -1163,7 +1169,7 @@ export function Document({ */ if (documentError && !documentData) { return ( - - -
-

- Table not found -

-

- This table may have been deleted or moved -

-
- + ) } diff --git a/packages/emcn/src/components/index.ts b/packages/emcn/src/components/index.ts index b194122e642..a1b1dbc1511 100644 --- a/packages/emcn/src/components/index.ts +++ b/packages/emcn/src/components/index.ts @@ -209,6 +209,10 @@ export { } from './popover/popover' export { POPOVER_ANIMATION_CLASSES } from './popover/popover-animation' export { ProgressItem } from './progress-item/progress-item' +export { + ResourceEmptyState, + type ResourceEmptyStateProps, +} from './resource-empty-state/resource-empty-state' export { SecretInput } from './secret-input/secret-input' export { SecretReveal } from './secret-reveal/secret-reveal' export { WORDMARK_PATHS, WORDMARK_VIEW_BOX } from './sim-wordmark/paths' diff --git a/packages/emcn/src/components/resource-empty-state/resource-empty-state.tsx b/packages/emcn/src/components/resource-empty-state/resource-empty-state.tsx new file mode 100644 index 00000000000..af181c7e890 --- /dev/null +++ b/packages/emcn/src/components/resource-empty-state/resource-empty-state.tsx @@ -0,0 +1,24 @@ +import type { ComponentType } from 'react' + +export interface ResourceEmptyStateProps { + icon: ComponentType<{ className?: string }> + title: string + description: string +} + +/** + * Centered resource-empty state for a page or panel. The caller owns loading and + * access decisions; this component only presents the unavailable-resource message. + * @example + */ +export function ResourceEmptyState({ icon: Icon, title, description }: ResourceEmptyStateProps) { + return ( +
+ +
+

{title}

+

{description}

+
+
+ ) +}