@eventcatalog/core 2.33.12 → 2.34.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/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-5THCKFYU.js → chunk-GUCQ43OT.js} +1 -1
- package/dist/{chunk-GXZYW6VG.js → chunk-VU6QMU5H.js} +1 -1
- package/dist/{chunk-K3CDZR6K.js → chunk-XOPHTY4E.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 +3 -3
- package/eventcatalog/src/components/Grids/DomainGrid.tsx +52 -0
- package/eventcatalog/src/components/Grids/ServiceGrid.tsx +198 -168
- package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.tsx +40 -16
- package/eventcatalog/src/components/MDX/NodeGraph/Nodes/Channel.tsx +3 -1
- package/eventcatalog/src/components/MDX/NodeGraph/Nodes/Command.tsx +3 -1
- package/eventcatalog/src/components/MDX/NodeGraph/Nodes/Custom.tsx +1 -1
- package/eventcatalog/src/components/MDX/NodeGraph/Nodes/Event.tsx +13 -2
- package/eventcatalog/src/components/MDX/NodeGraph/Nodes/Query.tsx +3 -1
- package/eventcatalog/src/components/MDX/NodeGraph/Nodes/Service.tsx +3 -1
- package/eventcatalog/src/components/MDX/NodeGraph/Nodes/User.tsx +1 -1
- package/eventcatalog/src/components/MDX/SchemaViewer/SchemaViewerRoot.astro +0 -3
- package/eventcatalog/src/components/SideBars/DomainSideBar.astro +47 -2
- package/eventcatalog/src/components/SideBars/ServiceSideBar.astro +22 -0
- package/eventcatalog/src/components/SideNav/ListViewSideBar/index.tsx +11 -1
- package/eventcatalog/src/components/SideNav/ListViewSideBar/types.ts +1 -0
- package/eventcatalog/src/components/SideNav/ListViewSideBar/utils.ts +1 -0
- package/eventcatalog/src/content.config.ts +1 -0
- package/eventcatalog/src/enterprise/custom-documentation/components/CustomDocsNav/index.tsx +0 -3
- package/eventcatalog/src/pages/architecture/architecture.astro +17 -6
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/index.astro +1 -5
- package/eventcatalog/src/pages/docs/teams/[id]/index.astro +100 -102
- package/eventcatalog/src/pages/docs/users/[id]/index.astro +4 -12
- package/eventcatalog/src/utils/collections/domains.ts +32 -2
- package/eventcatalog/src/utils/node-graphs/domains-node-graph.ts +31 -2
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
log_build_default
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
5
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-VU6QMU5H.js";
|
|
4
|
+
import "../chunk-GUCQ43OT.js";
|
|
5
|
+
import "../chunk-XOPHTY4E.js";
|
|
6
6
|
import "../chunk-E7TXTI7G.js";
|
|
7
7
|
export {
|
|
8
8
|
log_build_default as default
|
package/dist/constants.cjs
CHANGED
package/dist/constants.js
CHANGED
package/dist/eventcatalog.cjs
CHANGED
package/dist/eventcatalog.js
CHANGED
|
@@ -6,15 +6,15 @@ import {
|
|
|
6
6
|
} from "./chunk-UKJ7F5WR.js";
|
|
7
7
|
import {
|
|
8
8
|
log_build_default
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-VU6QMU5H.js";
|
|
10
|
+
import "./chunk-GUCQ43OT.js";
|
|
11
11
|
import {
|
|
12
12
|
catalogToAstro,
|
|
13
13
|
checkAndConvertMdToMdx
|
|
14
14
|
} from "./chunk-7SI5EVOX.js";
|
|
15
15
|
import {
|
|
16
16
|
VERSION
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-XOPHTY4E.js";
|
|
18
18
|
import {
|
|
19
19
|
isBackstagePluginEnabled,
|
|
20
20
|
isEventCatalogScaleEnabled,
|
|
@@ -10,6 +10,7 @@ export interface ExtendedDomain extends CollectionEntry<'domains'> {
|
|
|
10
10
|
sends: CollectionEntry<CollectionMessageTypes>[];
|
|
11
11
|
receives: CollectionEntry<CollectionMessageTypes>[];
|
|
12
12
|
services: CollectionEntry<'services'>[];
|
|
13
|
+
domains: CollectionEntry<'domains'>[];
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
interface DomainGridProps {
|
|
@@ -102,6 +103,12 @@ export default function DomainGrid({ domains, embeded }: DomainGridProps) {
|
|
|
102
103
|
</p>
|
|
103
104
|
|
|
104
105
|
<div className="flex gap-4 mb-4">
|
|
106
|
+
<div className="flex items-center gap-2 bg-white rounded-lg px-3 py-2 border border-orange-200">
|
|
107
|
+
<RectangleGroupIcon className="h-4 w-4 text-yellow-500" />
|
|
108
|
+
<div className="flex">
|
|
109
|
+
<p className="text-sm font-medium text-gray-900">{domain.data.domains?.length || 0} Subdomains</p>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
105
112
|
<div className="flex items-center gap-2 bg-white rounded-lg px-3 py-2 border border-pink-200">
|
|
106
113
|
<ServerIcon className="h-4 w-4 text-pink-500" />
|
|
107
114
|
<div className="flex">
|
|
@@ -119,6 +126,41 @@ export default function DomainGrid({ domains, embeded }: DomainGridProps) {
|
|
|
119
126
|
</div>
|
|
120
127
|
|
|
121
128
|
<div className="space-y-6">
|
|
129
|
+
{/* Subdomains and there services */}
|
|
130
|
+
{domain.data.domains?.slice(0, 2).map((subdomain: any) => (
|
|
131
|
+
<div
|
|
132
|
+
key={subdomain.data.id}
|
|
133
|
+
className="block space-y-2 bg-white border-2 border-dashed border-orange-400 p-4 rounded-lg transition-colors duration-200"
|
|
134
|
+
>
|
|
135
|
+
<div className="flex items-center justify-between">
|
|
136
|
+
<div className="flex items-center gap-2">
|
|
137
|
+
<RectangleGroupIcon className="h-4 w-4 text-orange-500" />
|
|
138
|
+
<h4 className="text-sm font-medium text-gray-900">
|
|
139
|
+
{subdomain.data.name || subdomain.data.id} (Subdomain)
|
|
140
|
+
</h4>
|
|
141
|
+
</div>
|
|
142
|
+
<span className="text-xs text-gray-500">v{subdomain.data.version}</span>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<div className="flex gap-4">
|
|
146
|
+
<div className="flex items-center gap-2 bg-white rounded-lg px-3 py-2 border border-pink-200">
|
|
147
|
+
<ServerIcon className="h-4 w-4 text-pink-500" />
|
|
148
|
+
<div className="flex">
|
|
149
|
+
<p className="text-sm font-medium text-gray-900">{subdomain.data.services?.length || 0} Services</p>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
<div className="flex items-center gap-2 bg-white rounded-lg px-3 py-2 border border-gray-200">
|
|
153
|
+
<EnvelopeIcon className="h-4 w-4 text-blue-500" />
|
|
154
|
+
<div>
|
|
155
|
+
<p className="text-sm font-medium text-gray-900">
|
|
156
|
+
{(subdomain.sends?.length || 0) + (subdomain.receives?.length || 0)} Messages
|
|
157
|
+
</p>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
))}
|
|
163
|
+
|
|
122
164
|
{/* Services and their messages */}
|
|
123
165
|
{domain.data.services?.slice(0, 2).map((service: any) => (
|
|
124
166
|
<div
|
|
@@ -211,6 +253,16 @@ export default function DomainGrid({ domains, embeded }: DomainGridProps) {
|
|
|
211
253
|
</div>
|
|
212
254
|
</div>
|
|
213
255
|
))}
|
|
256
|
+
{domain.data.domains && domain.data.domains.length > 2 && (
|
|
257
|
+
<div className="block space-y-2 bg-white border-2 border-dashed border-orange-400 p-4 rounded-lg transition-colors duration-200">
|
|
258
|
+
<div className="flex items-center justify-between">
|
|
259
|
+
<div className="flex items-center gap-2">
|
|
260
|
+
<RectangleGroupIcon className="h-4 w-4 text-orange-500/70" />
|
|
261
|
+
<h4 className="text-sm font-medium text-gray-600">+{domain.data.domains.length - 2} more subdomains</h4>
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
</div>
|
|
265
|
+
)}
|
|
214
266
|
{domain.data.services && domain.data.services.length > 2 && (
|
|
215
267
|
<div className="block space-y-2 bg-white border-2 border-dashed border-pink-400 p-4 rounded-lg transition-colors duration-200">
|
|
216
268
|
<div className="flex items-center justify-between">
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useMemo, useEffect } from 'react';
|
|
1
|
+
import { useState, useMemo, useEffect, memo } from 'react';
|
|
2
2
|
import { ServerIcon, ChevronRightIcon } from '@heroicons/react/24/outline';
|
|
3
3
|
import { RectangleGroupIcon } from '@heroicons/react/24/outline';
|
|
4
4
|
import { buildUrl, buildUrlWithParams } from '@utils/url-builder';
|
|
@@ -6,13 +6,187 @@ import type { CollectionEntry } from 'astro:content';
|
|
|
6
6
|
import type { CollectionMessageTypes } from '@types';
|
|
7
7
|
import { getCollectionStyles } from './utils';
|
|
8
8
|
import { SearchBar, TypeFilters, Pagination } from './components';
|
|
9
|
+
import type { ExtendedDomain } from './DomainGrid';
|
|
10
|
+
|
|
11
|
+
// Message component for reuse
|
|
12
|
+
const Message = memo(({ message, collection }: { message: any; collection: string }) => {
|
|
13
|
+
const { Icon, color } = getCollectionStyles(message.collection);
|
|
14
|
+
return (
|
|
15
|
+
<a
|
|
16
|
+
href={buildUrl(`/docs/${message.collection}/${message.data.id}/${message.data.version}`)}
|
|
17
|
+
className="group flex border border-gray-200 items-center gap-1 rounded-md text-[11px] font-medium hover:bg-gray-50 transition-colors duration-200 bg-white"
|
|
18
|
+
>
|
|
19
|
+
<div className="bg-white border-r border-gray-200 px-2 py-1.5 rounded-l-md">
|
|
20
|
+
<Icon className={`h-3 w-3 text-${color}-500`} />
|
|
21
|
+
</div>
|
|
22
|
+
<span className="px-1 py-1 truncate max-w-[140px]">{message.data.name}</span>
|
|
23
|
+
</a>
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Messages Container component
|
|
28
|
+
const MessagesContainer = memo(
|
|
29
|
+
({ messages, type, selectedTypes }: { messages: any[]; type: 'receives' | 'sends'; selectedTypes: string[] }) => {
|
|
30
|
+
const bgColor = type === 'receives' ? 'blue' : 'green';
|
|
31
|
+
const filteredMessages = messages?.filter(
|
|
32
|
+
(message: any) => selectedTypes.length === 0 || selectedTypes.includes(message.collection)
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div className={`flex-1 h-full flex flex-col bg-${bgColor}-100 border border-${bgColor}-300 rounded-lg p-4`}>
|
|
37
|
+
<div className="space-y-2 flex-1">
|
|
38
|
+
{filteredMessages?.map((message: any) => (
|
|
39
|
+
<Message key={message.data.name} message={message} collection={message.collection} />
|
|
40
|
+
))}
|
|
41
|
+
{(!messages?.length ||
|
|
42
|
+
(selectedTypes.length > 0 && !messages?.some((message: any) => selectedTypes.includes(message.collection)))) && (
|
|
43
|
+
<div className="text-center py-4">
|
|
44
|
+
<p className="text-gray-500 text-[10px]">
|
|
45
|
+
{selectedTypes.length > 0
|
|
46
|
+
? `Service does not ${type} ${selectedTypes.join(' or ')}`
|
|
47
|
+
: `Service does not ${type} any messages`}
|
|
48
|
+
</p>
|
|
49
|
+
</div>
|
|
50
|
+
)}
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
// Service Card component
|
|
58
|
+
const ServiceCard = memo(({ service, urlParams, selectedTypes }: { service: any; urlParams: any; selectedTypes: string[] }) => {
|
|
59
|
+
return (
|
|
60
|
+
<a
|
|
61
|
+
href={buildUrlWithParams('/architecture/messages', {
|
|
62
|
+
serviceName: service.data.name,
|
|
63
|
+
serviceId: service.data.id,
|
|
64
|
+
domainId: urlParams?.domainId,
|
|
65
|
+
domainName: urlParams?.domainName,
|
|
66
|
+
})}
|
|
67
|
+
className="group hover:bg-pink-50 bg-white border-2 border-dashed border-pink-400 rounded-lg shadow-sm hover:shadow-lg transition-all duration-200 overflow-hidden "
|
|
68
|
+
>
|
|
69
|
+
<div className="p-6">
|
|
70
|
+
<div className="flex items-center justify-between mb-3">
|
|
71
|
+
<div className="flex items-center gap-2 w-full">
|
|
72
|
+
<ServerIcon className="h-5 w-5 text-pink-500" />
|
|
73
|
+
<h3 className="text-lg font-semibold text-gray-900 truncate group-hover:underline transition-colors duration-200 w-full max-w-[90%]">
|
|
74
|
+
{service.data.name || service.data.id} (v{service.data.version})
|
|
75
|
+
</h3>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
{service.data.summary && <p className="text-gray-600 text-sm line-clamp-2 min-h-[2.5rem]">{service.data.summary}</p>}
|
|
80
|
+
|
|
81
|
+
{!urlParams?.serviceName && (
|
|
82
|
+
<div className="flex items-center gap-4 mt-4">
|
|
83
|
+
<MessagesContainer messages={service.data.receives} type="receives" selectedTypes={selectedTypes} />
|
|
84
|
+
|
|
85
|
+
<div className="flex items-center gap-2 max-w-[200px]">
|
|
86
|
+
<div className="w-4 h-[2px] bg-blue-200"></div>
|
|
87
|
+
<div className="bg-white border-2 border-pink-100 rounded-lg p-4 shadow-sm">
|
|
88
|
+
<div className="flex flex-col items-center gap-3">
|
|
89
|
+
<ServerIcon className="h-8 w-8 text-pink-500" />
|
|
90
|
+
<div className="text-center">
|
|
91
|
+
<p className="text-sm font-medium text-gray-900">{service.data.name || service.data.id}</p>
|
|
92
|
+
<p className="text-xs text-gray-500">v{service.data.version}</p>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
<div className="w-4 h-[2px] bg-emerald-200"></div>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<MessagesContainer messages={service.data.sends} type="sends" selectedTypes={selectedTypes} />
|
|
100
|
+
</div>
|
|
101
|
+
)}
|
|
102
|
+
</div>
|
|
103
|
+
</a>
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Domain Section component
|
|
108
|
+
const DomainSection = memo(
|
|
109
|
+
({ domain, services, urlParams, selectedTypes }: { domain: any; services: any[]; urlParams: any; selectedTypes: string[] }) => {
|
|
110
|
+
const subdomains = domain.data.domains || [];
|
|
111
|
+
const allSubDomainServices = subdomains.map((subdomain: any) => subdomain.data.services || []).flat();
|
|
112
|
+
|
|
113
|
+
const servicesWithoutSubdomains = services.filter((service) => {
|
|
114
|
+
return !allSubDomainServices.some((s: any) => s.id === service.data.id);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<div className="space-y-6">
|
|
119
|
+
{servicesWithoutSubdomains.length > 0 && (
|
|
120
|
+
<div className="grid grid-cols-1 sm:grid-cols-1 lg:grid-cols-1 xl:grid-cols-2 gap-6">
|
|
121
|
+
{servicesWithoutSubdomains.map((service) => (
|
|
122
|
+
<ServiceCard key={service.data.id} service={service} urlParams={urlParams} selectedTypes={selectedTypes} />
|
|
123
|
+
))}
|
|
124
|
+
</div>
|
|
125
|
+
)}
|
|
126
|
+
|
|
127
|
+
{subdomains.map((subdomainRef: any) => {
|
|
128
|
+
const subdomain = domain.data.domains?.find((d: any) => d.data.id === subdomainRef.data.id);
|
|
129
|
+
if (!subdomain) return null;
|
|
130
|
+
|
|
131
|
+
const subdomainServices = services.filter((service) =>
|
|
132
|
+
subdomain.data.services?.some((s: any) => s.id === service.data.id)
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
if (subdomainServices.length === 0) return null;
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<div key={subdomain.data.id} className="bg-orange-50 border-2 border-orange-400 rounded-lg p-6 space-y-4">
|
|
139
|
+
<div className="flex items-center justify-between">
|
|
140
|
+
<div className="flex items-center gap-2">
|
|
141
|
+
<RectangleGroupIcon className="h-5 w-5 text-orange-500" />
|
|
142
|
+
<h3 className="text-xl font-semibold text-gray-900">{subdomain.data.name} (Subdomain)</h3>
|
|
143
|
+
</div>
|
|
144
|
+
<div className="flex gap-2">
|
|
145
|
+
<a
|
|
146
|
+
href={buildUrl(`/visualiser/domains/${subdomain.data.id}`)}
|
|
147
|
+
className="inline-flex items-center px-3 py-2 text-sm font-medium bg-white border border-gray-300 rounded-md transition-colors duration-200"
|
|
148
|
+
>
|
|
149
|
+
View in visualizer
|
|
150
|
+
</a>
|
|
151
|
+
<a
|
|
152
|
+
href={buildUrl(`/docs/domains/${subdomain.data.id}`)}
|
|
153
|
+
className="inline-flex items-center px-3 py-2 text-sm font-medium text-black border border-gray-300 bg-white rounded-md transition-colors duration-200"
|
|
154
|
+
>
|
|
155
|
+
Read documentation
|
|
156
|
+
</a>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
<div className="grid grid-cols-1 sm:grid-cols-1 lg:grid-cols-1 xl:grid-cols-2 gap-6">
|
|
161
|
+
{subdomainServices.map((service) => (
|
|
162
|
+
<ServiceCard
|
|
163
|
+
key={service.data.id}
|
|
164
|
+
service={service}
|
|
165
|
+
urlParams={{
|
|
166
|
+
...urlParams,
|
|
167
|
+
domainId: subdomain.data.id,
|
|
168
|
+
domainName: `${subdomain.data.name} (Subdomain)`,
|
|
169
|
+
}}
|
|
170
|
+
selectedTypes={selectedTypes}
|
|
171
|
+
/>
|
|
172
|
+
))}
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
176
|
+
})}
|
|
177
|
+
</div>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
);
|
|
9
181
|
|
|
10
182
|
interface ServiceGridProps {
|
|
11
183
|
services: CollectionEntry<'services'>[];
|
|
184
|
+
domains: ExtendedDomain[];
|
|
12
185
|
embeded: boolean;
|
|
13
186
|
}
|
|
14
187
|
|
|
15
|
-
|
|
188
|
+
// Main ServiceGrid component
|
|
189
|
+
export default function ServiceGrid({ services, domains, embeded }: ServiceGridProps) {
|
|
16
190
|
const [searchQuery, setSearchQuery] = useState('');
|
|
17
191
|
const [currentPage, setCurrentPage] = useState(1);
|
|
18
192
|
const [selectedTypes, setSelectedTypes] = useState<CollectionMessageTypes[]>([]);
|
|
@@ -24,35 +198,27 @@ export default function ServiceGrid({ services, embeded }: ServiceGridProps) {
|
|
|
24
198
|
serviceName?: string;
|
|
25
199
|
} | null>(null);
|
|
26
200
|
|
|
27
|
-
// Effect to sync URL params with state
|
|
28
201
|
useEffect(() => {
|
|
29
202
|
const params = new URLSearchParams(window.location.search);
|
|
30
|
-
const serviceIds = params.get('serviceIds')?.split(',').filter(Boolean);
|
|
31
|
-
const domainId = params.get('domainId') || undefined;
|
|
32
|
-
const domainName = params.get('domainName') || undefined;
|
|
33
|
-
const serviceName = params.get('serviceName') || undefined;
|
|
34
203
|
setUrlParams({
|
|
35
|
-
serviceIds,
|
|
36
|
-
domainId,
|
|
37
|
-
domainName,
|
|
38
|
-
serviceName,
|
|
204
|
+
serviceIds: params.get('serviceIds')?.split(',').filter(Boolean),
|
|
205
|
+
domainId: params.get('domainId') || undefined,
|
|
206
|
+
domainName: params.get('domainName') || undefined,
|
|
207
|
+
serviceName: params.get('serviceName') || undefined,
|
|
39
208
|
});
|
|
40
209
|
}, []);
|
|
41
210
|
|
|
42
211
|
const filteredAndSortedServices = useMemo(() => {
|
|
43
|
-
// Don't filter until we have URL params
|
|
44
212
|
if (urlParams === null) return [];
|
|
45
213
|
|
|
46
214
|
let result = [...services];
|
|
47
215
|
|
|
48
|
-
// Filter by service IDs if present
|
|
49
216
|
if (urlParams.serviceIds?.length) {
|
|
50
217
|
result = result.filter(
|
|
51
218
|
(service) => urlParams.serviceIds?.includes(service.data.id) && !service.data.id.includes('/versioned/')
|
|
52
219
|
);
|
|
53
220
|
}
|
|
54
221
|
|
|
55
|
-
// Filter by search query
|
|
56
222
|
if (searchQuery) {
|
|
57
223
|
const query = searchQuery.toLowerCase();
|
|
58
224
|
result = result.filter(
|
|
@@ -64,7 +230,6 @@ export default function ServiceGrid({ services, embeded }: ServiceGridProps) {
|
|
|
64
230
|
);
|
|
65
231
|
}
|
|
66
232
|
|
|
67
|
-
// Filter by selected message types
|
|
68
233
|
if (selectedTypes.length > 0) {
|
|
69
234
|
result = result.filter((service) => {
|
|
70
235
|
const hasMatchingSends = service.data.sends?.some((message: any) => selectedTypes.includes(message.collection));
|
|
@@ -73,18 +238,14 @@ export default function ServiceGrid({ services, embeded }: ServiceGridProps) {
|
|
|
73
238
|
});
|
|
74
239
|
}
|
|
75
240
|
|
|
76
|
-
// Sort by name by default
|
|
77
241
|
result.sort((a, b) => (a.data.name || a.data.id).localeCompare(b.data.name || b.data.id));
|
|
78
|
-
|
|
79
242
|
return result;
|
|
80
243
|
}, [services, searchQuery, urlParams, selectedTypes]);
|
|
81
244
|
|
|
82
|
-
// Add pagination calculation
|
|
83
245
|
const paginatedServices = useMemo(() => {
|
|
84
246
|
if (urlParams?.domainId || urlParams?.serviceIds?.length) {
|
|
85
247
|
return filteredAndSortedServices;
|
|
86
248
|
}
|
|
87
|
-
|
|
88
249
|
const startIndex = (currentPage - 1) * ITEMS_PER_PAGE;
|
|
89
250
|
return filteredAndSortedServices.slice(startIndex, startIndex + ITEMS_PER_PAGE);
|
|
90
251
|
}, [filteredAndSortedServices, currentPage, urlParams]);
|
|
@@ -94,7 +255,6 @@ export default function ServiceGrid({ services, embeded }: ServiceGridProps) {
|
|
|
94
255
|
return Math.ceil(filteredAndSortedServices.length / ITEMS_PER_PAGE);
|
|
95
256
|
}, [filteredAndSortedServices.length, urlParams]);
|
|
96
257
|
|
|
97
|
-
// Reset pagination when search query or filters change
|
|
98
258
|
useEffect(() => {
|
|
99
259
|
setCurrentPage(1);
|
|
100
260
|
}, [searchQuery, selectedTypes]);
|
|
@@ -184,155 +344,25 @@ export default function ServiceGrid({ services, embeded }: ServiceGridProps) {
|
|
|
184
344
|
|
|
185
345
|
{filteredAndSortedServices.length > 0 && (
|
|
186
346
|
<div className={`rounded-xl overflow-hidden ${urlParams?.domainId ? 'bg-yellow-50 p-8 border-2 border-yellow-400' : ''}`}>
|
|
187
|
-
{urlParams?.domainName
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
Read documentation
|
|
206
|
-
</a>
|
|
207
|
-
</div>
|
|
208
|
-
</div>
|
|
209
|
-
</>
|
|
347
|
+
{urlParams?.domainName ? (
|
|
348
|
+
domains
|
|
349
|
+
.filter((domain: ExtendedDomain) => domain.data.id === urlParams.domainId)
|
|
350
|
+
.map((domain: ExtendedDomain) => (
|
|
351
|
+
<DomainSection
|
|
352
|
+
key={domain.data.id}
|
|
353
|
+
domain={domain}
|
|
354
|
+
services={paginatedServices}
|
|
355
|
+
urlParams={urlParams}
|
|
356
|
+
selectedTypes={selectedTypes}
|
|
357
|
+
/>
|
|
358
|
+
))
|
|
359
|
+
) : (
|
|
360
|
+
<div className={`grid grid-cols-1 sm:grid-cols-1 lg:grid-cols-1 xl:grid-cols-${embeded ? 1 : 2} gap-6`}>
|
|
361
|
+
{paginatedServices.map((service) => (
|
|
362
|
+
<ServiceCard key={service.data.id} service={service} urlParams={urlParams} selectedTypes={selectedTypes} />
|
|
363
|
+
))}
|
|
364
|
+
</div>
|
|
210
365
|
)}
|
|
211
|
-
|
|
212
|
-
<div className={`grid grid-cols-1 sm:grid-cols-1 lg:grid-cols-1 xl:grid-cols-${embeded ? 1 : 2} gap-6`}>
|
|
213
|
-
{paginatedServices.map((service) => {
|
|
214
|
-
return (
|
|
215
|
-
<a
|
|
216
|
-
key={service.data.id}
|
|
217
|
-
href={buildUrlWithParams('/architecture/messages', {
|
|
218
|
-
serviceName: service.data.name,
|
|
219
|
-
serviceId: service.data.id,
|
|
220
|
-
domainId: urlParams?.domainId,
|
|
221
|
-
domainName: urlParams?.domainName,
|
|
222
|
-
})}
|
|
223
|
-
className="group hover:bg-pink-50 bg-white border-2 border-dashed border-pink-400 rounded-lg shadow-sm hover:shadow-lg transition-all duration-200 overflow-hidden"
|
|
224
|
-
>
|
|
225
|
-
<div className="p-6">
|
|
226
|
-
<div className="flex items-center justify-between mb-3">
|
|
227
|
-
<div className="flex items-center gap-2 w-full">
|
|
228
|
-
<ServerIcon className="h-5 w-5 text-pink-500" />
|
|
229
|
-
<h3 className="text-lg font-semibold text-gray-900 truncate group-hover:underline transition-colors duration-200 w-full max-w-[90%]">
|
|
230
|
-
{service.data.name || service.data.id} (v{service.data.version})
|
|
231
|
-
</h3>
|
|
232
|
-
</div>
|
|
233
|
-
</div>
|
|
234
|
-
|
|
235
|
-
{service.data.summary && (
|
|
236
|
-
<p className="text-gray-600 text-sm line-clamp-2 min-h-[2.5rem]">{service.data.summary}</p>
|
|
237
|
-
)}
|
|
238
|
-
|
|
239
|
-
<div className="space-y-4">
|
|
240
|
-
{/* Messages Section */}
|
|
241
|
-
{!urlParams?.serviceName && (
|
|
242
|
-
<div className="flex items-center gap-4">
|
|
243
|
-
<div className="flex-1 h-full flex flex-col bg-blue-100 border border-blue-300 rounded-lg p-4">
|
|
244
|
-
<div className="space-y-2 flex-1">
|
|
245
|
-
{service.data.receives
|
|
246
|
-
?.filter(
|
|
247
|
-
(message: any) => selectedTypes.length === 0 || selectedTypes.includes(message.collection)
|
|
248
|
-
)
|
|
249
|
-
?.map((message: any) => {
|
|
250
|
-
const { Icon, color } = getCollectionStyles(message.collection);
|
|
251
|
-
return (
|
|
252
|
-
<a
|
|
253
|
-
key={message.data.name}
|
|
254
|
-
href={buildUrl(`/docs/${message.collection}/${message.data.id}/${message.data.version}`)}
|
|
255
|
-
className="group flex border border-gray-200 items-center gap-1 rounded-md text-[11px] font-medium hover:bg-gray-50 transition-colors duration-200 bg-white"
|
|
256
|
-
>
|
|
257
|
-
<div className="bg-white border-r border-gray-200 px-2 py-1.5 rounded-l-md">
|
|
258
|
-
<Icon className={`h-3 w-3 text-${color}-500`} />
|
|
259
|
-
</div>
|
|
260
|
-
<span className="px-1 py-1 truncate max-w-[140px]">{message.data.name}</span>
|
|
261
|
-
</a>
|
|
262
|
-
);
|
|
263
|
-
})}
|
|
264
|
-
{(!service.data.receives?.length ||
|
|
265
|
-
(selectedTypes.length > 0 &&
|
|
266
|
-
!service.data.receives?.some((message: any) =>
|
|
267
|
-
selectedTypes.includes(message.collection)
|
|
268
|
-
))) && (
|
|
269
|
-
<div className="text-center py-4">
|
|
270
|
-
<p className="text-gray-500 text-[10px]">
|
|
271
|
-
{selectedTypes.length > 0
|
|
272
|
-
? `Service does not receive ${selectedTypes.join(' or ')}`
|
|
273
|
-
: 'Service does not receive any messages'}
|
|
274
|
-
</p>
|
|
275
|
-
</div>
|
|
276
|
-
)}
|
|
277
|
-
</div>
|
|
278
|
-
</div>
|
|
279
|
-
|
|
280
|
-
<div className="flex items-center gap-2 max-w-[200px]">
|
|
281
|
-
<div className="w-4 h-[2px] bg-blue-200"></div>
|
|
282
|
-
<div className="bg-white border-2 border-pink-100 rounded-lg p-4 shadow-sm">
|
|
283
|
-
<div className="flex flex-col items-center gap-3">
|
|
284
|
-
<ServerIcon className="h-8 w-8 text-pink-500" />
|
|
285
|
-
<div className="text-center">
|
|
286
|
-
<p className="text-sm font-medium text-gray-900">{service.data.name || service.data.id}</p>
|
|
287
|
-
<p className="text-xs text-gray-500">v{service.data.version}</p>
|
|
288
|
-
</div>
|
|
289
|
-
</div>
|
|
290
|
-
</div>
|
|
291
|
-
<div className="w-4 h-[2px] bg-emerald-200"></div>
|
|
292
|
-
</div>
|
|
293
|
-
|
|
294
|
-
<div className="flex-1 h-full flex flex-col bg-green-100 border border-green-300 rounded-lg p-4">
|
|
295
|
-
<div className="space-y-2 flex-1">
|
|
296
|
-
{service.data.sends
|
|
297
|
-
?.filter(
|
|
298
|
-
(message: any) => selectedTypes.length === 0 || selectedTypes.includes(message.collection)
|
|
299
|
-
)
|
|
300
|
-
?.map((message: any) => {
|
|
301
|
-
const { Icon, color } = getCollectionStyles(message.collection);
|
|
302
|
-
return (
|
|
303
|
-
<a
|
|
304
|
-
key={message.data.name}
|
|
305
|
-
href={buildUrl(`/docs/${message.collection}/${message.data.id}/${message.data.version}`)}
|
|
306
|
-
className="group flex border border-gray-200 items-center gap-1 rounded-md text-[11px] font-medium hover:bg-gray-50 transition-colors duration-200 bg-white"
|
|
307
|
-
>
|
|
308
|
-
<div className="bg-white border-r border-gray-200 px-2 py-1.5 rounded-l-md">
|
|
309
|
-
<Icon className={`h-3 w-3 text-${color}-500`} />
|
|
310
|
-
</div>
|
|
311
|
-
<span className="px-1 py-1 truncate max-w-[140px]">{message.data.name}</span>
|
|
312
|
-
</a>
|
|
313
|
-
);
|
|
314
|
-
})}
|
|
315
|
-
{(!service.data.sends?.length ||
|
|
316
|
-
(selectedTypes.length > 0 &&
|
|
317
|
-
!service.data.sends?.some((message: any) => selectedTypes.includes(message.collection)))) && (
|
|
318
|
-
<div className="text-center py-4 ">
|
|
319
|
-
<p className="text-gray-500 text-[10px]">
|
|
320
|
-
{selectedTypes.length > 0
|
|
321
|
-
? `Service does not send ${selectedTypes.join(' or ')}`
|
|
322
|
-
: 'Service does not send any messages'}
|
|
323
|
-
</p>
|
|
324
|
-
</div>
|
|
325
|
-
)}
|
|
326
|
-
</div>
|
|
327
|
-
</div>
|
|
328
|
-
</div>
|
|
329
|
-
)}
|
|
330
|
-
</div>
|
|
331
|
-
</div>
|
|
332
|
-
</a>
|
|
333
|
-
);
|
|
334
|
-
})}
|
|
335
|
-
</div>
|
|
336
366
|
</div>
|
|
337
367
|
)}
|
|
338
368
|
|