@eventcatalog/core 3.0.0-beta.2 → 3.0.0-beta.21
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/README.md +10 -0
- package/dist/__mocks__/astro-content.cjs +32 -0
- package/dist/__mocks__/astro-content.d.cts +13 -0
- package/dist/__mocks__/astro-content.d.ts +13 -0
- package/dist/__mocks__/astro-content.js +7 -0
- package/dist/analytics/analytics.cjs +1 -1
- package/dist/analytics/analytics.js +2 -2
- package/dist/analytics/log-build.cjs +1 -1
- package/dist/analytics/log-build.js +3 -3
- package/dist/{chunk-JSONCD7V.js → chunk-2FUEBPD3.js} +1 -1
- package/dist/{chunk-3W6JYTHP.js → chunk-HABY2LVH.js} +6 -2
- package/dist/{chunk-H4QHE5YZ.js → chunk-KQAMO3R4.js} +1 -1
- package/dist/chunk-Q6KRYWPV.js +44 -0
- package/dist/{chunk-PQL6O5YA.js → chunk-RRP2B7BL.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +84 -65
- package/dist/eventcatalog.config.d.cts +4 -0
- package/dist/eventcatalog.config.d.ts +4 -0
- package/dist/eventcatalog.js +45 -57
- package/dist/generate.cjs +48 -2
- package/dist/generate.js +3 -1
- package/dist/utils/cli-logger.cjs +82 -0
- package/dist/utils/cli-logger.d.cts +10 -0
- package/dist/utils/cli-logger.d.ts +10 -0
- package/dist/utils/cli-logger.js +7 -0
- package/eventcatalog/astro.config.mjs +4 -1
- package/eventcatalog/integrations/ecstudio-watcher.mjs +1 -1
- package/eventcatalog/integrations/eventcatalog-features.ts +69 -0
- package/eventcatalog/public/icons/asyncapi-black.svg +2 -0
- package/eventcatalog/public/icons/graphql-black.svg +1 -0
- package/eventcatalog/public/icons/openapi-black.svg +1 -0
- package/eventcatalog/src/components/ChatPanel/ChatPanel.tsx +821 -0
- package/eventcatalog/src/components/ChatPanel/ChatPanelButton.tsx +24 -0
- package/eventcatalog/src/components/Grids/DomainGrid.tsx +1 -3
- package/eventcatalog/src/components/Grids/MessageGrid.tsx +8 -8
- package/eventcatalog/src/components/Header.astro +25 -5
- package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.tsx +14 -3
- package/eventcatalog/src/components/Search/Search.astro +2 -2
- package/eventcatalog/src/components/Search/SearchModal.tsx +16 -7
- package/eventcatalog/src/components/SideNav/NestedSideBar/SearchBar.tsx +9 -2
- package/eventcatalog/src/components/SideNav/NestedSideBar/builders/domain.ts +7 -6
- package/eventcatalog/src/components/SideNav/NestedSideBar/builders/service.ts +6 -3
- package/eventcatalog/src/components/SideNav/NestedSideBar/builders/shared.ts +1 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +23 -8
- package/eventcatalog/src/components/SideNav/NestedSideBar/sidebar-builder.ts +57 -11
- package/eventcatalog/src/content.config.ts +1 -10
- package/eventcatalog/src/enterprise/ai/chat-api.ts +262 -0
- package/eventcatalog/src/enterprise/auth/[...auth].ts +3 -0
- package/eventcatalog/src/enterprise/auth/login.astro +420 -0
- package/eventcatalog/src/enterprise/collections/index.ts +0 -1
- package/eventcatalog/src/layouts/Footer.astro +8 -5
- package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +30 -19
- package/eventcatalog/src/pages/_index.astro +8 -9
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/asyncapi/[filename].astro +19 -3
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/index.astro +7 -7
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/graphql/[filename].astro +1 -1
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +5 -5
- package/eventcatalog/src/pages/docs/teams/[id].mdx.ts +36 -0
- package/eventcatalog/src/pages/docs/users/[id].mdx.ts +36 -0
- package/eventcatalog/src/pages/schemas/explorer/_index.data.ts +178 -0
- package/eventcatalog/src/pages/schemas/explorer/index.astro +5 -155
- package/eventcatalog/src/remark-plugins/directives.ts +30 -9
- package/eventcatalog/src/utils/collections/schemas.ts +31 -7
- package/eventcatalog/src/utils/feature.ts +8 -4
- package/eventcatalog/src/utils/resource-files.ts +86 -0
- package/package.json +12 -15
- package/default-files-for-collections/changelogs.md +0 -5
- package/default-files-for-collections/channels.md +0 -8
- package/default-files-for-collections/commands.md +0 -8
- package/default-files-for-collections/domains.md +0 -8
- package/default-files-for-collections/events.md +0 -8
- package/default-files-for-collections/flows.md +0 -11
- package/default-files-for-collections/queries.md +0 -8
- package/default-files-for-collections/services.md +0 -8
- package/default-files-for-collections/ubiquitousLanguages.md +0 -7
- package/eventcatalog/src/enterprise/collections/chat-prompts.ts +0 -32
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/Chat.tsx +0 -60
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/ChatMessage.tsx +0 -414
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/ChatSidebar.tsx +0 -169
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/InputModal.tsx +0 -244
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/MentionInput.tsx +0 -211
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/WelcomePromptArea.tsx +0 -176
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/default-prompts.ts +0 -93
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/hooks/ChatProvider.tsx +0 -143
- package/eventcatalog/src/enterprise/eventcatalog-chat/components/windows/ChatWindow.server.tsx +0 -387
- package/eventcatalog/src/enterprise/eventcatalog-chat/pages/api/chat.ts +0 -59
- package/eventcatalog/src/enterprise/eventcatalog-chat/pages/chat/index.astro +0 -104
- package/eventcatalog/src/enterprise/eventcatalog-chat/providers/ai-provider.ts +0 -140
- package/eventcatalog/src/enterprise/eventcatalog-chat/providers/anthropic.ts +0 -28
- package/eventcatalog/src/enterprise/eventcatalog-chat/providers/google.ts +0 -41
- package/eventcatalog/src/enterprise/eventcatalog-chat/providers/index.ts +0 -26
- package/eventcatalog/src/enterprise/eventcatalog-chat/providers/openai.ts +0 -61
- package/eventcatalog/src/enterprise/eventcatalog-chat/utils/chat-prompts.ts +0 -50
- package/eventcatalog/src/pages/auth/login.astro +0 -280
- package/eventcatalog/src/pages/chat/feature.astro +0 -179
- package/eventcatalog/src/pages/chat/index.astro +0 -10
- package/eventcatalog/src/pages/nav-index.json.ts +0 -30
- /package/eventcatalog/src/{pages → enterprise}/auth/error.astro +0 -0
- /package/eventcatalog/src/{middleware-auth.ts → enterprise/auth/middleware/middleware-auth.ts} +0 -0
- /package/eventcatalog/src/{middleware.ts → enterprise/auth/middleware/middleware.ts} +0 -0
- /package/eventcatalog/src/{pages/unauthorized/index.astro → enterprise/auth/unauthorized.astro} +0 -0
- /package/eventcatalog/src/{pages → enterprise}/plans/index.astro +0 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { Sparkles } from 'lucide-react';
|
|
3
|
+
import ChatPanel from './ChatPanel';
|
|
4
|
+
|
|
5
|
+
const ChatPanelButton = () => {
|
|
6
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<>
|
|
10
|
+
<button
|
|
11
|
+
onClick={() => setIsOpen(true)}
|
|
12
|
+
className="flex items-center gap-1.5 px-4 py-1.5 rounded-md bg-white hover:bg-gray-50 ring-1 ring-inset ring-gray-300 shadow-sm transition-colors text-sm ml-[-1px]"
|
|
13
|
+
aria-label="Open AI Assistant"
|
|
14
|
+
>
|
|
15
|
+
<Sparkles size={14} className="text-purple-500" />
|
|
16
|
+
<span className="font-light text-gray-600">Ask AI</span>
|
|
17
|
+
</button>
|
|
18
|
+
|
|
19
|
+
<ChatPanel isOpen={isOpen} onClose={() => setIsOpen(false)} />
|
|
20
|
+
</>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default ChatPanelButton;
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
ArrowsPointingOutIcon,
|
|
12
12
|
} from '@heroicons/react/24/outline';
|
|
13
13
|
import { buildUrl } from '@utils/url-builder';
|
|
14
|
-
import { BoxIcon
|
|
14
|
+
import { BoxIcon } from 'lucide-react';
|
|
15
15
|
|
|
16
16
|
// ============================================
|
|
17
17
|
// Types
|
|
@@ -135,7 +135,6 @@ const ServiceCard = memo(({ service }: { service: any }) => {
|
|
|
135
135
|
{/* Receives (Inbound) */}
|
|
136
136
|
<div className="flex-1 bg-blue-50 border border-blue-200 rounded-lg p-3">
|
|
137
137
|
<div className="flex items-center gap-1.5 mb-2">
|
|
138
|
-
<ArrowRight className="h-3.5 w-3.5 text-blue-500" />
|
|
139
138
|
<span className="text-xs font-semibold text-blue-700 uppercase">Inbound Messages</span>
|
|
140
139
|
<span className="text-xs text-blue-500">({receives.length})</span>
|
|
141
140
|
</div>
|
|
@@ -162,7 +161,6 @@ const ServiceCard = memo(({ service }: { service: any }) => {
|
|
|
162
161
|
{/* Sends (Outbound) */}
|
|
163
162
|
<div className="flex-1 bg-green-50 border border-green-200 rounded-lg p-3">
|
|
164
163
|
<div className="flex items-center gap-1.5 mb-2">
|
|
165
|
-
<ArrowLeft className="h-3.5 w-3.5 text-green-500" />
|
|
166
164
|
<span className="text-xs font-semibold text-green-700 uppercase">Outbound Messages</span>
|
|
167
165
|
<span className="text-xs text-green-500">({sends.length})</span>
|
|
168
166
|
</div>
|
|
@@ -41,9 +41,9 @@ export default function MessageGridV2({ service, embeded = false }: MessageGridV
|
|
|
41
41
|
);
|
|
42
42
|
|
|
43
43
|
return (
|
|
44
|
-
<div className={`rounded-xl overflow-hidden bg-
|
|
44
|
+
<div className={`rounded-xl overflow-hidden bg-white p-8 border-2 border-dashed border-pink-300`}>
|
|
45
45
|
{/* Service Title */}
|
|
46
|
-
<div className="flex items-center gap-2 mb-8">
|
|
46
|
+
{/* <div className="flex items-center gap-2 mb-8">
|
|
47
47
|
<ServerIcon className="h-6 w-6 text-pink-500" />
|
|
48
48
|
<h2 className="text-2xl font-semibold text-gray-900">{service.data.name}</h2>
|
|
49
49
|
<div className="flex gap-2 ml-auto">
|
|
@@ -60,7 +60,7 @@ export default function MessageGridV2({ service, embeded = false }: MessageGridV
|
|
|
60
60
|
Read documentation
|
|
61
61
|
</a>
|
|
62
62
|
</div>
|
|
63
|
-
</div>
|
|
63
|
+
</div> */}
|
|
64
64
|
|
|
65
65
|
<div className="grid grid-cols-3 gap-8 relative">
|
|
66
66
|
{/* Left Column - Receives Messages & Reads From Containers */}
|
|
@@ -70,7 +70,7 @@ export default function MessageGridV2({ service, embeded = false }: MessageGridV
|
|
|
70
70
|
<div className="mb-6">
|
|
71
71
|
<h2 className={`font-semibold text-gray-900 flex items-center gap-2 ${embeded ? 'text-sm' : 'text-xl'}`}>
|
|
72
72
|
<ServerIcon className="h-5 w-5 text-blue-500" />
|
|
73
|
-
|
|
73
|
+
Inbound Messages ({receives.length})
|
|
74
74
|
</h2>
|
|
75
75
|
</div>
|
|
76
76
|
{receives.length > 0 ? (
|
|
@@ -124,7 +124,7 @@ export default function MessageGridV2({ service, embeded = false }: MessageGridV
|
|
|
124
124
|
</div>
|
|
125
125
|
|
|
126
126
|
{/* Service Information (Center) */}
|
|
127
|
-
<div className="bg-
|
|
127
|
+
<div className="bg-pink-50 border-2 border-pink-100 rounded-lg p-6 flex flex-col justify-center">
|
|
128
128
|
<div className="flex flex-col items-center gap-4">
|
|
129
129
|
<ServerIcon className="h-12 w-12 text-pink-500" />
|
|
130
130
|
<p className="text-xl font-semibold text-gray-900 text-center">{service.data.name}</p>
|
|
@@ -133,11 +133,11 @@ export default function MessageGridV2({ service, embeded = false }: MessageGridV
|
|
|
133
133
|
<div className="w-full grid grid-cols-2 gap-3 mt-2">
|
|
134
134
|
<div className="text-center p-3 bg-blue-50 rounded-lg border border-blue-200">
|
|
135
135
|
<div className="text-2xl font-bold text-blue-600">{receives.length}</div>
|
|
136
|
-
<div className="text-xs text-gray-600 mt-1">
|
|
136
|
+
<div className="text-xs text-gray-600 mt-1">Inbound Messages</div>
|
|
137
137
|
</div>
|
|
138
138
|
<div className="text-center p-3 bg-green-50 rounded-lg border border-green-200">
|
|
139
139
|
<div className="text-2xl font-bold text-green-600">{sends.length}</div>
|
|
140
|
-
<div className="text-xs text-gray-600 mt-1">
|
|
140
|
+
<div className="text-xs text-gray-600 mt-1">Outbound Messages</div>
|
|
141
141
|
</div>
|
|
142
142
|
{readsFrom.length > 0 && (
|
|
143
143
|
<div className="text-center p-3 bg-orange-50 rounded-lg border border-orange-200">
|
|
@@ -168,7 +168,7 @@ export default function MessageGridV2({ service, embeded = false }: MessageGridV
|
|
|
168
168
|
<div className="mb-6">
|
|
169
169
|
<h2 className={`font-semibold text-gray-900 flex items-center gap-2 ${embeded ? 'text-sm' : 'text-xl'}`}>
|
|
170
170
|
<ServerIcon className="h-5 w-5 text-emerald-500" />
|
|
171
|
-
|
|
171
|
+
Outbound Messages ({sends.length})
|
|
172
172
|
</h2>
|
|
173
173
|
</div>
|
|
174
174
|
{sends.length > 0 ? (
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
import catalog from '@utils/eventcatalog-config/catalog';
|
|
3
3
|
import Search from '@components/Search/Search.astro';
|
|
4
4
|
import { buildUrl } from '@utils/url-builder';
|
|
5
|
-
import { showEventCatalogBranding, showCustomBranding } from '@utils/feature';
|
|
5
|
+
import { showEventCatalogBranding, showCustomBranding, isEventCatalogChatEnabled } from '@utils/feature';
|
|
6
6
|
import { getSession } from 'auth-astro/server';
|
|
7
7
|
import { isAuthEnabled, isSSR } from '@utils/feature';
|
|
8
8
|
import { EnvironmentDropdown } from './EnvironmentDropdown';
|
|
9
|
+
import ChatPanelButton from './ChatPanel/ChatPanelButton';
|
|
9
10
|
|
|
10
11
|
let session = null;
|
|
11
12
|
if (isAuthEnabled()) {
|
|
@@ -42,8 +43,11 @@ const repositoryUrl = catalog?.repositoryUrl || 'https://github.com/event-catalo
|
|
|
42
43
|
</a>
|
|
43
44
|
</div>
|
|
44
45
|
|
|
45
|
-
<div class="hidden lg:
|
|
46
|
-
<
|
|
46
|
+
<div class="hidden lg:flex flex-grow -ml-1 items-center">
|
|
47
|
+
<div class="flex-grow max-w-xl">
|
|
48
|
+
<Search />
|
|
49
|
+
</div>
|
|
50
|
+
{isEventCatalogChatEnabled() && <ChatPanelButton client:load />}
|
|
47
51
|
</div>
|
|
48
52
|
|
|
49
53
|
<div class="hidden md:block w-3/12">
|
|
@@ -60,6 +64,7 @@ const repositoryUrl = catalog?.repositoryUrl || 'https://github.com/event-catalo
|
|
|
60
64
|
class="flex items-center focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-400 rounded-full"
|
|
61
65
|
aria-expanded="false"
|
|
62
66
|
aria-haspopup="true"
|
|
67
|
+
aria-label="User menu"
|
|
63
68
|
>
|
|
64
69
|
{session.user?.image && !session.user?.image?.includes('googleusercontent.com') ? (
|
|
65
70
|
<img
|
|
@@ -117,16 +122,28 @@ const repositoryUrl = catalog?.repositoryUrl || 'https://github.com/event-catalo
|
|
|
117
122
|
<a
|
|
118
123
|
href="https://discord.com/invite/3rjaZMmrAm"
|
|
119
124
|
class="block p-1.5 rounded-lg hover:bg-gray-100 transition-colors"
|
|
125
|
+
aria-label="Join our Discord community"
|
|
120
126
|
>
|
|
121
|
-
<img
|
|
127
|
+
<img
|
|
128
|
+
src={buildUrl('/icons/discord.svg', true)}
|
|
129
|
+
alt=""
|
|
130
|
+
class="h-6 w-6 hover:opacity-100 transition-opacity"
|
|
131
|
+
aria-hidden="true"
|
|
132
|
+
/>
|
|
122
133
|
</a>
|
|
123
134
|
</li>
|
|
124
135
|
<li>
|
|
125
136
|
<a
|
|
126
137
|
href="https://github.com/event-catalog/eventcatalog"
|
|
127
138
|
class="block p-1.5 rounded-lg hover:bg-gray-100 transition-colors"
|
|
139
|
+
aria-label="View EventCatalog on GitHub"
|
|
128
140
|
>
|
|
129
|
-
<img
|
|
141
|
+
<img
|
|
142
|
+
src={buildUrl('/icons/github.svg', true)}
|
|
143
|
+
alt=""
|
|
144
|
+
class="h-6 w-6 hover:opacity-100 transition-opacity"
|
|
145
|
+
aria-hidden="true"
|
|
146
|
+
/>
|
|
130
147
|
</a>
|
|
131
148
|
</li>
|
|
132
149
|
</ul>
|
|
@@ -137,10 +154,13 @@ const repositoryUrl = catalog?.repositoryUrl || 'https://github.com/event-catalo
|
|
|
137
154
|
<a
|
|
138
155
|
href={repositoryUrl}
|
|
139
156
|
class="block p-1.5 rounded-lg hover:bg-gray-100 transition-colors focus:outline-none"
|
|
157
|
+
aria-label="View repository on GitHub"
|
|
140
158
|
>
|
|
141
159
|
<img
|
|
142
160
|
src={buildUrl('/icons/github.svg', true)}
|
|
161
|
+
alt=""
|
|
143
162
|
class="h-6 w-6 opacity-70 hover:opacity-100 transition-opacity"
|
|
163
|
+
aria-hidden="true"
|
|
144
164
|
/>
|
|
145
165
|
</a>
|
|
146
166
|
</li>
|
|
@@ -232,10 +232,21 @@ const NodeGraphBuilder = ({
|
|
|
232
232
|
};
|
|
233
233
|
|
|
234
234
|
// animate messages, between views
|
|
235
|
+
// URL parameter takes priority over localStorage
|
|
235
236
|
useEffect(() => {
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
237
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
238
|
+
const animateParam = urlParams.get('animate');
|
|
239
|
+
|
|
240
|
+
if (animateParam === 'true') {
|
|
241
|
+
setAnimateMessages(true);
|
|
242
|
+
} else if (animateParam === 'false') {
|
|
243
|
+
setAnimateMessages(false);
|
|
244
|
+
} else {
|
|
245
|
+
// Fall back to localStorage if no URL parameter
|
|
246
|
+
const storedAnimateMessages = localStorage.getItem('EventCatalog:animateMessages');
|
|
247
|
+
if (storedAnimateMessages !== null) {
|
|
248
|
+
setAnimateMessages(storedAnimateMessages === 'true');
|
|
249
|
+
}
|
|
239
250
|
}
|
|
240
251
|
}, []);
|
|
241
252
|
|
|
@@ -4,7 +4,7 @@ import SearchModal from './SearchModal.tsx';
|
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
<div>
|
|
7
|
-
<div class="relative flex items-center w-
|
|
7
|
+
<div class="relative flex items-center w-full pr-4">
|
|
8
8
|
<input
|
|
9
9
|
id="search-dummy-input"
|
|
10
10
|
type="text"
|
|
@@ -14,7 +14,7 @@ import SearchModal from './SearchModal.tsx';
|
|
|
14
14
|
class="block w-full rounded-md caret-transparent border-0 py-1.5 pr-14 pl-10 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 font-light sm:text-sm sm:leading-6 px-4"
|
|
15
15
|
/>
|
|
16
16
|
<MagnifyingGlassIcon className="absolute inset-y-0 left-0 h-9 w-8 flex items-center pl-4 text-gray-400" />
|
|
17
|
-
<div class="absolute inset-y-0 right-0 flex py-1.5 pr-
|
|
17
|
+
<div class="absolute inset-y-0 right-0 flex py-1.5 pr-6">
|
|
18
18
|
<kbd class="inline-flex items-center rounded px-1 font-sans text-xs text-gray-400">⌘K</kbd>
|
|
19
19
|
</div>
|
|
20
20
|
</div>
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
ArrowUturnLeftIcon,
|
|
21
21
|
StarIcon,
|
|
22
22
|
Square2StackIcon,
|
|
23
|
+
ArrowsRightLeftIcon,
|
|
23
24
|
} from '@heroicons/react/24/outline';
|
|
24
25
|
import { StarIcon as StarIconSolid, CircleStackIcon } from '@heroicons/react/24/solid';
|
|
25
26
|
import { useStore } from '@nanostores/react';
|
|
@@ -33,7 +34,7 @@ const typeIcons: any = {
|
|
|
33
34
|
Command: ChatBubbleLeftIcon,
|
|
34
35
|
Query: QueryIcon,
|
|
35
36
|
Entity: CubeIcon,
|
|
36
|
-
Channel:
|
|
37
|
+
Channel: ArrowsRightLeftIcon,
|
|
37
38
|
Team: UserGroupIcon,
|
|
38
39
|
User: UserIcon,
|
|
39
40
|
Language: BookOpenIcon,
|
|
@@ -68,8 +69,6 @@ function classNames(...classes: (string | boolean | undefined)[]) {
|
|
|
68
69
|
|
|
69
70
|
// Helper to construct URL from key if href is missing
|
|
70
71
|
const getUrlForItem = (node: any, key: string) => {
|
|
71
|
-
if (node.href) return node.href;
|
|
72
|
-
|
|
73
72
|
const parts = key.split(':');
|
|
74
73
|
if (parts.length < 2) return null; // Need at least type:id
|
|
75
74
|
|
|
@@ -83,8 +82,16 @@ const getUrlForItem = (node: any, key: string) => {
|
|
|
83
82
|
// Only show items that have a version to avoid duplicates
|
|
84
83
|
if (!version) return null;
|
|
85
84
|
|
|
85
|
+
// If node has href, use it, otherwise construct from key
|
|
86
|
+
if (node.href) return node.href;
|
|
87
|
+
|
|
86
88
|
// Pluralize type for URL if needed
|
|
87
|
-
|
|
89
|
+
let pluralType = type;
|
|
90
|
+
if (['event', 'command', 'domain', 'service', 'flow', 'container', 'channel'].includes(type)) {
|
|
91
|
+
pluralType = type + 's';
|
|
92
|
+
} else if (type === 'query') {
|
|
93
|
+
pluralType = 'queries';
|
|
94
|
+
}
|
|
88
95
|
|
|
89
96
|
return `/docs/${pluralType}/${id}/${version}`;
|
|
90
97
|
};
|
|
@@ -171,6 +178,7 @@ export default function SearchModal() {
|
|
|
171
178
|
Team: 0,
|
|
172
179
|
Container: 0,
|
|
173
180
|
Design: 0,
|
|
181
|
+
Channel: 0,
|
|
174
182
|
};
|
|
175
183
|
|
|
176
184
|
itemsToCount.forEach((item) => {
|
|
@@ -195,6 +203,7 @@ export default function SearchModal() {
|
|
|
195
203
|
if (counts.Service > 0) dynamicFilters.push({ id: 'Service', name: `Services (${counts.Service})` });
|
|
196
204
|
if (counts.Message > 0) dynamicFilters.push({ id: 'Message', name: `Messages (${counts.Message})` });
|
|
197
205
|
if (counts.Container > 0) dynamicFilters.push({ id: 'Container', name: `Containers (${counts.Container})` });
|
|
206
|
+
if (counts.Channel > 0) dynamicFilters.push({ id: 'Channel', name: `Channels (${counts.Channel})` });
|
|
198
207
|
if (counts.Design > 0) dynamicFilters.push({ id: 'Design', name: `Designs (${counts.Design})` });
|
|
199
208
|
if (counts.Team > 0) dynamicFilters.push({ id: 'Team', name: `Teams & Users (${counts.Team})` });
|
|
200
209
|
|
|
@@ -224,7 +233,7 @@ export default function SearchModal() {
|
|
|
224
233
|
const filteredItems = useMemo(() => {
|
|
225
234
|
if (query === '') {
|
|
226
235
|
// Show favorites when search is empty
|
|
227
|
-
if (favorites.length > 0) {
|
|
236
|
+
if (favorites.length > 0 && activeFilter === 'all') {
|
|
228
237
|
return favorites
|
|
229
238
|
.slice(0, 5)
|
|
230
239
|
.map((fav) => {
|
|
@@ -375,14 +384,14 @@ export default function SearchModal() {
|
|
|
375
384
|
<p className={classNames('text-sm font-medium', active ? 'text-gray-900' : 'text-gray-700')}>
|
|
376
385
|
{item.name}
|
|
377
386
|
</p>
|
|
378
|
-
<div className="flex items-
|
|
387
|
+
<div className="flex items-center gap-2">
|
|
379
388
|
<p
|
|
380
389
|
className={classNames('text-sm flex-shrink-0', active ? 'text-gray-700' : 'text-gray-500')}
|
|
381
390
|
>
|
|
382
391
|
{item.type}
|
|
383
392
|
</p>
|
|
384
393
|
{item.rawNode.summary && (
|
|
385
|
-
<p className={classNames('text-
|
|
394
|
+
<p className={classNames('text-sm truncate', active ? 'text-gray-600' : 'text-gray-400')}>
|
|
386
395
|
• {item.rawNode.summary}
|
|
387
396
|
</p>
|
|
388
397
|
)}
|
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
Database,
|
|
15
15
|
Waypoints,
|
|
16
16
|
SquareMousePointer,
|
|
17
|
+
ListOrdered,
|
|
18
|
+
ArrowLeftRight,
|
|
17
19
|
} from 'lucide-react';
|
|
18
20
|
import type { NavNode } from './sidebar-builder';
|
|
19
21
|
|
|
@@ -28,6 +30,7 @@ const getBadgeClasses = (badge: string): string => {
|
|
|
28
30
|
query: 'bg-purple-100 text-purple-700',
|
|
29
31
|
message: 'bg-indigo-100 text-indigo-700',
|
|
30
32
|
design: 'bg-teal-100 text-teal-700',
|
|
33
|
+
channel: 'bg-indigo-100 text-indigo-700',
|
|
31
34
|
};
|
|
32
35
|
return badgeColors[badge.toLowerCase()] || 'bg-gray-100 text-gray-600';
|
|
33
36
|
};
|
|
@@ -77,6 +80,7 @@ export default function SearchBar({ nodes, onSelectResult, onSearchChange }: Pro
|
|
|
77
80
|
};
|
|
78
81
|
|
|
79
82
|
const filterTypes = [
|
|
83
|
+
{ key: 'channel', label: 'Channels', badge: 'Channel', icon: ArrowLeftRight },
|
|
80
84
|
{ key: 'command', label: 'Commands', badge: 'Command', icon: MessageSquare },
|
|
81
85
|
{ key: 'container', label: 'Data Stores', badge: 'Container', icon: Database },
|
|
82
86
|
{ key: 'design', label: 'Designs', badge: 'Design', icon: SquareMousePointer },
|
|
@@ -174,8 +178,9 @@ export default function SearchBar({ nodes, onSelectResult, onSearchChange }: Pro
|
|
|
174
178
|
<button
|
|
175
179
|
onClick={clearSearch}
|
|
176
180
|
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
|
181
|
+
aria-label="Clear search"
|
|
177
182
|
>
|
|
178
|
-
<X className="w-4 h-4" />
|
|
183
|
+
<X className="w-4 h-4" aria-hidden="true" />
|
|
179
184
|
</button>
|
|
180
185
|
)}
|
|
181
186
|
</div>
|
|
@@ -189,8 +194,10 @@ export default function SearchBar({ nodes, onSelectResult, onSearchChange }: Pro
|
|
|
189
194
|
? 'bg-purple-50 border-purple-200 text-purple-600'
|
|
190
195
|
: 'bg-gray-50 border-gray-200 text-gray-400 hover:text-gray-600 hover:bg-gray-100'
|
|
191
196
|
)}
|
|
197
|
+
aria-label="Filter search results"
|
|
198
|
+
aria-expanded={showFilterDropdown}
|
|
192
199
|
>
|
|
193
|
-
<SlidersHorizontal className="w-4 h-4" />
|
|
200
|
+
<SlidersHorizontal className="w-4 h-4" aria-hidden="true" />
|
|
194
201
|
{searchFilters.size > 0 && (
|
|
195
202
|
<span className="absolute -top-1 -right-1 w-4 h-4 bg-purple-600 text-white text-[10px] font-bold rounded-full flex items-center justify-center">
|
|
196
203
|
{searchFilters.size}
|
|
@@ -65,6 +65,12 @@ export const buildDomainNode = (domain: CollectionEntry<'domains'>, owners: any[
|
|
|
65
65
|
},
|
|
66
66
|
].filter(Boolean) as ChildRef[],
|
|
67
67
|
},
|
|
68
|
+
renderSubDomains && {
|
|
69
|
+
type: 'group',
|
|
70
|
+
title: 'Subdomains',
|
|
71
|
+
icon: 'Boxes',
|
|
72
|
+
pages: subDomains.map((domain) => `domain:${(domain as any).data.id}:${(domain as any).data.version}`),
|
|
73
|
+
},
|
|
68
74
|
hasFlows && {
|
|
69
75
|
type: 'group',
|
|
70
76
|
title: 'Flows',
|
|
@@ -81,12 +87,7 @@ export const buildDomainNode = (domain: CollectionEntry<'domains'>, owners: any[
|
|
|
81
87
|
href: buildUrl(`/docs/entities/${(entity as any).data.id}/${(entity as any).data.version}`),
|
|
82
88
|
})),
|
|
83
89
|
},
|
|
84
|
-
|
|
85
|
-
type: 'group',
|
|
86
|
-
title: 'Subdomains',
|
|
87
|
-
icon: 'Boxes',
|
|
88
|
-
pages: subDomains.map((domain) => `domain:${(domain as any).data.id}:${(domain as any).data.version}`),
|
|
89
|
-
},
|
|
90
|
+
|
|
90
91
|
...(hasResourceGroups ? buildResourceGroupSections(resourceGroups, context) : []),
|
|
91
92
|
renderServices && {
|
|
92
93
|
type: 'group',
|
|
@@ -80,21 +80,24 @@ export const buildServiceNode = (service: CollectionEntry<'services'>, owners: a
|
|
|
80
80
|
pages: [
|
|
81
81
|
...openAPISpecifications.map((specification) => ({
|
|
82
82
|
type: 'item',
|
|
83
|
-
title: `${specification.name}
|
|
83
|
+
title: `${specification.name}`,
|
|
84
|
+
leftIcon: '/icons/openapi-black.svg',
|
|
84
85
|
href: buildUrl(
|
|
85
86
|
`/docs/services/${service.data.id}/${service.data.version}/spec/${specification.filenameWithoutExtension}`
|
|
86
87
|
),
|
|
87
88
|
})),
|
|
88
89
|
...asyncAPISpecifications.map((specification) => ({
|
|
89
90
|
type: 'item',
|
|
90
|
-
title: `${specification.name}
|
|
91
|
+
title: `${specification.name}`,
|
|
92
|
+
leftIcon: '/icons/asyncapi-black.svg',
|
|
91
93
|
href: buildUrl(
|
|
92
94
|
`/docs/services/${service.data.id}/${service.data.version}/asyncapi/${specification.filenameWithoutExtension}`
|
|
93
95
|
),
|
|
94
96
|
})),
|
|
95
97
|
...graphQLSpecifications.map((specification) => ({
|
|
96
98
|
type: 'item',
|
|
97
|
-
title: `${specification.name}
|
|
99
|
+
title: `${specification.name}`,
|
|
100
|
+
leftIcon: '/icons/graphql-black.svg',
|
|
98
101
|
href: buildUrl(
|
|
99
102
|
`/docs/services/${service.data.id}/${service.data.version}/graphql/${specification.filenameWithoutExtension}`
|
|
100
103
|
),
|
|
@@ -17,6 +17,7 @@ export type NavNode = {
|
|
|
17
17
|
type: 'group' | 'item';
|
|
18
18
|
title: string;
|
|
19
19
|
icon?: string; // Lucide icon name
|
|
20
|
+
leftIcon?: string; // Path to SVG icon shown on the left of the label
|
|
20
21
|
href?: string; // URL (for leaf items)
|
|
21
22
|
external?: boolean; // If true, the item will open in a new tab
|
|
22
23
|
pages?: ChildRef[]; // Can mix keys and inline nodes
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState, useEffect, useCallback, useMemo } from 'react';
|
|
4
4
|
import * as LucideIcons from 'lucide-react';
|
|
5
|
-
import { ChevronRight, ChevronLeft, ChevronDown, Home, Star } from 'lucide-react';
|
|
5
|
+
import { ChevronRight, ChevronLeft, ChevronDown, Home, Star, FileQuestion } from 'lucide-react';
|
|
6
6
|
import type { NavigationData, NavNode, ChildRef } from './sidebar-builder';
|
|
7
7
|
import SearchBar from './SearchBar';
|
|
8
8
|
import { saveState, loadState, saveCollapsedSections, loadCollapsedSections } from './storage';
|
|
@@ -25,6 +25,7 @@ const getBadgeClasses = (badge: string): string => {
|
|
|
25
25
|
query: 'bg-purple-100 text-purple-700',
|
|
26
26
|
message: 'bg-indigo-100 text-indigo-700',
|
|
27
27
|
design: 'bg-teal-100 text-teal-700',
|
|
28
|
+
channel: 'bg-indigo-100 text-indigo-700',
|
|
28
29
|
};
|
|
29
30
|
return badgeColors[badge.toLowerCase()] || 'bg-gray-100 text-gray-600';
|
|
30
31
|
};
|
|
@@ -775,6 +776,7 @@ export default function NestedSideBar() {
|
|
|
775
776
|
<IconComponent className="w-4 h-4" />
|
|
776
777
|
</span>
|
|
777
778
|
)}
|
|
779
|
+
{item.leftIcon && <img src={item.leftIcon} alt="" className="w-4 h-4 flex-shrink-0" />}
|
|
778
780
|
<span
|
|
779
781
|
className={cn(
|
|
780
782
|
'text-[13px] truncate',
|
|
@@ -786,17 +788,17 @@ export default function NestedSideBar() {
|
|
|
786
788
|
</div>
|
|
787
789
|
<div className="flex items-center gap-1 flex-shrink-0">
|
|
788
790
|
{canFavorite && (
|
|
789
|
-
<
|
|
791
|
+
<div
|
|
790
792
|
onClick={handleStarClick}
|
|
791
793
|
className={cn(
|
|
792
|
-
'flex items-center justify-center w-5 h-5 rounded transition-colors',
|
|
794
|
+
'flex items-center justify-center w-5 h-5 rounded transition-colors cursor-pointer',
|
|
793
795
|
isFav
|
|
794
796
|
? 'text-amber-400 hover:text-amber-500'
|
|
795
797
|
: 'text-gray-300 opacity-0 group-hover:opacity-100 hover:text-amber-400'
|
|
796
798
|
)}
|
|
797
799
|
>
|
|
798
800
|
<Star className={cn('w-3.5 h-3.5', isFav && 'fill-current')} />
|
|
799
|
-
</
|
|
801
|
+
</div>
|
|
800
802
|
)}
|
|
801
803
|
{itemHasChildren && (
|
|
802
804
|
<span className="flex items-center justify-center w-5 h-5 text-gray-400 group-hover:text-black group-hover:translate-x-0.5 transition-transform">
|
|
@@ -1038,15 +1040,15 @@ export default function NestedSideBar() {
|
|
|
1038
1040
|
{fav.badge}
|
|
1039
1041
|
</span>
|
|
1040
1042
|
)}
|
|
1041
|
-
<
|
|
1043
|
+
<div
|
|
1042
1044
|
onClick={(e) => {
|
|
1043
1045
|
e.stopPropagation();
|
|
1044
1046
|
if (node) toggleFavorite(fav.nodeKey, node);
|
|
1045
1047
|
}}
|
|
1046
|
-
className="flex items-center justify-center w-5 h-5 text-amber-400 hover:text-amber-500 rounded transition-colors"
|
|
1048
|
+
className="flex items-center justify-center w-5 h-5 text-amber-400 hover:text-amber-500 rounded transition-colors cursor-pointer"
|
|
1047
1049
|
>
|
|
1048
1050
|
<Star className="w-3.5 h-3.5 fill-current" />
|
|
1049
|
-
</
|
|
1051
|
+
</div>
|
|
1050
1052
|
{node?.pages && node.pages.length > 0 && (
|
|
1051
1053
|
<span className="flex items-center justify-center w-5 h-5 text-gray-400 group-hover:text-black">
|
|
1052
1054
|
<ChevronRight className="w-4 h-4" />
|
|
@@ -1060,7 +1062,20 @@ export default function NestedSideBar() {
|
|
|
1060
1062
|
</div>
|
|
1061
1063
|
)}
|
|
1062
1064
|
|
|
1063
|
-
{
|
|
1065
|
+
{/* Empty State */}
|
|
1066
|
+
{currentLevel.entries.length === 0 && favorites.length === 0 && (
|
|
1067
|
+
<div className="flex flex-col items-center justify-center px-6 py-12 text-center">
|
|
1068
|
+
<div className="mb-4 p-3 rounded-full bg-gray-100">
|
|
1069
|
+
<FileQuestion className="w-8 h-8 text-gray-400" />
|
|
1070
|
+
</div>
|
|
1071
|
+
<h3 className="text-sm font-semibold text-gray-900 mb-2">Your catalog is empty</h3>
|
|
1072
|
+
<p className="text-xs text-gray-500 leading-relaxed max-w-[240px]">
|
|
1073
|
+
Navigation will appear here when you add resources to your EventCatalog.
|
|
1074
|
+
</p>
|
|
1075
|
+
</div>
|
|
1076
|
+
)}
|
|
1077
|
+
|
|
1078
|
+
{currentLevel.entries.length > 0 && renderEntries(currentLevel.entries)}
|
|
1064
1079
|
</nav>
|
|
1065
1080
|
</>
|
|
1066
1081
|
)}
|
|
@@ -15,6 +15,7 @@ import { buildContainerNode } from './builders/container';
|
|
|
15
15
|
import { buildFlowNode } from './builders/flow';
|
|
16
16
|
import config from '@config';
|
|
17
17
|
import { getDesigns } from '@utils/collections/designs';
|
|
18
|
+
import { getChannels } from '@utils/collections/channels';
|
|
18
19
|
|
|
19
20
|
export type { NavigationData, NavNode, ChildRef };
|
|
20
21
|
|
|
@@ -29,16 +30,18 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
|
|
|
29
30
|
return memoryCache;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
const [domains, services, { events, commands, queries }, containers, flows, users, teams, designs] =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
33
|
+
const [domains, services, { events, commands, queries }, containers, flows, users, teams, designs, channels] =
|
|
34
|
+
await Promise.all([
|
|
35
|
+
getDomains({ getAllVersions: false, includeServicesInSubdomains: false }),
|
|
36
|
+
getServices({ getAllVersions: false }),
|
|
37
|
+
getMessages({ getAllVersions: false }),
|
|
38
|
+
getContainers({ getAllVersions: false }),
|
|
39
|
+
getFlows({ getAllVersions: false }),
|
|
40
|
+
getUsers(),
|
|
41
|
+
getTeams(),
|
|
42
|
+
getDesigns(),
|
|
43
|
+
getChannels({ getAllVersions: false }),
|
|
44
|
+
]);
|
|
42
45
|
|
|
43
46
|
// Calculate derived lists to avoid extra fetches
|
|
44
47
|
const allSubDomainIds = new Set(domains.flatMap((d) => (d.data.domains || []).map((sd: any) => sd.data.id)));
|
|
@@ -178,6 +181,30 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
|
|
|
178
181
|
{} as Record<string, NavNode>
|
|
179
182
|
);
|
|
180
183
|
|
|
184
|
+
const channelNodes = channels.reduce(
|
|
185
|
+
(acc, channel) => {
|
|
186
|
+
acc[`channel:${channel.data.id}:${channel.data.version}`] = {
|
|
187
|
+
type: 'item',
|
|
188
|
+
title: channel.data.name,
|
|
189
|
+
badge: 'Channel',
|
|
190
|
+
summary: channel.data.summary,
|
|
191
|
+
href: buildUrl(`/docs/${channel.collection}/${channel.data.id}/${channel.data.version}`),
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
if (channel.data.latestVersion === channel.data.version) {
|
|
195
|
+
acc[`channel:${channel.data.id}`] = {
|
|
196
|
+
type: 'item',
|
|
197
|
+
title: channel.data.name,
|
|
198
|
+
badge: 'Channel',
|
|
199
|
+
summary: channel.data.summary,
|
|
200
|
+
href: buildUrl(`/docs/${channel.collection}/${channel.data.id}/${channel.data.version}`),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
return acc;
|
|
204
|
+
},
|
|
205
|
+
{} as Record<string, NavNode>
|
|
206
|
+
);
|
|
207
|
+
|
|
181
208
|
const teamNodes = teams.reduce(
|
|
182
209
|
(acc, team) => {
|
|
183
210
|
acc[`team:${team.data.id}`] = {
|
|
@@ -273,6 +300,13 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
|
|
|
273
300
|
pages: users.map((user) => `user:${user.data.id}`),
|
|
274
301
|
});
|
|
275
302
|
|
|
303
|
+
const channelList = createLeaf(channels, {
|
|
304
|
+
type: 'item',
|
|
305
|
+
title: 'Channels',
|
|
306
|
+
icon: 'ArrowRightLeft',
|
|
307
|
+
pages: channels.map((channel) => `channel:${channel.data.id}:${channel.data.version}`),
|
|
308
|
+
});
|
|
309
|
+
|
|
276
310
|
const messagesChildren = ['list:events', 'list:commands', 'list:queries'].filter(
|
|
277
311
|
(key, index) => [eventsList, commandsList, queriesList][index] !== undefined
|
|
278
312
|
);
|
|
@@ -303,12 +337,22 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
|
|
|
303
337
|
'list:domains',
|
|
304
338
|
'list:services',
|
|
305
339
|
'list:messages',
|
|
340
|
+
'list:channels',
|
|
306
341
|
'list:flows',
|
|
307
342
|
'list:containers',
|
|
308
343
|
'list:designs',
|
|
309
344
|
'list:people',
|
|
310
345
|
];
|
|
311
|
-
const allChildrenNodes = [
|
|
346
|
+
const allChildrenNodes = [
|
|
347
|
+
domainsList,
|
|
348
|
+
servicesList,
|
|
349
|
+
messagesList,
|
|
350
|
+
channelList,
|
|
351
|
+
flowsList,
|
|
352
|
+
containersList,
|
|
353
|
+
designsList,
|
|
354
|
+
peopleList,
|
|
355
|
+
];
|
|
312
356
|
|
|
313
357
|
const validAllChildren = allChildrenKeys.filter((_, idx) => allChildrenNodes[idx] !== undefined);
|
|
314
358
|
|
|
@@ -334,6 +378,7 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
|
|
|
334
378
|
...(designsList ? { 'list:designs': designsList } : {}),
|
|
335
379
|
...(teamsList ? { 'list:teams': teamsList } : {}),
|
|
336
380
|
...(usersList ? { 'list:users': usersList } : {}),
|
|
381
|
+
...(channelList ? { 'list:channels': channelList as NavNode } : {}),
|
|
337
382
|
...(peopleList ? { 'list:people': peopleList as NavNode } : {}),
|
|
338
383
|
...(allList ? { 'list:all': allList as NavNode } : {}),
|
|
339
384
|
};
|
|
@@ -343,6 +388,7 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
|
|
|
343
388
|
...domainNodes,
|
|
344
389
|
...serviceNodes,
|
|
345
390
|
...messageNodes,
|
|
391
|
+
...channelNodes,
|
|
346
392
|
...containerNodes,
|
|
347
393
|
...flowNodes,
|
|
348
394
|
...userNodes,
|