@eventcatalog/core 3.5.2 → 3.6.1
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 +1 -1
- 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-YVX5C6L3.js → chunk-FCIJEGOL.js} +1 -1
- package/dist/{chunk-WO3AKJVB.js → chunk-N2VBSHPU.js} +1 -1
- package/dist/{chunk-OKWCSRLE.js → chunk-OFHFRJ42.js} +1 -1
- package/dist/{chunk-YOFNY2RC.js → chunk-SI6IEUYS.js} +1 -1
- package/dist/{chunk-YTZSPYJN.js → chunk-XRLZZXIS.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +1 -1
- package/dist/eventcatalog.js +5 -5
- package/dist/generate.cjs +1 -1
- package/dist/generate.js +3 -3
- package/dist/utils/cli-logger.cjs +1 -1
- package/dist/utils/cli-logger.js +2 -2
- package/eventcatalog/astro.config.mjs +2 -1
- package/eventcatalog/src/components/EnvironmentDropdown.tsx +1 -1
- package/eventcatalog/src/components/MDX/ResourceRef/ResourceRef.astro +477 -0
- package/eventcatalog/src/components/MDX/components.tsx +2 -0
- package/eventcatalog/src/components/Search/SearchDataLoader.astro +23 -11
- package/eventcatalog/src/components/Search/SearchModal.tsx +17 -2
- package/eventcatalog/src/components/SideNav/NestedSideBar/SearchBar.tsx +12 -6
- package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +37 -16
- package/eventcatalog/src/components/Tables/Discover/DiscoverTable.tsx +816 -0
- package/eventcatalog/src/components/Tables/Discover/FilterComponents.tsx +161 -0
- package/eventcatalog/src/components/Tables/Discover/columns.tsx +565 -0
- package/eventcatalog/src/components/Tables/Discover/index.ts +4 -0
- package/eventcatalog/src/components/Tables/columns/ContainersTableColumns.tsx +1 -1
- package/eventcatalog/src/components/Tables/columns/DomainTableColumns.tsx +1 -1
- package/eventcatalog/src/components/Tables/columns/FlowTableColumns.tsx +1 -1
- package/eventcatalog/src/components/Tables/columns/MessageTableColumns.tsx +1 -1
- package/eventcatalog/src/components/Tables/columns/ServiceTableColumns.tsx +54 -64
- package/eventcatalog/src/components/Tables/columns/SharedColumns.tsx +15 -30
- package/eventcatalog/src/enterprise/plans/index.astro +125 -98
- package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +1 -1
- package/eventcatalog/src/pages/api/sidebar-data.json.ts +22 -0
- package/eventcatalog/src/pages/discover/[type]/_index.data.ts +5 -1
- package/eventcatalog/src/pages/discover/[type]/index.astro +360 -41
- package/eventcatalog/src/pages/docs/custom/feature.astro +45 -39
- package/eventcatalog/src/remark-plugins/resource-ref.ts +51 -0
- package/eventcatalog/src/stores/sidebar-store/builders/shared.ts +1 -1
- package/eventcatalog/src/stores/sidebar-store/state.ts +25 -22
- package/package.json +3 -2
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
---
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
2
|
+
import { QueueListIcon, RectangleGroupIcon, BoltIcon, ChatBubbleLeftIcon } from '@heroicons/react/24/outline';
|
|
3
|
+
import ServerIcon from '@heroicons/react/24/outline/ServerIcon';
|
|
4
|
+
import { MagnifyingGlassIcon } from '@heroicons/react/20/solid';
|
|
5
|
+
import { DatabaseIcon } from 'lucide-react';
|
|
6
|
+
import { getCommands } from '@utils/collections/commands';
|
|
7
|
+
import { getDomains, getDomainsForService } from '@utils/collections/domains';
|
|
8
|
+
import { getFlows } from '@utils/collections/flows';
|
|
9
|
+
import { getEvents } from '@utils/collections/events';
|
|
10
|
+
import { getServices } from '@utils/collections/services';
|
|
11
|
+
import { getQueries } from '@utils/collections/queries';
|
|
12
|
+
import { getContainers } from '@utils/collections/containers';
|
|
13
|
+
import { getUsers } from '@utils/collections/users';
|
|
14
|
+
import { getTeams } from '@utils/collections/teams';
|
|
15
|
+
import { buildUrl } from '@utils/url-builder';
|
|
16
|
+
import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
|
|
17
|
+
import { DiscoverTable, type DiscoverTableData, type CollectionType } from '@components/Tables/Discover';
|
|
18
|
+
import config from '@config';
|
|
5
19
|
import { Page } from './_index.data';
|
|
6
20
|
|
|
7
21
|
export const prerender = Page.prerender;
|
|
@@ -9,12 +23,165 @@ export const getStaticPaths = Page.getStaticPaths;
|
|
|
9
23
|
|
|
10
24
|
const { type, data } = await Page.getData(Astro);
|
|
11
25
|
|
|
12
|
-
|
|
26
|
+
// Fetch all collections for tabs
|
|
27
|
+
const events = await getEvents();
|
|
28
|
+
const queries = await getQueries();
|
|
29
|
+
const commands = await getCommands();
|
|
30
|
+
const services = await getServices();
|
|
31
|
+
const domains = await getDomains({ getAllVersions: false });
|
|
32
|
+
const flows = await getFlows();
|
|
33
|
+
const containers = await getContainers();
|
|
34
|
+
const users = await getUsers();
|
|
35
|
+
const teams = await getTeams();
|
|
13
36
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
37
|
+
// Create lookup maps for users and teams
|
|
38
|
+
const userMap = new Map(users.map((u) => [u.data.id, u]));
|
|
39
|
+
const teamMap = new Map(teams.map((t) => [t.data.id, t]));
|
|
40
|
+
|
|
41
|
+
// Type configuration for property options
|
|
42
|
+
const typeConfig: Record<
|
|
43
|
+
string,
|
|
44
|
+
{
|
|
45
|
+
label: string;
|
|
46
|
+
propertyOptions: Array<{ id: string; label: string }>;
|
|
47
|
+
}
|
|
48
|
+
> = {
|
|
49
|
+
events: {
|
|
50
|
+
label: 'Events',
|
|
51
|
+
propertyOptions: [
|
|
52
|
+
{ id: 'hasProducers', label: 'Has Producers' },
|
|
53
|
+
{ id: 'hasConsumers', label: 'Has Consumers' },
|
|
54
|
+
{ id: 'hasOwners', label: 'Has Owners' },
|
|
55
|
+
{ id: 'isDeprecated', label: 'Is Deprecated' },
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
commands: {
|
|
59
|
+
label: 'Commands',
|
|
60
|
+
propertyOptions: [
|
|
61
|
+
{ id: 'hasProducers', label: 'Has Producers' },
|
|
62
|
+
{ id: 'hasConsumers', label: 'Has Consumers' },
|
|
63
|
+
{ id: 'hasOwners', label: 'Has Owners' },
|
|
64
|
+
{ id: 'isDeprecated', label: 'Is Deprecated' },
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
queries: {
|
|
68
|
+
label: 'Queries',
|
|
69
|
+
propertyOptions: [
|
|
70
|
+
{ id: 'hasProducers', label: 'Has Producers' },
|
|
71
|
+
{ id: 'hasConsumers', label: 'Has Consumers' },
|
|
72
|
+
{ id: 'hasOwners', label: 'Has Owners' },
|
|
73
|
+
{ id: 'isDeprecated', label: 'Is Deprecated' },
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
domains: {
|
|
77
|
+
label: 'Domains',
|
|
78
|
+
propertyOptions: [
|
|
79
|
+
{ id: 'hasServices', label: 'Has Services' },
|
|
80
|
+
{ id: 'hasOwners', label: 'Has Owners' },
|
|
81
|
+
{ id: 'isSubdomain', label: 'Is Subdomain' },
|
|
82
|
+
{ id: 'isDeprecated', label: 'Is Deprecated' },
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
services: {
|
|
86
|
+
label: 'Services',
|
|
87
|
+
propertyOptions: [
|
|
88
|
+
{ id: 'hasSpecifications', label: 'Has Specifications' },
|
|
89
|
+
{ id: 'hasOwners', label: 'Has Owners' },
|
|
90
|
+
{ id: 'hasRepository', label: 'Has Repository' },
|
|
91
|
+
{ id: 'hasDataDependencies', label: 'Has Data Dependencies' },
|
|
92
|
+
{ id: 'isDeprecated', label: 'Is Deprecated' },
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
flows: {
|
|
96
|
+
label: 'Flows',
|
|
97
|
+
propertyOptions: [
|
|
98
|
+
{ id: 'hasOwners', label: 'Has Owners' },
|
|
99
|
+
{ id: 'isDeprecated', label: 'Is Deprecated' },
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
containers: {
|
|
103
|
+
label: 'Data',
|
|
104
|
+
propertyOptions: [
|
|
105
|
+
{ id: 'hasOwners', label: 'Has Owners' },
|
|
106
|
+
{ id: 'hasWriters', label: 'Has Writers' },
|
|
107
|
+
{ id: 'hasReaders', label: 'Has Readers' },
|
|
108
|
+
{ id: 'isDeprecated', label: 'Is Deprecated' },
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const currentTypeConfig = typeConfig[type] || typeConfig.events;
|
|
114
|
+
|
|
115
|
+
// @ts-ignore
|
|
116
|
+
const tableConfiguration = config[type as keyof typeof config]?.tableConfiguration ?? { columns: {} };
|
|
17
117
|
|
|
118
|
+
const tabs = [
|
|
119
|
+
{
|
|
120
|
+
label: `Domains (${domains.length})`,
|
|
121
|
+
href: buildUrl('/discover/domains'),
|
|
122
|
+
isActive: type === 'domains',
|
|
123
|
+
icon: RectangleGroupIcon,
|
|
124
|
+
activeColor: 'yellow',
|
|
125
|
+
enabled: domains.length > 0,
|
|
126
|
+
visible: domains.length > 0,
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
label: `Services (${services.length})`,
|
|
130
|
+
href: buildUrl('/discover/services'),
|
|
131
|
+
isActive: type === 'services',
|
|
132
|
+
icon: ServerIcon,
|
|
133
|
+
activeColor: 'pink',
|
|
134
|
+
enabled: services.length > 0,
|
|
135
|
+
visible: services.length > 0,
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
label: `Data (${containers.length})`,
|
|
139
|
+
href: buildUrl('/discover/containers'),
|
|
140
|
+
isActive: type === 'containers',
|
|
141
|
+
icon: DatabaseIcon,
|
|
142
|
+
activeColor: 'blue',
|
|
143
|
+
enabled: containers.length > 0,
|
|
144
|
+
visible: containers.length > 0,
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
label: `Events (${events.length})`,
|
|
148
|
+
href: buildUrl('/discover/events'),
|
|
149
|
+
isActive: type === 'events',
|
|
150
|
+
icon: BoltIcon,
|
|
151
|
+
activeColor: 'orange',
|
|
152
|
+
enabled: events.length > 0,
|
|
153
|
+
visible: events.length > 0,
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
label: `Queries (${queries.length})`,
|
|
157
|
+
href: buildUrl('/discover/queries'),
|
|
158
|
+
isActive: type === 'queries',
|
|
159
|
+
icon: MagnifyingGlassIcon,
|
|
160
|
+
activeColor: 'green',
|
|
161
|
+
enabled: queries.length > 0,
|
|
162
|
+
visible: queries.length > 0,
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
label: `Commands (${commands.length})`,
|
|
166
|
+
href: buildUrl('/discover/commands'),
|
|
167
|
+
isActive: type === 'commands',
|
|
168
|
+
icon: ChatBubbleLeftIcon,
|
|
169
|
+
activeColor: 'blue',
|
|
170
|
+
enabled: commands.length > 0,
|
|
171
|
+
visible: commands.length > 0,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
label: `Flows (${flows.length})`,
|
|
175
|
+
href: buildUrl('/discover/flows'),
|
|
176
|
+
isActive: type === 'flows',
|
|
177
|
+
icon: QueueListIcon,
|
|
178
|
+
activeColor: 'teal',
|
|
179
|
+
enabled: flows.length > 0,
|
|
180
|
+
visible: flows.length > 0,
|
|
181
|
+
},
|
|
182
|
+
];
|
|
183
|
+
|
|
184
|
+
// Map data to match the expected structure for the table
|
|
18
185
|
function mapToItem(i: any) {
|
|
19
186
|
return {
|
|
20
187
|
collection: i.collection,
|
|
@@ -25,39 +192,191 @@ function mapToItem(i: any) {
|
|
|
25
192
|
},
|
|
26
193
|
};
|
|
27
194
|
}
|
|
195
|
+
|
|
196
|
+
// Helper to map owner references with type detection
|
|
197
|
+
function mapOwner(o: any) {
|
|
198
|
+
if (!o) return null;
|
|
199
|
+
const id = typeof o === 'string' ? o : o.data?.id || o.id || o;
|
|
200
|
+
|
|
201
|
+
// Look up in users first, then teams
|
|
202
|
+
const user = userMap.get(id);
|
|
203
|
+
if (user) {
|
|
204
|
+
return { id, name: user.data.name || id, type: 'user' as const };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const team = teamMap.get(id);
|
|
208
|
+
if (team) {
|
|
209
|
+
return { id, name: team.data.name || id, type: 'team' as const };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Fallback if not found in either
|
|
213
|
+
const name = typeof o === 'string' ? o : o.data?.name || o.name || id;
|
|
214
|
+
return { id, name, type: 'user' as const };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Helper to check if service has specifications
|
|
218
|
+
function hasSpecifications(service: any): boolean {
|
|
219
|
+
const specs = service.data?.specifications;
|
|
220
|
+
if (!specs) return false;
|
|
221
|
+
if (Array.isArray(specs)) return specs.length > 0;
|
|
222
|
+
return !!(specs.openapiPath || specs.asyncapiPath || specs.graphqlPath);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Build a Set of subdomain IDs (domains that are nested within other domains)
|
|
226
|
+
const allSubdomainIds = new Set(
|
|
227
|
+
domains.flatMap((d: any) => (d.data?.domains || []).map((sd: any) => sd.data?.id || sd.id)).filter(Boolean)
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
// For services, enrich with domain information
|
|
231
|
+
const enrichedData =
|
|
232
|
+
type === 'services'
|
|
233
|
+
? await Promise.all(
|
|
234
|
+
data.map(async (service: any) => {
|
|
235
|
+
const serviceDomains = await getDomainsForService(service);
|
|
236
|
+
return {
|
|
237
|
+
...service,
|
|
238
|
+
enrichedDomains: serviceDomains.map((d: any) => ({
|
|
239
|
+
id: d.data.id,
|
|
240
|
+
name: d.data.name,
|
|
241
|
+
version: d.data.version,
|
|
242
|
+
})),
|
|
243
|
+
};
|
|
244
|
+
})
|
|
245
|
+
)
|
|
246
|
+
: data;
|
|
247
|
+
|
|
248
|
+
const tableData = enrichedData.map((d: any) => ({
|
|
249
|
+
collection: d.collection,
|
|
250
|
+
owners: (d.data?.owners || []).map(mapOwner).filter(Boolean),
|
|
251
|
+
hasOwners: (d.data?.owners || []).length > 0,
|
|
252
|
+
hasServices: type === 'domains' ? (d.data?.services || []).length > 0 : false,
|
|
253
|
+
isSubdomain: type === 'domains' ? allSubdomainIds.has(d.data.id) : false,
|
|
254
|
+
// Service-specific properties
|
|
255
|
+
domains: type === 'services' ? d.enrichedDomains : undefined,
|
|
256
|
+
hasSpecifications: type === 'services' ? hasSpecifications(d) : false,
|
|
257
|
+
hasRepository: type === 'services' ? !!d.data?.repository?.url : false,
|
|
258
|
+
hasDataDependencies: type === 'services' ? (d.data?.writesTo || []).length > 0 || (d.data?.readsFrom || []).length > 0 : false,
|
|
259
|
+
isDeprecated: d.data?.deprecated === true || (typeof d.data?.deprecated === 'object' && d.data?.deprecated !== null),
|
|
260
|
+
data: {
|
|
261
|
+
id: d.data.id,
|
|
262
|
+
name: d.data.name,
|
|
263
|
+
summary: d.data?.summary,
|
|
264
|
+
version: d.data.version,
|
|
265
|
+
latestVersion: d.data?.latestVersion,
|
|
266
|
+
draft: d.data?.draft,
|
|
267
|
+
badges: d.data?.badges,
|
|
268
|
+
producers: d.data?.producers?.map(mapToItem) ?? [],
|
|
269
|
+
consumers: d.data?.consumers?.map(mapToItem) ?? [],
|
|
270
|
+
receives: d.data?.receives?.map(mapToItem) ?? [],
|
|
271
|
+
sends: d.data?.sends?.map(mapToItem) ?? [],
|
|
272
|
+
services: d.data?.services?.map(mapToItem) ?? [],
|
|
273
|
+
servicesThatWriteToContainer: d.data?.servicesThatWriteToContainer?.map(mapToItem) ?? [],
|
|
274
|
+
servicesThatReadFromContainer: d.data?.servicesThatReadFromContainer?.map(mapToItem) ?? [],
|
|
275
|
+
},
|
|
276
|
+
}));
|
|
277
|
+
|
|
278
|
+
// Get unique owners from all items
|
|
279
|
+
const uniqueOwners = Array.from(new Map(tableData.flatMap((d: any) => d.owners || []).map((o: any) => [o.id, o])).values()).sort(
|
|
280
|
+
(a: any, b: any) => a.name.localeCompare(b.name)
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
// Get unique producers (services) for message types
|
|
284
|
+
// Check for duplicate names and add version if needed
|
|
285
|
+
const servicesByName = new Map<string, typeof services>();
|
|
286
|
+
services.forEach((s) => {
|
|
287
|
+
const name = s.data.name;
|
|
288
|
+
if (!servicesByName.has(name)) servicesByName.set(name, []);
|
|
289
|
+
servicesByName.get(name)!.push(s);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
const uniqueProducers = services
|
|
293
|
+
.map((s) => {
|
|
294
|
+
const hasDuplicateName = (servicesByName.get(s.data.name)?.length ?? 0) > 1;
|
|
295
|
+
const isLatest = s.data.version === s.data.latestVersion;
|
|
296
|
+
const versionLabel = isLatest ? 'latest' : `v${s.data.version}`;
|
|
297
|
+
return {
|
|
298
|
+
id: s.data.id,
|
|
299
|
+
name: hasDuplicateName ? `${s.data.name} (${versionLabel})` : s.data.name,
|
|
300
|
+
};
|
|
301
|
+
})
|
|
302
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
303
|
+
|
|
304
|
+
// Show producers/consumers filter only for events, commands, queries
|
|
305
|
+
const showProducersFilter = ['events', 'commands', 'queries'].includes(type);
|
|
306
|
+
const showConsumersFilter = ['events', 'commands', 'queries'].includes(type);
|
|
307
|
+
|
|
308
|
+
// Consumers are the same services list
|
|
309
|
+
const uniqueConsumers = uniqueProducers;
|
|
310
|
+
|
|
311
|
+
// Get unique domains for the services filter
|
|
312
|
+
const uniqueDomains = domains.map((d) => ({
|
|
313
|
+
id: d.data.id,
|
|
314
|
+
name: d.data.name,
|
|
315
|
+
version: d.data.version,
|
|
316
|
+
}));
|
|
317
|
+
|
|
318
|
+
// Show domains filter only for services
|
|
319
|
+
const showDomainsFilter = type === 'services';
|
|
320
|
+
|
|
321
|
+
const title = `${currentTypeConfig.label} (${data.length})`;
|
|
28
322
|
---
|
|
29
323
|
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
324
|
+
<VerticalSideBarLayout title={`Explore | ${title}`} showNestedSideBar={false}>
|
|
325
|
+
<main class="ml-0 bg-[rgb(var(--ec-page-bg))] min-h-content">
|
|
326
|
+
<div id="discover-collection-tabs">
|
|
327
|
+
<div class="hidden sm:block">
|
|
328
|
+
<div class="border-b border-[rgb(var(--ec-page-border))]">
|
|
329
|
+
<nav class="flex space-x-8 -mb-0.5 pl-6" aria-label="Tabs">
|
|
330
|
+
{
|
|
331
|
+
tabs
|
|
332
|
+
.filter((tab) => tab.visible)
|
|
333
|
+
.map((tab) => (
|
|
334
|
+
<a
|
|
335
|
+
href={tab.href}
|
|
336
|
+
class:list={[
|
|
337
|
+
'group inline-flex items-center py-4 px-1 text-sm font-light text-[rgb(var(--ec-page-text))]',
|
|
338
|
+
tab.isActive && 'border-b-[2px] border-[rgb(var(--ec-accent))] text-[rgb(var(--ec-accent))]',
|
|
339
|
+
!tab.isActive && 'opacity-70 hover:opacity-100',
|
|
340
|
+
!tab.enabled && 'disabled',
|
|
341
|
+
]}
|
|
342
|
+
aria-current="page"
|
|
343
|
+
>
|
|
344
|
+
<tab.icon
|
|
345
|
+
className={`w-6 h-6 -ml-0.5 mr-2 ${tab.isActive ? 'text-[rgb(var(--ec-accent))]' : 'text-[rgb(var(--ec-icon-color))]'}`}
|
|
346
|
+
/>
|
|
347
|
+
<span>{tab.label}</span>
|
|
348
|
+
</a>
|
|
349
|
+
))
|
|
350
|
+
}
|
|
351
|
+
</nav>
|
|
352
|
+
</div>
|
|
353
|
+
</div>
|
|
354
|
+
</div>
|
|
355
|
+
|
|
356
|
+
<div class="py-4 px-6 md:pr-10">
|
|
357
|
+
<DiscoverTable
|
|
358
|
+
data={tableData as DiscoverTableData[]}
|
|
359
|
+
collectionType={type as CollectionType}
|
|
360
|
+
collectionLabel={currentTypeConfig.label}
|
|
361
|
+
domains={uniqueDomains}
|
|
362
|
+
owners={uniqueOwners as Array<{ id: string; name: string; type?: 'user' | 'team' }>}
|
|
363
|
+
producers={uniqueProducers}
|
|
364
|
+
consumers={uniqueConsumers}
|
|
365
|
+
propertyOptions={currentTypeConfig.propertyOptions}
|
|
366
|
+
tableConfiguration={tableConfiguration}
|
|
367
|
+
showDomainsFilter={showDomainsFilter}
|
|
368
|
+
showProducersFilter={showProducersFilter}
|
|
369
|
+
showConsumersFilter={showConsumersFilter}
|
|
370
|
+
client:only="react"
|
|
371
|
+
/>
|
|
372
|
+
</div>
|
|
373
|
+
</main>
|
|
374
|
+
</VerticalSideBarLayout>
|
|
375
|
+
|
|
376
|
+
<style>
|
|
377
|
+
a.disabled {
|
|
378
|
+
pointer-events: none;
|
|
379
|
+
cursor: default;
|
|
380
|
+
opacity: 0.25;
|
|
381
|
+
}
|
|
382
|
+
</style>
|
|
@@ -4,19 +4,21 @@ import { BookOpenIcon, FileText } from 'lucide-react';
|
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
<VerticalSideBarLayout title="Custom Documentation" showNestedSideBar={false}>
|
|
7
|
-
<div class="min-h-[calc(100vh-60px)] bg-
|
|
7
|
+
<div class="min-h-[calc(100vh-60px)] bg-[rgb(var(--ec-page-bg))]">
|
|
8
8
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
|
|
9
9
|
{/* Hero Section */}
|
|
10
10
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center mb-16">
|
|
11
11
|
<div>
|
|
12
|
-
<div
|
|
12
|
+
<div
|
|
13
|
+
class="inline-flex items-center px-4 py-2 rounded-full bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300 font-medium text-sm mb-6"
|
|
14
|
+
>
|
|
13
15
|
<FileText className="w-4 h-4 mr-2" />
|
|
14
16
|
New: Bring your documentation into EventCatalog
|
|
15
17
|
</div>
|
|
16
|
-
<h1 class="text-4xl font-bold text-
|
|
18
|
+
<h1 class="text-4xl font-bold text-[rgb(var(--ec-page-text))] tracking-tight mb-4">
|
|
17
19
|
Document Everything. Share Knowledge. Build Better.
|
|
18
20
|
</h1>
|
|
19
|
-
<p class="text-xl text-
|
|
21
|
+
<p class="text-xl text-[rgb(var(--ec-page-text-muted))] mb-8">
|
|
20
22
|
Add your own documentation to EventCatalog — from ADRs and system guides to runbooks and onboarding material. Connect
|
|
21
23
|
your knowledge to your architecture.
|
|
22
24
|
</p>
|
|
@@ -37,14 +39,14 @@ import { BookOpenIcon, FileText } from 'lucide-react';
|
|
|
37
39
|
<a
|
|
38
40
|
href="https://www.eventcatalog.cloud"
|
|
39
41
|
target="_blank"
|
|
40
|
-
class="inline-flex items-center justify-center px-6 py-3 border border-
|
|
42
|
+
class="inline-flex items-center justify-center px-6 py-3 border border-[rgb(var(--ec-page-border))] text-base font-medium rounded-lg text-[rgb(var(--ec-page-text))] bg-[rgb(var(--ec-card-bg))] hover:bg-[rgb(var(--ec-content-hover))] transition-colors duration-150"
|
|
41
43
|
>
|
|
42
44
|
Try for free
|
|
43
45
|
</a>
|
|
44
46
|
</div>
|
|
45
|
-
<p class="text-sm text-
|
|
47
|
+
<p class="text-sm text-[rgb(var(--ec-page-text-muted))]">
|
|
46
48
|
Available with EventCatalog Starter or Scale plans
|
|
47
|
-
<a href="https://www.eventcatalog.dev/pricing" class="text-blue-600 font-medium block"
|
|
49
|
+
<a href="https://www.eventcatalog.dev/pricing" class="text-blue-600 dark:text-blue-400 font-medium block"
|
|
48
50
|
>Try free for 14 days, no credit card required</a
|
|
49
51
|
>
|
|
50
52
|
</p>
|
|
@@ -62,7 +64,7 @@ import { BookOpenIcon, FileText } from 'lucide-react';
|
|
|
62
64
|
<img
|
|
63
65
|
src="/images/custom-docs-placeholder.png"
|
|
64
66
|
alt="Custom Documentation Preview"
|
|
65
|
-
class="rounded-xl shadow-xl border border-
|
|
67
|
+
class="rounded-xl shadow-xl border border-[rgb(var(--ec-page-border))]"
|
|
66
68
|
/>
|
|
67
69
|
</div>
|
|
68
70
|
</div>
|
|
@@ -70,75 +72,79 @@ import { BookOpenIcon, FileText } from 'lucide-react';
|
|
|
70
72
|
|
|
71
73
|
{/* Features Section */}
|
|
72
74
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
|
|
73
|
-
<div class="bg-
|
|
74
|
-
<div class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center mb-4">
|
|
75
|
-
<svg class="w-6 h-6 text-blue-600" viewBox="0 0 24 24" fill="currentColor">
|
|
75
|
+
<div class="bg-[rgb(var(--ec-card-bg))] rounded-xl p-6 shadow-sm border border-[rgb(var(--ec-page-border))]">
|
|
76
|
+
<div class="w-12 h-12 bg-blue-100 dark:bg-blue-900/30 rounded-lg flex items-center justify-center mb-4">
|
|
77
|
+
<svg class="w-6 h-6 text-blue-600 dark:text-blue-400" viewBox="0 0 24 24" fill="currentColor">
|
|
76
78
|
<path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zM6 20V4h7v5h5v11H6z"></path>
|
|
77
79
|
</svg>
|
|
78
80
|
</div>
|
|
79
|
-
<h3 class="text-lg font-semibold text-
|
|
80
|
-
<p class="text-
|
|
81
|
+
<h3 class="text-lg font-semibold text-[rgb(var(--ec-page-text))] mb-2">Centralized Knowledge</h3>
|
|
82
|
+
<p class="text-[rgb(var(--ec-page-text-muted))]">
|
|
81
83
|
Keep architecture decisions, system guides, and runbooks in one place — easy to access, easy to trust.
|
|
82
84
|
</p>
|
|
83
85
|
</div>
|
|
84
86
|
|
|
85
|
-
<div class="bg-
|
|
86
|
-
<div class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center mb-4">
|
|
87
|
-
<svg class="w-6 h-6 text-blue-600" viewBox="0 0 24 24" fill="currentColor">
|
|
87
|
+
<div class="bg-[rgb(var(--ec-card-bg))] rounded-xl p-6 shadow-sm border border-[rgb(var(--ec-page-border))]">
|
|
88
|
+
<div class="w-12 h-12 bg-blue-100 dark:bg-blue-900/30 rounded-lg flex items-center justify-center mb-4">
|
|
89
|
+
<svg class="w-6 h-6 text-blue-600 dark:text-blue-400" viewBox="0 0 24 24" fill="currentColor">
|
|
88
90
|
<path
|
|
89
91
|
d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-5 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z"
|
|
90
92
|
></path>
|
|
91
93
|
</svg>
|
|
92
94
|
</div>
|
|
93
|
-
<h3 class="text-lg font-semibold text-
|
|
94
|
-
<p class="text-
|
|
95
|
+
<h3 class="text-lg font-semibold text-[rgb(var(--ec-page-text))] mb-2">Rich Formatting</h3>
|
|
96
|
+
<p class="text-[rgb(var(--ec-page-text-muted))]">
|
|
95
97
|
Use Markdown, diagrams, code blocks, and EventCatalog components to create structured, useful documentation.
|
|
96
98
|
</p>
|
|
97
99
|
</div>
|
|
98
100
|
|
|
99
|
-
<div class="bg-
|
|
100
|
-
<div class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center mb-4">
|
|
101
|
-
<svg class="w-6 h-6 text-blue-600" viewBox="0 0 24 24" fill="currentColor">
|
|
101
|
+
<div class="bg-[rgb(var(--ec-card-bg))] rounded-xl p-6 shadow-sm border border-[rgb(var(--ec-page-border))]">
|
|
102
|
+
<div class="w-12 h-12 bg-blue-100 dark:bg-blue-900/30 rounded-lg flex items-center justify-center mb-4">
|
|
103
|
+
<svg class="w-6 h-6 text-blue-600 dark:text-blue-400" viewBox="0 0 24 24" fill="currentColor">
|
|
102
104
|
<path d="M18 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM6 4h5v8l-2.5-1.5L6 12V4z"
|
|
103
105
|
></path>
|
|
104
106
|
</svg>
|
|
105
107
|
</div>
|
|
106
|
-
<h3 class="text-lg font-semibold text-
|
|
107
|
-
<p class="text-
|
|
108
|
+
<h3 class="text-lg font-semibold text-[rgb(var(--ec-page-text))] mb-2">Version Control</h3>
|
|
109
|
+
<p class="text-[rgb(var(--ec-page-text-muted))]">
|
|
110
|
+
Track changes and ensure your documentation grows alongside your system.
|
|
111
|
+
</p>
|
|
108
112
|
</div>
|
|
109
113
|
|
|
110
|
-
<div class="bg-
|
|
111
|
-
<div class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center mb-4">
|
|
112
|
-
<svg class="w-6 h-6 text-blue-600" viewBox="0 0 24 24" fill="currentColor">
|
|
114
|
+
<div class="bg-[rgb(var(--ec-card-bg))] rounded-xl p-6 shadow-sm border border-[rgb(var(--ec-page-border))]">
|
|
115
|
+
<div class="w-12 h-12 bg-blue-100 dark:bg-blue-900/30 rounded-lg flex items-center justify-center mb-4">
|
|
116
|
+
<svg class="w-6 h-6 text-blue-600 dark:text-blue-400" viewBox="0 0 24 24" fill="currentColor">
|
|
113
117
|
<path
|
|
114
118
|
d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"
|
|
115
119
|
></path>
|
|
116
120
|
</svg>
|
|
117
121
|
</div>
|
|
118
|
-
<h3 class="text-lg font-semibold text-
|
|
119
|
-
<p class="text-
|
|
122
|
+
<h3 class="text-lg font-semibold text-[rgb(var(--ec-page-text))] mb-2">Documentation Ownership</h3>
|
|
123
|
+
<p class="text-[rgb(var(--ec-page-text-muted))]">
|
|
124
|
+
Assign and track document owners, making it easy to find the right person in seconds.
|
|
125
|
+
</p>
|
|
120
126
|
</div>
|
|
121
127
|
|
|
122
|
-
<div class="bg-
|
|
123
|
-
<div class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center mb-4">
|
|
124
|
-
<svg class="w-6 h-6 text-blue-600" viewBox="0 0 24 24" fill="currentColor">
|
|
128
|
+
<div class="bg-[rgb(var(--ec-card-bg))] rounded-xl p-6 shadow-sm border border-[rgb(var(--ec-page-border))]">
|
|
129
|
+
<div class="w-12 h-12 bg-blue-100 dark:bg-blue-900/30 rounded-lg flex items-center justify-center mb-4">
|
|
130
|
+
<svg class="w-6 h-6 text-blue-600 dark:text-blue-400" viewBox="0 0 24 24" fill="currentColor">
|
|
125
131
|
<path d="M3 9h14V7H3v2zm0 4h14v-2H3v2zm0 4h14v-2H3v2zm16 0h2v-2h-2v2zm0-10v2h2V7h-2zm0 6h2v-2h-2v2z"></path>
|
|
126
132
|
</svg>
|
|
127
133
|
</div>
|
|
128
|
-
<h3 class="text-lg font-semibold text-
|
|
129
|
-
<p class="text-
|
|
134
|
+
<h3 class="text-lg font-semibold text-[rgb(var(--ec-page-text))] mb-2">Customizable Sidebars</h3>
|
|
135
|
+
<p class="text-[rgb(var(--ec-page-text-muted))]">
|
|
130
136
|
Auto-generated and fully customizable navigation sidebars to organize your documentation perfectly.
|
|
131
137
|
</p>
|
|
132
138
|
</div>
|
|
133
139
|
|
|
134
|
-
<div class="bg-
|
|
135
|
-
<div class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center mb-4">
|
|
136
|
-
<svg class="w-6 h-6 text-blue-600" viewBox="0 0 24 24" fill="currentColor">
|
|
140
|
+
<div class="bg-[rgb(var(--ec-card-bg))] rounded-xl p-6 shadow-sm border border-[rgb(var(--ec-page-border))]">
|
|
141
|
+
<div class="w-12 h-12 bg-blue-100 dark:bg-blue-900/30 rounded-lg flex items-center justify-center mb-4">
|
|
142
|
+
<svg class="w-6 h-6 text-blue-600 dark:text-blue-400" viewBox="0 0 24 24" fill="currentColor">
|
|
137
143
|
<path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"></path>
|
|
138
144
|
</svg>
|
|
139
145
|
</div>
|
|
140
|
-
<h3 class="text-lg font-semibold text-
|
|
141
|
-
<p class="text-
|
|
146
|
+
<h3 class="text-lg font-semibold text-[rgb(var(--ec-page-text))] mb-2">EventCatalog Chat</h3>
|
|
147
|
+
<p class="text-[rgb(var(--ec-page-text-muted))]">
|
|
142
148
|
Interact with your documentation using AI-powered chat to find answers quickly and efficiently.
|
|
143
149
|
</p>
|
|
144
150
|
</div>
|
|
@@ -149,7 +155,7 @@ import { BookOpenIcon, FileText } from 'lucide-react';
|
|
|
149
155
|
<a
|
|
150
156
|
href="https://www.eventcatalog.dev/docs/development/guides/customize-sidebars/application-sidebar"
|
|
151
157
|
target="_blank"
|
|
152
|
-
class="text-sm text-
|
|
158
|
+
class="text-sm text-[rgb(var(--ec-page-text-muted))] hover:text-[rgb(var(--ec-page-text))] transition-colors duration-150"
|
|
153
159
|
>
|
|
154
160
|
Not ready for custom documentation? You can hide this feature in settings
|
|
155
161
|
</a>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { findAndReplace } from 'mdast-util-find-and-replace';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Remark plugin that transforms [[type|Name]] or [[Name]] syntax into ResourceRef MDX components.
|
|
5
|
+
*
|
|
6
|
+
* Supported patterns:
|
|
7
|
+
* - [[entity|Order]] -> <ResourceRef type="entity">Order</ResourceRef>
|
|
8
|
+
* - [[service|OrderService@1.0.0]] -> <ResourceRef type="service" version="1.0.0">OrderService</ResourceRef>
|
|
9
|
+
* - [[diagram|target-architecture]] -> <ResourceRef type="diagram">target-architecture</ResourceRef>
|
|
10
|
+
* - [[Order]] -> <ResourceRef type="entity">Order</ResourceRef> (defaults to entity)
|
|
11
|
+
* - [[Order@0.0.1]] -> <ResourceRef type="entity" version="0.0.1">Order</ResourceRef>
|
|
12
|
+
*/
|
|
13
|
+
export function remarkResourceRef() {
|
|
14
|
+
return function (tree: any) {
|
|
15
|
+
// First pass: match [[type|Name]] or [[type|Name@version]] pattern
|
|
16
|
+
findAndReplace(tree, [
|
|
17
|
+
/\[\[([a-z]+)\|([\w-]+)(?:@([\d.]+))?\]\]/g,
|
|
18
|
+
// @ts-ignore: Types are complex but it works
|
|
19
|
+
function (_match: string, type: string, resourceId: string, version?: string) {
|
|
20
|
+
const attributes: any[] = [{ type: 'mdxJsxAttribute', name: 'type', value: type }];
|
|
21
|
+
if (version) {
|
|
22
|
+
attributes.push({ type: 'mdxJsxAttribute', name: 'version', value: version });
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
type: 'mdxJsxTextElement',
|
|
26
|
+
name: 'ResourceRef',
|
|
27
|
+
attributes,
|
|
28
|
+
children: [{ type: 'text', value: resourceId }],
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
// Second pass: match [[Name]] or [[Name@version]] pattern (defaults to entity)
|
|
34
|
+
findAndReplace(tree, [
|
|
35
|
+
/\[\[([\w-]+)(?:@([\d.]+))?\]\]/g,
|
|
36
|
+
// @ts-ignore: Types are complex but it works
|
|
37
|
+
function (_match: string, resourceId: string, version?: string) {
|
|
38
|
+
const attributes: any[] = [{ type: 'mdxJsxAttribute', name: 'type', value: 'entity' }];
|
|
39
|
+
if (version) {
|
|
40
|
+
attributes.push({ type: 'mdxJsxAttribute', name: 'version', value: version });
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
type: 'mdxJsxTextElement',
|
|
44
|
+
name: 'ResourceRef',
|
|
45
|
+
attributes,
|
|
46
|
+
children: [{ type: 'text', value: resourceId }],
|
|
47
|
+
};
|
|
48
|
+
},
|
|
49
|
+
]);
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -31,7 +31,7 @@ export type NavNode = {
|
|
|
31
31
|
*/
|
|
32
32
|
export type NavigationData = {
|
|
33
33
|
roots: ChildRef[]; // What to show at top level
|
|
34
|
-
nodes: Record<string, NavNode>; // Flat map of
|
|
34
|
+
nodes: Record<string, NavNode | string>; // Flat map of nodes by key, strings are references to other keys (e.g., unversioned aliases)
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
export const uniqueBy = <T>(array: T[], key: keyof T): T[] => {
|