@eventcatalog/core 5.0.0-beta.2 → 5.0.0-beta.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +234 -134
- package/README.md +5 -5
- package/dist/analytics/analytics.cjs +18 -3
- package/dist/analytics/analytics.d.cts +1 -1
- package/dist/analytics/analytics.d.ts +1 -1
- package/dist/analytics/analytics.js +3 -2
- package/dist/analytics/log-build.cjs +71 -35
- package/dist/analytics/log-build.js +5 -4
- package/dist/analytics/telemetry.cjs +42 -0
- package/dist/analytics/telemetry.d.cts +20 -0
- package/dist/analytics/telemetry.d.ts +20 -0
- package/dist/analytics/telemetry.js +8 -0
- package/dist/catalog-runtime.cjs +1 -1
- package/dist/{chunk-HGRFXTFC.js → chunk-3VQSVQTU.js} +1 -1
- package/dist/{chunk-KIDISU6X.js → chunk-5ZUN2TXB.js} +56 -33
- package/dist/{chunk-TQLRG35T.js → chunk-AWOO63ME.js} +71 -45
- package/dist/chunk-CPW6G62V.js +17 -0
- package/dist/{chunk-4J2ZM2HX.js → chunk-F236OJZD.js} +1 -1
- package/dist/{chunk-XARPIXWF.js → chunk-FWH5T6U4.js} +9 -3
- package/dist/{chunk-62EV4IJA.js → chunk-R2X4ZEF6.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +214 -129
- package/dist/eventcatalog.config.d.cts +59 -1
- package/dist/eventcatalog.config.d.ts +59 -1
- package/dist/eventcatalog.js +39 -15
- package/dist/federation/federate.js +2 -2
- package/dist/generate.cjs +1 -1
- package/dist/generate.js +3 -3
- package/dist/runtime-dependencies/@eventcatalog__visualiser__studio-fonts.css.mjs +3 -0
- package/dist/runtime-dependencies/@eventcatalog__visualiser__studio.css.mjs +3 -0
- package/dist/runtime-dependencies/@eventcatalog__visualiser__studio.mjs +3 -0
- package/dist/runtime-dependencies/@hocuspocus__provider.mjs +3 -0
- package/dist/runtime-dependencies/@hocuspocus__server.mjs +3 -0
- package/dist/runtime-dependencies/@mcp-b__webmcp-local-relay.mjs +3 -0
- package/dist/runtime-dependencies/@mcp-b__webmcp-polyfill.mjs +3 -0
- package/dist/runtime-dependencies/htmlparser2.mjs +3 -0
- package/dist/runtime-dependencies/manifest.json +13 -1
- package/dist/runtime-dependencies/react-hotkeys-hook.mjs +3 -0
- package/dist/runtime-dependencies/react-syntax-highlighter__dist__cjs__create-element.js.mjs +3 -0
- package/dist/runtime-dependencies/y-protocols.mjs +3 -0
- package/dist/runtime-dependencies/y-protocols__awareness.mjs +3 -0
- package/dist/runtime-dependencies/yjs.mjs +3 -0
- package/dist/utils/cli-logger.cjs +1 -1
- package/dist/utils/cli-logger.js +2 -2
- package/dist/utils/license-status.cjs +71 -45
- package/dist/utils/license-status.d.cts +14 -5
- package/dist/utils/license-status.d.ts +14 -5
- package/dist/utils/license-status.js +1 -1
- package/eventcatalog/astro.config.mjs +23 -1
- package/eventcatalog/integrations/skip-startup-reload.mjs +31 -0
- package/eventcatalog/integrations/stable-dependency-order.mjs +29 -0
- package/eventcatalog/integrations/storage.mjs +20 -0
- package/eventcatalog/integrations/studio-server.mjs +32 -0
- package/eventcatalog/src/components/CatalogGraph/CatalogForceGraph.tsx +39 -0
- package/eventcatalog/src/components/ChatPanel/ChatPanel.tsx +2 -0
- package/eventcatalog/src/components/Grids/utils.tsx +0 -23
- package/eventcatalog/src/components/Header.astro +3 -3
- package/eventcatalog/src/components/MDX/CodeGroup/CodeGroup.tsx +6 -1
- package/eventcatalog/src/components/MDX/NodeGraph/AstroNodeGraph.tsx +42 -3
- package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +5 -1
- package/eventcatalog/src/components/NavigationProgress.astro +7 -0
- package/eventcatalog/src/components/SchemaExplorer/ApiContentViewer.tsx +2 -0
- package/eventcatalog/src/components/SchemaExplorer/ExamplesViewer.tsx +2 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaCodeModal.tsx +2 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaContentViewer.tsx +2 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +12 -4
- package/eventcatalog/src/components/Search/SearchDataLoader.astro +16 -28
- package/eventcatalog/src/components/Search/SearchModal.tsx +2 -10
- package/eventcatalog/src/components/Search/search-utils.ts +26 -0
- package/eventcatalog/src/components/Seo.astro +5 -1
- package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +53 -83
- package/eventcatalog/src/components/SideNav/NestedSideBar/storage.ts +2 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/utils.ts +131 -0
- package/eventcatalog/src/components/SideNav/SideNav.astro +9 -1
- package/eventcatalog/src/components/Tables/Discover/DiscoverTable.tsx +4 -62
- package/eventcatalog/src/components/Tables/Discover/columns.tsx +17 -150
- package/eventcatalog/src/components/Tables/Resources/ResourcesTable.tsx +425 -0
- package/eventcatalog/src/components/Tables/RowActionsMenu.tsx +157 -0
- package/eventcatalog/src/components/Tables/TablePagination.tsx +82 -0
- package/eventcatalog/src/features/ai/chat-api.ts +3 -3
- package/eventcatalog/src/features/auth/error.astro +4 -2
- package/eventcatalog/src/features/auth/login.astro +2 -1
- package/eventcatalog/src/features/auth/middleware/middleware-auth.ts +10 -0
- package/eventcatalog/src/features/auth/unauthorized.astro +1 -1
- package/eventcatalog/src/features/fields/pages/fields.astro +1 -1
- package/eventcatalog/src/features/integrations/eventcatalog-features.ts +34 -1
- package/eventcatalog/src/features/mcp/apps/architecture-diagram/node-chat.tsx +6 -45
- package/eventcatalog/src/features/mcp/apps/canvas/shared.ts +47 -0
- package/eventcatalog/src/features/mcp/apps/canvas/styles.css +24 -0
- package/eventcatalog/src/features/mcp/apps/canvas/view.tsx +278 -0
- package/eventcatalog/src/features/mcp/apps/generated/canvas.html +272 -0
- package/eventcatalog/src/features/mcp/apps/generated/viewer.html +77 -76
- package/eventcatalog/src/features/mcp/apps/shared/app-view.tsx +2 -1
- package/eventcatalog/src/features/mcp/mcp-apps.ts +6 -3
- package/eventcatalog/src/features/mcp/mcp-server.ts +36 -5
- package/eventcatalog/src/features/server/start.ts +25 -0
- package/eventcatalog/src/features/storage/database.ts +88 -0
- package/eventcatalog/src/features/storage/migrations.ts +85 -0
- package/eventcatalog/src/features/storage/sql/database.sql +5 -0
- package/eventcatalog/src/features/storage/sql/migrations/0001-studio-canvases.sql +12 -0
- package/eventcatalog/src/features/storage/sql/migrations.sql +15 -0
- package/eventcatalog/src/features/storage/sql.ts +28 -0
- package/eventcatalog/src/features/studio/agent-choreography.ts +384 -0
- package/eventcatalog/src/features/studio/agents.ts +28 -0
- package/eventcatalog/src/features/studio/api/canvases-api.ts +219 -0
- package/eventcatalog/src/features/studio/api/canvases.ts +25 -0
- package/eventcatalog/src/features/studio/api/client.ts +40 -0
- package/eventcatalog/src/features/studio/canvas-actions.ts +540 -0
- package/eventcatalog/src/features/studio/canvas-preview.ts +52 -0
- package/eventcatalog/src/features/studio/catalog-resources.ts +155 -0
- package/eventcatalog/src/features/studio/catalog.ts +173 -0
- package/eventcatalog/src/features/studio/components/CanvasHeader.tsx +518 -0
- package/eventcatalog/src/features/studio/components/CanvasStatusMenu.tsx +118 -0
- package/eventcatalog/src/features/studio/components/ConnectAgentDialog.tsx +434 -0
- package/eventcatalog/src/features/studio/components/ConnectionsChooser.tsx +143 -0
- package/eventcatalog/src/features/studio/components/DeleteCanvasDialog.tsx +65 -0
- package/eventcatalog/src/features/studio/components/Dialog.tsx +60 -0
- package/eventcatalog/src/features/studio/components/JoinForm.tsx +109 -0
- package/eventcatalog/src/features/studio/components/LeftPanel.tsx +203 -0
- package/eventcatalog/src/features/studio/components/NodeDetails.tsx +183 -0
- package/eventcatalog/src/features/studio/components/OpenInStudioDialog.tsx +79 -0
- package/eventcatalog/src/features/studio/components/Presence.tsx +293 -0
- package/eventcatalog/src/features/studio/components/ShareDialog.tsx +396 -0
- package/eventcatalog/src/features/studio/components/ShortcutsDialog.tsx +41 -0
- package/eventcatalog/src/features/studio/components/StudioDesigner.tsx +2091 -0
- package/eventcatalog/src/features/studio/components/StudioNotice.astro +24 -0
- package/eventcatalog/src/features/studio/document-name.ts +7 -0
- package/eventcatalog/src/features/studio/from-visualiser.ts +138 -0
- package/eventcatalog/src/features/studio/hooks/presence-sender.ts +63 -0
- package/eventcatalog/src/features/studio/hooks/presence-store.ts +119 -0
- package/eventcatalog/src/features/studio/hooks/use-canvas-webmcp.ts +353 -0
- package/eventcatalog/src/features/studio/hooks/use-comments.ts +54 -0
- package/eventcatalog/src/features/studio/hooks/use-follow-camera.ts +109 -0
- package/eventcatalog/src/features/studio/hooks/use-studio-flow.ts +782 -0
- package/eventcatalog/src/features/studio/identity.ts +59 -0
- package/eventcatalog/src/features/studio/limits.ts +2 -0
- package/eventcatalog/src/features/studio/pages/[id].astro +69 -0
- package/eventcatalog/src/features/studio/pages/index.astro +784 -0
- package/eventcatalog/src/features/studio/pages/webmcp-relay.ts +27 -0
- package/eventcatalog/src/features/studio/server/canvas-mcp.ts +495 -0
- package/eventcatalog/src/features/studio/server/runtime.ts +498 -0
- package/eventcatalog/src/features/studio/server/sign-in.ts +18 -0
- package/eventcatalog/src/features/studio/server/sql/storage.sql +18 -0
- package/eventcatalog/src/features/studio/server/storage.ts +90 -0
- package/eventcatalog/src/features/studio/server/tool-icons.ts +30 -0
- package/eventcatalog/src/features/studio/tool-sync.ts +75 -0
- package/eventcatalog/src/layouts/BaseLayout.astro +4 -4
- package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +13 -7
- package/eventcatalog/src/pages/_index.astro +1 -1
- package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.spec.ts +52 -63
- package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.ts +34 -46
- package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/index.astro +11 -29
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_ResourcesPage.astro +79 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_index.data.spec.ts +32 -14
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_index.data.ts +63 -58
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/index.astro +2 -62
- package/eventcatalog/src/pages/docs/[type]/[id]/index.astro +1 -1
- package/eventcatalog/src/pages/docs/services/[id]/[version]/resources/index.astro +13 -0
- package/eventcatalog/src/pages/index.astro +1 -1
- package/eventcatalog/src/pages/schemas/explorer/index.astro +1 -1
- package/eventcatalog/src/pages/visualiser/[type]/[id]/index.astro +1 -1
- package/eventcatalog/src/stores/sidebar-store/builders/domain.ts +24 -42
- package/eventcatalog/src/stores/sidebar-store/builders/flow.ts +2 -0
- package/eventcatalog/src/stores/sidebar-store/builders/message.ts +2 -0
- package/eventcatalog/src/stores/sidebar-store/builders/service.ts +23 -23
- package/eventcatalog/src/stores/sidebar-store/builders/shared.ts +13 -0
- package/eventcatalog/src/stores/sidebar-store/builders/system.ts +18 -27
- package/eventcatalog/src/stores/sidebar-store/state.ts +5 -0
- package/eventcatalog/src/styles/tailwind.css +2 -2
- package/eventcatalog/src/types/react-syntax-highlighter.d.ts +10 -0
- package/eventcatalog/src/utils/collections/channels.ts +56 -1
- package/eventcatalog/src/utils/collections/flows.ts +50 -1
- package/eventcatalog/src/utils/collections/message-connections.ts +60 -0
- package/eventcatalog/src/utils/collections/message-triggers.ts +2 -1
- package/eventcatalog/src/utils/collections/resource-owners.ts +193 -0
- package/eventcatalog/src/utils/collections/resources.ts +310 -0
- package/eventcatalog/src/utils/collections/util.ts +31 -0
- package/eventcatalog/src/utils/feature.ts +8 -0
- package/eventcatalog/src/utils/git.ts +11 -5
- package/eventcatalog/src/utils/link-validation.ts +25 -19
- package/eventcatalog/src/utils/node-graphs/domain-entity-map.ts +3 -1
- package/eventcatalog/src/utils/node-graphs/domain-levels-node-graph.ts +3 -3
- package/eventcatalog/src/utils/node-graphs/export-mermaid.ts +0 -2
- package/eventcatalog/src/utils/node-graphs/layout-node-graph.ts +20 -1
- package/eventcatalog/src/utils/node-graphs/node-resource.ts +67 -0
- package/eventcatalog/src/utils/node-graphs/systems-node-graph.ts +1 -1
- package/eventcatalog/src/utils/page-title.ts +15 -0
- package/eventcatalog/src/utils/syntax-highlighter-renderer.ts +64 -0
- package/eventcatalog/src/utils/trial.ts +1 -1
- package/package.json +22 -16
- package/dist/runtime-dependencies/shelljs.mjs +0 -3
- package/eventcatalog/public/studio-bg.png +0 -0
- package/eventcatalog/src/components/Grids/DomainGrid.tsx +0 -838
- package/eventcatalog/src/components/Grids/MessageGrid.tsx +0 -344
- package/eventcatalog/src/components/Grids/SystemGrid.tsx +0 -185
- package/eventcatalog/src/components/Grids/components.tsx +0 -170
- package/eventcatalog/src/components/Grids/message-link.ts +0 -38
- package/eventcatalog/src/components/Studio/StudioPageModal.tsx +0 -150
- package/eventcatalog/src/components/Tables/SystemResources/SystemResourcesTable.tsx +0 -294
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_resources.spec.ts +0 -65
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_resources.ts +0 -46
- package/eventcatalog/src/pages/studio.astro +0 -316
- package/dist/{chunk-AYNZXUB5.js → chunk-KHACCTW2.js} +3 -3
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
MagnifyingGlassIcon,
|
|
4
|
-
ChevronLeftIcon,
|
|
5
|
-
ChevronRightIcon,
|
|
6
|
-
ChevronDoubleLeftIcon,
|
|
7
|
-
ChevronDoubleRightIcon,
|
|
8
|
-
} from '@heroicons/react/24/outline';
|
|
9
|
-
import type { CollectionMessageTypes } from '@types';
|
|
10
|
-
import { getCollectionStyles, type PaginationProps, type SearchBarProps, type TypeFilterProps } from './utils';
|
|
11
|
-
|
|
12
|
-
export function SearchBar({ searchQuery, onSearchChange, placeholder, totalResults, totalItems }: SearchBarProps) {
|
|
13
|
-
return (
|
|
14
|
-
<div className="w-full md:w-96">
|
|
15
|
-
<div className="relative">
|
|
16
|
-
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
|
|
17
|
-
<MagnifyingGlassIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
|
|
18
|
-
</div>
|
|
19
|
-
<input
|
|
20
|
-
type="text"
|
|
21
|
-
placeholder={placeholder || 'Search...'}
|
|
22
|
-
value={searchQuery}
|
|
23
|
-
onChange={(e) => onSearchChange(e.target.value)}
|
|
24
|
-
className="block w-full rounded-lg border-0 py-2.5 pl-10 pr-4 text-gray-900 shadow-xs ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-primary sm:text-sm sm:leading-6"
|
|
25
|
-
/>
|
|
26
|
-
{searchQuery && (
|
|
27
|
-
<div className="absolute inset-y-0 right-0 flex items-center pr-3">
|
|
28
|
-
<button onClick={() => onSearchChange('')} className="text-gray-400 hover:text-gray-500 focus:outline-hidden">
|
|
29
|
-
<span className="sr-only">Clear search</span>
|
|
30
|
-
<svg className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
|
31
|
-
<path
|
|
32
|
-
fillRule="evenodd"
|
|
33
|
-
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
|
|
34
|
-
clipRule="evenodd"
|
|
35
|
-
/>
|
|
36
|
-
</svg>
|
|
37
|
-
</button>
|
|
38
|
-
</div>
|
|
39
|
-
)}
|
|
40
|
-
</div>
|
|
41
|
-
{searchQuery && totalResults !== undefined && totalItems !== undefined && (
|
|
42
|
-
<div className="mt-2 text-sm text-gray-500 flex items-center justify-between">
|
|
43
|
-
<span>
|
|
44
|
-
Found <span className="font-medium text-gray-900">{totalResults}</span> of{' '}
|
|
45
|
-
<span className="font-medium text-gray-900">{totalItems}</span>
|
|
46
|
-
</span>
|
|
47
|
-
<span className="text-gray-400 text-xs">ESC to clear</span>
|
|
48
|
-
</div>
|
|
49
|
-
)}
|
|
50
|
-
</div>
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export function TypeFilters({ selectedTypes, onTypeChange, filteredCount, totalCount }: TypeFilterProps) {
|
|
55
|
-
const types: CollectionMessageTypes[] = ['events', 'commands', 'queries'];
|
|
56
|
-
|
|
57
|
-
return (
|
|
58
|
-
<div className="flex items-center gap-2">
|
|
59
|
-
{types.map((type) => {
|
|
60
|
-
const { color, Icon } = getCollectionStyles(type);
|
|
61
|
-
const isSelected = selectedTypes.includes(type);
|
|
62
|
-
return (
|
|
63
|
-
<button
|
|
64
|
-
key={type}
|
|
65
|
-
onClick={() => {
|
|
66
|
-
onTypeChange(selectedTypes.includes(type) ? selectedTypes.filter((t) => t !== type) : [...selectedTypes, type]);
|
|
67
|
-
}}
|
|
68
|
-
className={`
|
|
69
|
-
inline-flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-medium
|
|
70
|
-
transition-colors duration-200
|
|
71
|
-
${
|
|
72
|
-
isSelected
|
|
73
|
-
? `bg-${color}-100 text-${color}-700 ring-2 ring-${color}-500`
|
|
74
|
-
: 'bg-gray-50 text-gray-600 hover:bg-gray-100'
|
|
75
|
-
}
|
|
76
|
-
`}
|
|
77
|
-
>
|
|
78
|
-
<Icon className={`h-4 w-4 ${isSelected ? `text-${color}-500` : 'text-gray-400'}`} />
|
|
79
|
-
<span className="capitalize">{type}</span>
|
|
80
|
-
{isSelected && filteredCount !== undefined && (
|
|
81
|
-
<span
|
|
82
|
-
className={`inline-flex items-center justify-center px-2 py-0.5 text-xs font-medium bg-${color}-50 text-${color}-700 rounded-full`}
|
|
83
|
-
>
|
|
84
|
-
{filteredCount}
|
|
85
|
-
</span>
|
|
86
|
-
)}
|
|
87
|
-
</button>
|
|
88
|
-
);
|
|
89
|
-
})}
|
|
90
|
-
{selectedTypes.length > 0 && (
|
|
91
|
-
<button onClick={() => onTypeChange([])} className="text-xs text-gray-500 hover:text-gray-700 hover:underline">
|
|
92
|
-
Clear filters
|
|
93
|
-
</button>
|
|
94
|
-
)}
|
|
95
|
-
</div>
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export function Pagination({ currentPage, totalPages, totalItems, itemsPerPage, onPageChange }: PaginationProps) {
|
|
100
|
-
if (totalPages <= 1) return null;
|
|
101
|
-
|
|
102
|
-
return (
|
|
103
|
-
<div className="flex items-center justify-between border-gray-200 bg-white px-4 py-3 sm:px-6">
|
|
104
|
-
<div className="flex flex-1 justify-between sm:hidden">
|
|
105
|
-
<button
|
|
106
|
-
onClick={() => onPageChange(Math.max(1, currentPage - 1))}
|
|
107
|
-
disabled={currentPage === 1}
|
|
108
|
-
className="relative inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 disabled:opacity-50"
|
|
109
|
-
>
|
|
110
|
-
Previous
|
|
111
|
-
</button>
|
|
112
|
-
<button
|
|
113
|
-
onClick={() => onPageChange(Math.min(totalPages, currentPage + 1))}
|
|
114
|
-
disabled={currentPage === totalPages}
|
|
115
|
-
className="relative ml-3 inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 disabled:opacity-50"
|
|
116
|
-
>
|
|
117
|
-
Next
|
|
118
|
-
</button>
|
|
119
|
-
</div>
|
|
120
|
-
<div className="hidden sm:flex sm:flex-1 sm:items-center sm:justify-between">
|
|
121
|
-
<div className="pr-4">
|
|
122
|
-
<p className="text-sm text-gray-700">
|
|
123
|
-
Showing <span className="font-medium">{(currentPage - 1) * itemsPerPage + 1}</span> to{' '}
|
|
124
|
-
<span className="font-medium">{Math.min(currentPage * itemsPerPage, totalItems)}</span> of{' '}
|
|
125
|
-
<span className="font-medium">{totalItems}</span> results
|
|
126
|
-
</p>
|
|
127
|
-
</div>
|
|
128
|
-
<div>
|
|
129
|
-
<nav className="isolate inline-flex -space-x-px rounded-md shadow-xs" aria-label="Pagination">
|
|
130
|
-
<button
|
|
131
|
-
onClick={() => onPageChange(1)}
|
|
132
|
-
disabled={currentPage === 1}
|
|
133
|
-
className="relative inline-flex items-center rounded-l-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0 disabled:opacity-50"
|
|
134
|
-
>
|
|
135
|
-
<span className="sr-only">First</span>
|
|
136
|
-
<ChevronDoubleLeftIcon className="h-5 w-5" aria-hidden="true" />
|
|
137
|
-
</button>
|
|
138
|
-
<button
|
|
139
|
-
onClick={() => onPageChange(Math.max(1, currentPage - 1))}
|
|
140
|
-
disabled={currentPage === 1}
|
|
141
|
-
className="relative inline-flex items-center px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0 disabled:opacity-50"
|
|
142
|
-
>
|
|
143
|
-
<span className="sr-only">Previous</span>
|
|
144
|
-
<ChevronLeftIcon className="h-5 w-5" aria-hidden="true" />
|
|
145
|
-
</button>
|
|
146
|
-
<span className="relative inline-flex items-center px-4 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 focus:outline-offset-0">
|
|
147
|
-
Page {currentPage} of {totalPages}
|
|
148
|
-
</span>
|
|
149
|
-
<button
|
|
150
|
-
onClick={() => onPageChange(Math.min(totalPages, currentPage + 1))}
|
|
151
|
-
disabled={currentPage === totalPages}
|
|
152
|
-
className="relative inline-flex items-center px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0 disabled:opacity-50"
|
|
153
|
-
>
|
|
154
|
-
<span className="sr-only">Next</span>
|
|
155
|
-
<ChevronRightIcon className="h-5 w-5" aria-hidden="true" />
|
|
156
|
-
</button>
|
|
157
|
-
<button
|
|
158
|
-
onClick={() => onPageChange(totalPages)}
|
|
159
|
-
disabled={currentPage === totalPages}
|
|
160
|
-
className="relative inline-flex items-center rounded-r-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0 disabled:opacity-50"
|
|
161
|
-
>
|
|
162
|
-
<span className="sr-only">Last</span>
|
|
163
|
-
<ChevronDoubleRightIcon className="h-5 w-5" aria-hidden="true" />
|
|
164
|
-
</button>
|
|
165
|
-
</nav>
|
|
166
|
-
</div>
|
|
167
|
-
</div>
|
|
168
|
-
</div>
|
|
169
|
-
);
|
|
170
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import type { CollectionMessageTypes } from '@types';
|
|
2
|
-
|
|
3
|
-
export const MESSAGE_COLLECTIONS: readonly CollectionMessageTypes[] = ['events', 'commands', 'queries'];
|
|
4
|
-
|
|
5
|
-
export function isMessageCollection(value: unknown): value is CollectionMessageTypes {
|
|
6
|
-
return value === 'events' || value === 'commands' || value === 'queries';
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface MessageLinkProps {
|
|
10
|
-
collection?: CollectionMessageTypes;
|
|
11
|
-
id?: string;
|
|
12
|
-
name?: string;
|
|
13
|
-
version: string;
|
|
14
|
-
href?: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Resolve a DomainGrid/SystemGrid receives/sends item into a docs link.
|
|
19
|
-
*
|
|
20
|
-
* Hydrated collection entries carry `collection` + `data.name`. Bare `{id, version}`
|
|
21
|
-
* pointers do not — never guess `events` for those, or commands/queries get the wrong URL.
|
|
22
|
-
*/
|
|
23
|
-
export function getMessageLinkProps(message: any): MessageLinkProps {
|
|
24
|
-
const data = message?.data ?? message;
|
|
25
|
-
const collectionCandidate = message?.collection ?? data?.collection;
|
|
26
|
-
const collection = isMessageCollection(collectionCandidate) ? collectionCandidate : undefined;
|
|
27
|
-
const id = data?.id ?? message?.id;
|
|
28
|
-
const name = data?.name || message?.name || id;
|
|
29
|
-
const version = data?.version ?? message?.version ?? 'latest';
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
collection,
|
|
33
|
-
id,
|
|
34
|
-
name,
|
|
35
|
-
version,
|
|
36
|
-
href: collection && id ? `/docs/${collection}/${id}/${version}` : undefined,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react';
|
|
2
|
-
import * as Dialog from '@radix-ui/react-dialog';
|
|
3
|
-
import { ExternalLinkIcon, CheckIcon, ClipboardIcon, Loader2 } from 'lucide-react';
|
|
4
|
-
|
|
5
|
-
const StudioPageModal: React.FC = () => {
|
|
6
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
7
|
-
const [copySuccess, setCopySuccess] = useState(false);
|
|
8
|
-
const [hasCopied, setHasCopied] = useState(false);
|
|
9
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
10
|
-
const [error, setError] = useState<string | null>(null);
|
|
11
|
-
|
|
12
|
-
useEffect(() => {
|
|
13
|
-
const handleOpen = () => setIsOpen(true);
|
|
14
|
-
window.addEventListener('openStudioModal', handleOpen);
|
|
15
|
-
return () => window.removeEventListener('openStudioModal', handleOpen);
|
|
16
|
-
}, []);
|
|
17
|
-
|
|
18
|
-
const handleCopyResources = async () => {
|
|
19
|
-
setIsLoading(true);
|
|
20
|
-
setError(null);
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
const response = await fetch('/api/catalog');
|
|
24
|
-
|
|
25
|
-
if (!response.ok) {
|
|
26
|
-
throw new Error('Failed to fetch catalog data');
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const catalogData = await response.json();
|
|
30
|
-
|
|
31
|
-
await navigator.clipboard.writeText(JSON.stringify(catalogData, null, 2));
|
|
32
|
-
setCopySuccess(true);
|
|
33
|
-
setHasCopied(true);
|
|
34
|
-
setTimeout(() => setCopySuccess(false), 2000);
|
|
35
|
-
} catch (err) {
|
|
36
|
-
console.error('Failed to copy resources:', err);
|
|
37
|
-
setError('Failed to copy resources. Please try again.');
|
|
38
|
-
} finally {
|
|
39
|
-
setIsLoading(false);
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const handleOpenStudio = () => {
|
|
44
|
-
window.open(
|
|
45
|
-
'https://app.eventcatalog.studio/playground?import-resources=true&utm_source=eventcatalog&utm_medium=referral&utm_campaign=studio-page',
|
|
46
|
-
'_blank'
|
|
47
|
-
);
|
|
48
|
-
setIsOpen(false);
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
return (
|
|
52
|
-
<Dialog.Root open={isOpen} onOpenChange={setIsOpen}>
|
|
53
|
-
<Dialog.Portal>
|
|
54
|
-
<Dialog.Overlay className="fixed inset-0 bg-black/50 data-[state=open]:animate-overlayShow z-50" />
|
|
55
|
-
<Dialog.Content className="fixed top-1/2 left-1/2 w-[90vw] max-w-md -translate-x-1/2 -translate-y-1/2 rounded-lg bg-white p-6 shadow-xl focus:outline-hidden data-[state=open]:animate-contentShow z-[100]">
|
|
56
|
-
<Dialog.Title className="text-lg font-semibold text-gray-900 mb-3">Open EventCatalog Studio</Dialog.Title>
|
|
57
|
-
|
|
58
|
-
<Dialog.Description className="text-sm text-gray-600 mb-6">
|
|
59
|
-
Import your catalog resources into{' '}
|
|
60
|
-
<a
|
|
61
|
-
href="https://eventcatalog.studio"
|
|
62
|
-
className="text-gray-900 hover:text-gray-700 underline font-semibold"
|
|
63
|
-
target="_blank"
|
|
64
|
-
rel="noopener noreferrer"
|
|
65
|
-
>
|
|
66
|
-
EventCatalog Studio
|
|
67
|
-
</a>{' '}
|
|
68
|
-
to create architecture diagrams.
|
|
69
|
-
</Dialog.Description>
|
|
70
|
-
|
|
71
|
-
<div className="space-y-4">
|
|
72
|
-
{/* Step 1: Copy Resources */}
|
|
73
|
-
<div className="bg-gray-50 rounded-lg p-4 border border-gray-200">
|
|
74
|
-
<h4 className="text-sm font-bold text-gray-900 mb-2">Step 1: Copy your resources</h4>
|
|
75
|
-
<p className="text-xs text-gray-600 mb-3">Copy your EventCatalog resources to your clipboard.</p>
|
|
76
|
-
|
|
77
|
-
<button
|
|
78
|
-
onClick={handleCopyResources}
|
|
79
|
-
disabled={isLoading}
|
|
80
|
-
className={`w-full flex items-center justify-center space-x-2 px-4 py-2 text-sm font-medium rounded-md border transition-colors ${
|
|
81
|
-
copySuccess
|
|
82
|
-
? 'bg-green-50 border-green-200 text-green-700'
|
|
83
|
-
: isLoading
|
|
84
|
-
? 'bg-gray-100 border-gray-300 text-gray-400 cursor-not-allowed'
|
|
85
|
-
: 'bg-white border-gray-300 text-gray-700 hover:bg-gray-50'
|
|
86
|
-
}`}
|
|
87
|
-
>
|
|
88
|
-
{isLoading ? (
|
|
89
|
-
<>
|
|
90
|
-
<Loader2 className="w-4 h-4 animate-spin" />
|
|
91
|
-
<span>Loading resources...</span>
|
|
92
|
-
</>
|
|
93
|
-
) : copySuccess ? (
|
|
94
|
-
<>
|
|
95
|
-
<CheckIcon className="w-4 h-4" />
|
|
96
|
-
<span>Copied!</span>
|
|
97
|
-
</>
|
|
98
|
-
) : (
|
|
99
|
-
<>
|
|
100
|
-
<ClipboardIcon className="w-4 h-4" />
|
|
101
|
-
<span>Copy resources to clipboard</span>
|
|
102
|
-
</>
|
|
103
|
-
)}
|
|
104
|
-
</button>
|
|
105
|
-
|
|
106
|
-
{error && <p className="text-xs text-red-600 mt-2">{error}</p>}
|
|
107
|
-
</div>
|
|
108
|
-
|
|
109
|
-
{/* Step 2: Open Studio */}
|
|
110
|
-
<div className="bg-gray-50 rounded-lg p-4 border border-gray-200">
|
|
111
|
-
<h4 className="text-sm font-bold text-gray-900 mb-2">Step 2: Open EventCatalog Studio</h4>
|
|
112
|
-
<p className="text-xs text-gray-600 mb-3">
|
|
113
|
-
Go to EventCatalog Studio and paste your resources into the modal dialog.
|
|
114
|
-
</p>
|
|
115
|
-
|
|
116
|
-
<button
|
|
117
|
-
onClick={handleOpenStudio}
|
|
118
|
-
disabled={!hasCopied}
|
|
119
|
-
className={`w-full flex items-center justify-center space-x-2 px-4 py-2 text-sm font-medium rounded-md transition-colors ${
|
|
120
|
-
hasCopied ? 'bg-gray-900 text-white hover:bg-gray-800' : 'bg-gray-300 text-gray-500 cursor-not-allowed'
|
|
121
|
-
}`}
|
|
122
|
-
>
|
|
123
|
-
<ExternalLinkIcon className="w-4 h-4" />
|
|
124
|
-
<span>Open EventCatalog Studio</span>
|
|
125
|
-
</button>
|
|
126
|
-
|
|
127
|
-
<p className="text-[12px] text-gray-500 italic mt-4 mb-0">
|
|
128
|
-
All data is stored locally in your browser. Nothing is sent to external servers.
|
|
129
|
-
</p>
|
|
130
|
-
</div>
|
|
131
|
-
</div>
|
|
132
|
-
|
|
133
|
-
<div className="mt-6 flex justify-end">
|
|
134
|
-
<Dialog.Close asChild>
|
|
135
|
-
<button
|
|
136
|
-
type="button"
|
|
137
|
-
className="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-hidden focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 transition-colors"
|
|
138
|
-
onClick={() => setIsOpen(false)}
|
|
139
|
-
>
|
|
140
|
-
Close
|
|
141
|
-
</button>
|
|
142
|
-
</Dialog.Close>
|
|
143
|
-
</div>
|
|
144
|
-
</Dialog.Content>
|
|
145
|
-
</Dialog.Portal>
|
|
146
|
-
</Dialog.Root>
|
|
147
|
-
);
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
export default StudioPageModal;
|
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
import { useMemo, useState } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
createColumnHelper,
|
|
4
|
-
flexRender,
|
|
5
|
-
getCoreRowModel,
|
|
6
|
-
getSortedRowModel,
|
|
7
|
-
useReactTable,
|
|
8
|
-
type SortingState,
|
|
9
|
-
} from '@tanstack/react-table';
|
|
10
|
-
import { ArrowDown, ArrowUp, ArrowUpDown, Search, SearchX } from 'lucide-react';
|
|
11
|
-
import { getColorAndIconForCollection } from '@utils/collections/icons';
|
|
12
|
-
import { getCollectionTextColorClass } from '@utils/collection-colors';
|
|
13
|
-
import { isIconPath, resolveIconUrl } from '@utils/icon';
|
|
14
|
-
|
|
15
|
-
export type SystemResourceCollection =
|
|
16
|
-
| 'domains'
|
|
17
|
-
| 'systems'
|
|
18
|
-
| 'agents'
|
|
19
|
-
| 'services'
|
|
20
|
-
| 'flows'
|
|
21
|
-
| 'entities'
|
|
22
|
-
| 'data-products'
|
|
23
|
-
| 'containers'
|
|
24
|
-
| 'events'
|
|
25
|
-
| 'commands'
|
|
26
|
-
| 'queries';
|
|
27
|
-
|
|
28
|
-
export interface SystemResourceItem {
|
|
29
|
-
collection: SystemResourceCollection;
|
|
30
|
-
id: string;
|
|
31
|
-
name: string;
|
|
32
|
-
version: string;
|
|
33
|
-
summary?: string;
|
|
34
|
-
icon?: string;
|
|
35
|
-
href: string;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
interface SystemResourcesTableProps {
|
|
39
|
-
resources: SystemResourceItem[];
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const TYPE_LABELS: Record<SystemResourceCollection, string> = {
|
|
43
|
-
domains: 'Subdomains',
|
|
44
|
-
systems: 'Systems',
|
|
45
|
-
agents: 'Agents',
|
|
46
|
-
services: 'Services',
|
|
47
|
-
flows: 'Flows',
|
|
48
|
-
entities: 'Entities',
|
|
49
|
-
'data-products': 'Data Products',
|
|
50
|
-
containers: 'Data Stores',
|
|
51
|
-
events: 'Events',
|
|
52
|
-
commands: 'Commands',
|
|
53
|
-
queries: 'Queries',
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
// Order the type filter pills follow on the page.
|
|
57
|
-
const TYPE_ORDER: SystemResourceCollection[] = [
|
|
58
|
-
'domains',
|
|
59
|
-
'systems',
|
|
60
|
-
'agents',
|
|
61
|
-
'services',
|
|
62
|
-
'flows',
|
|
63
|
-
'entities',
|
|
64
|
-
'data-products',
|
|
65
|
-
'containers',
|
|
66
|
-
'events',
|
|
67
|
-
'commands',
|
|
68
|
-
'queries',
|
|
69
|
-
];
|
|
70
|
-
|
|
71
|
-
// Small icon for a collection type (used in filter pills and the type column).
|
|
72
|
-
const CollectionTypeIcon = ({ collection, className }: { collection: SystemResourceCollection; className?: string }) => {
|
|
73
|
-
const { color, Icon } = getColorAndIconForCollection(collection);
|
|
74
|
-
const colorClass = getCollectionTextColorClass(color, 'text-[rgb(var(--ec-icon-color))]');
|
|
75
|
-
return <Icon className={`${className ?? 'h-3.5 w-3.5'} ${colorClass}`} aria-hidden="true" />;
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const ResourceIcon = ({ item }: { item: SystemResourceItem }) => {
|
|
79
|
-
if (item.icon && isIconPath(item.icon)) {
|
|
80
|
-
return <img src={resolveIconUrl(item.icon)} alt="" className="h-5 w-5" loading="lazy" />;
|
|
81
|
-
}
|
|
82
|
-
return <CollectionTypeIcon collection={item.collection} className="h-5 w-5" />;
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
const columnHelper = createColumnHelper<SystemResourceItem>();
|
|
86
|
-
|
|
87
|
-
const SortIcon = ({ direction }: { direction: false | 'asc' | 'desc' }) => {
|
|
88
|
-
if (direction === 'asc') return <ArrowUp className="h-3.5 w-3.5" aria-hidden="true" />;
|
|
89
|
-
if (direction === 'desc') return <ArrowDown className="h-3.5 w-3.5" aria-hidden="true" />;
|
|
90
|
-
return <ArrowUpDown className="h-3.5 w-3.5 opacity-40" aria-hidden="true" />;
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
export function SystemResourcesTable({ resources }: SystemResourcesTableProps) {
|
|
94
|
-
const [search, setSearch] = useState('');
|
|
95
|
-
const [selectedTypes, setSelectedTypes] = useState<SystemResourceCollection[]>([]);
|
|
96
|
-
const [sorting, setSorting] = useState<SortingState>([{ id: 'name', desc: false }]);
|
|
97
|
-
|
|
98
|
-
// Only show type filters that actually have resources in this system.
|
|
99
|
-
const availableTypes = useMemo(
|
|
100
|
-
() => TYPE_ORDER.filter((type) => resources.some((resource) => resource.collection === type)),
|
|
101
|
-
[resources]
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
const filtered = useMemo(() => {
|
|
105
|
-
const query = search.trim().toLowerCase();
|
|
106
|
-
return resources.filter((resource) => {
|
|
107
|
-
if (selectedTypes.length > 0 && !selectedTypes.includes(resource.collection)) {
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
if (query) {
|
|
111
|
-
const haystack = `${resource.name} ${resource.id} ${resource.summary ?? ''}`.toLowerCase();
|
|
112
|
-
if (!haystack.includes(query)) return false;
|
|
113
|
-
}
|
|
114
|
-
return true;
|
|
115
|
-
});
|
|
116
|
-
}, [resources, search, selectedTypes]);
|
|
117
|
-
|
|
118
|
-
const columns = useMemo(
|
|
119
|
-
() => [
|
|
120
|
-
columnHelper.accessor('name', {
|
|
121
|
-
header: 'Name',
|
|
122
|
-
cell: (info) => {
|
|
123
|
-
const resource = info.row.original;
|
|
124
|
-
return (
|
|
125
|
-
<a href={resource.href} className="group flex items-center gap-3">
|
|
126
|
-
<span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-md border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-card-bg))]">
|
|
127
|
-
<ResourceIcon item={resource} />
|
|
128
|
-
</span>
|
|
129
|
-
<span className="text-sm font-medium text-[rgb(var(--ec-page-text))] group-hover:text-[rgb(var(--ec-accent))]">
|
|
130
|
-
{resource.name}
|
|
131
|
-
</span>
|
|
132
|
-
</a>
|
|
133
|
-
);
|
|
134
|
-
},
|
|
135
|
-
}),
|
|
136
|
-
columnHelper.accessor((row) => TYPE_LABELS[row.collection], {
|
|
137
|
-
id: 'type',
|
|
138
|
-
header: 'Type',
|
|
139
|
-
cell: (info) => {
|
|
140
|
-
const { collection } = info.row.original;
|
|
141
|
-
return (
|
|
142
|
-
<span className="inline-flex items-center gap-1.5 text-sm text-[rgb(var(--ec-page-text-muted))]">
|
|
143
|
-
<CollectionTypeIcon collection={collection} />
|
|
144
|
-
{TYPE_LABELS[collection]}
|
|
145
|
-
</span>
|
|
146
|
-
);
|
|
147
|
-
},
|
|
148
|
-
}),
|
|
149
|
-
columnHelper.accessor('version', {
|
|
150
|
-
header: 'Version',
|
|
151
|
-
cell: (info) => (
|
|
152
|
-
<span className="inline-flex items-center rounded-md border border-[rgb(var(--ec-page-border))] px-2 py-0.5 text-xs text-[rgb(var(--ec-page-text-muted))]">
|
|
153
|
-
v{info.getValue()}
|
|
154
|
-
</span>
|
|
155
|
-
),
|
|
156
|
-
}),
|
|
157
|
-
columnHelper.accessor((row) => row.summary ?? '', {
|
|
158
|
-
id: 'summary',
|
|
159
|
-
header: 'Summary',
|
|
160
|
-
enableSorting: false,
|
|
161
|
-
cell: (info) => (
|
|
162
|
-
<span className="line-clamp-2 text-sm text-[rgb(var(--ec-page-text-muted))]">{info.getValue() || '-'}</span>
|
|
163
|
-
),
|
|
164
|
-
}),
|
|
165
|
-
],
|
|
166
|
-
[]
|
|
167
|
-
);
|
|
168
|
-
|
|
169
|
-
const table = useReactTable({
|
|
170
|
-
data: filtered,
|
|
171
|
-
columns,
|
|
172
|
-
state: { sorting },
|
|
173
|
-
onSortingChange: setSorting,
|
|
174
|
-
getCoreRowModel: getCoreRowModel(),
|
|
175
|
-
getSortedRowModel: getSortedRowModel(),
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
const toggleType = (type: SystemResourceCollection) => {
|
|
179
|
-
setSelectedTypes((prev) => (prev.includes(type) ? prev.filter((t) => t !== type) : [...prev, type]));
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
return (
|
|
183
|
-
<div className="flex flex-col gap-4">
|
|
184
|
-
{/* Controls: search + type filters */}
|
|
185
|
-
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
|
186
|
-
<div className="relative w-full sm:max-w-xs">
|
|
187
|
-
<Search
|
|
188
|
-
className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-[rgb(var(--ec-icon-color))]"
|
|
189
|
-
aria-hidden="true"
|
|
190
|
-
/>
|
|
191
|
-
<input
|
|
192
|
-
type="text"
|
|
193
|
-
value={search}
|
|
194
|
-
onChange={(event) => setSearch(event.target.value)}
|
|
195
|
-
placeholder="Search resources..."
|
|
196
|
-
className="w-full rounded-md border border-[rgb(var(--ec-input-border))] bg-[rgb(var(--ec-input-bg))] py-2 pl-9 pr-3 text-sm text-[rgb(var(--ec-input-text))] placeholder:text-[rgb(var(--ec-page-text-muted))] focus:outline-none focus:ring-2 focus:ring-[rgb(var(--ec-accent)/0.4)]"
|
|
197
|
-
/>
|
|
198
|
-
</div>
|
|
199
|
-
|
|
200
|
-
{availableTypes.length > 1 && (
|
|
201
|
-
<div className="flex flex-wrap gap-2">
|
|
202
|
-
{availableTypes.map((type) => {
|
|
203
|
-
const active = selectedTypes.includes(type);
|
|
204
|
-
return (
|
|
205
|
-
<button
|
|
206
|
-
key={type}
|
|
207
|
-
type="button"
|
|
208
|
-
onClick={() => toggleType(type)}
|
|
209
|
-
className={[
|
|
210
|
-
'inline-flex items-center gap-1.5 rounded-full border px-3 py-1 text-xs font-medium transition-colors',
|
|
211
|
-
active
|
|
212
|
-
? 'border-[rgb(var(--ec-accent))] bg-[rgb(var(--ec-accent-subtle))] text-[rgb(var(--ec-accent))]'
|
|
213
|
-
: 'border-[rgb(var(--ec-page-border))] text-[rgb(var(--ec-page-text-muted))] hover:text-[rgb(var(--ec-page-text))]',
|
|
214
|
-
].join(' ')}
|
|
215
|
-
>
|
|
216
|
-
<CollectionTypeIcon collection={type} />
|
|
217
|
-
{TYPE_LABELS[type]}
|
|
218
|
-
</button>
|
|
219
|
-
);
|
|
220
|
-
})}
|
|
221
|
-
</div>
|
|
222
|
-
)}
|
|
223
|
-
</div>
|
|
224
|
-
|
|
225
|
-
{/* Table */}
|
|
226
|
-
<div className="overflow-hidden rounded-lg border border-[rgb(var(--ec-page-border))]">
|
|
227
|
-
<table className="min-w-full divide-y divide-[rgb(var(--ec-page-border))]">
|
|
228
|
-
<thead className="bg-[rgb(var(--ec-card-bg))]">
|
|
229
|
-
{table.getHeaderGroups().map((headerGroup) => (
|
|
230
|
-
<tr key={headerGroup.id}>
|
|
231
|
-
{headerGroup.headers.map((header) => {
|
|
232
|
-
const canSort = header.column.getCanSort();
|
|
233
|
-
const isSummary = header.column.id === 'summary';
|
|
234
|
-
return (
|
|
235
|
-
<th
|
|
236
|
-
key={header.id}
|
|
237
|
-
className={[
|
|
238
|
-
'px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-[rgb(var(--ec-page-text-muted))]',
|
|
239
|
-
isSummary ? 'hidden md:table-cell' : '',
|
|
240
|
-
].join(' ')}
|
|
241
|
-
>
|
|
242
|
-
{canSort ? (
|
|
243
|
-
<button
|
|
244
|
-
type="button"
|
|
245
|
-
onClick={header.column.getToggleSortingHandler()}
|
|
246
|
-
className="inline-flex items-center gap-1.5 uppercase tracking-wider hover:text-[rgb(var(--ec-page-text))]"
|
|
247
|
-
>
|
|
248
|
-
{flexRender(header.column.columnDef.header, header.getContext())}
|
|
249
|
-
<SortIcon direction={header.column.getIsSorted()} />
|
|
250
|
-
</button>
|
|
251
|
-
) : (
|
|
252
|
-
flexRender(header.column.columnDef.header, header.getContext())
|
|
253
|
-
)}
|
|
254
|
-
</th>
|
|
255
|
-
);
|
|
256
|
-
})}
|
|
257
|
-
</tr>
|
|
258
|
-
))}
|
|
259
|
-
</thead>
|
|
260
|
-
<tbody className="divide-y divide-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-page-bg))]">
|
|
261
|
-
{table.getRowModel().rows.length === 0 && (
|
|
262
|
-
<tr>
|
|
263
|
-
<td colSpan={columns.length} className="px-4 py-12">
|
|
264
|
-
<div className="flex flex-col items-center justify-center gap-2 text-center text-[rgb(var(--ec-page-text-muted))]">
|
|
265
|
-
<SearchX className="h-6 w-6" aria-hidden="true" />
|
|
266
|
-
<p className="text-sm">No resources found.</p>
|
|
267
|
-
</div>
|
|
268
|
-
</td>
|
|
269
|
-
</tr>
|
|
270
|
-
)}
|
|
271
|
-
{table.getRowModel().rows.map((row) => (
|
|
272
|
-
<tr key={row.id} className="transition-colors hover:bg-[rgb(var(--ec-card-bg))]">
|
|
273
|
-
{row.getVisibleCells().map((cell) => {
|
|
274
|
-
const isSummary = cell.column.id === 'summary';
|
|
275
|
-
return (
|
|
276
|
-
<td key={cell.id} className={['px-4 py-3', isSummary ? 'hidden max-w-md md:table-cell' : ''].join(' ')}>
|
|
277
|
-
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
278
|
-
</td>
|
|
279
|
-
);
|
|
280
|
-
})}
|
|
281
|
-
</tr>
|
|
282
|
-
))}
|
|
283
|
-
</tbody>
|
|
284
|
-
</table>
|
|
285
|
-
</div>
|
|
286
|
-
|
|
287
|
-
<p className="text-xs text-[rgb(var(--ec-page-text-muted))]">
|
|
288
|
-
Showing {filtered.length} of {resources.length} resources
|
|
289
|
-
</p>
|
|
290
|
-
</div>
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
export default SystemResourcesTable;
|