@eventcatalog/core 2.65.0 → 3.0.0-beta.0
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 -26
- 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-NK6OYMRD.js → chunk-JB4YT5JY.js} +1 -1
- package/dist/{chunk-BMDTX5IN.js → chunk-TQ4HZREX.js} +1 -1
- package/dist/{chunk-IJRFYF4B.js → chunk-X4W4YC3U.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +1 -21
- package/dist/eventcatalog.config.d.cts +10 -0
- package/dist/eventcatalog.config.d.ts +10 -0
- package/dist/eventcatalog.js +3 -20
- package/eventcatalog/src/components/CopyAsMarkdown.tsx +19 -1
- package/eventcatalog/src/components/FavoriteButton.tsx +54 -0
- package/eventcatalog/src/components/Grids/DomainGrid.tsx +386 -362
- package/eventcatalog/src/components/Grids/MessageGrid.tsx +166 -518
- package/eventcatalog/src/components/Header.astro +48 -23
- package/eventcatalog/src/components/Lists/VersionList.astro +2 -2
- package/eventcatalog/src/components/MDX/Design/Design.astro +4 -1
- package/eventcatalog/src/components/MDX/Flow/Flow.astro +2 -1
- package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +3 -3
- package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +8 -2
- package/eventcatalog/src/components/SchemaExplorer/SchemaPageViewer.tsx +37 -0
- package/eventcatalog/src/components/Search/Search.astro +48 -28
- package/eventcatalog/src/components/Search/SearchModal.tsx +393 -702
- package/eventcatalog/src/components/SideNav/NestedSideBar/SearchBar.tsx +298 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/builders/container.ts +66 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/builders/domain.ts +101 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/builders/flow.ts +29 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/builders/message.ts +84 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/builders/service.ts +147 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/builders/shared.ts +146 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +1073 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/sidebar-builder.ts +365 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/storage.ts +90 -0
- package/eventcatalog/src/components/SideNav/SideNav.astro +18 -28
- package/eventcatalog/src/content.config.ts +2 -0
- package/eventcatalog/src/enterprise/custom-documentation/pages/docs/custom/index.astro +10 -4
- package/eventcatalog/src/enterprise/eventcatalog-chat/pages/chat/index.astro +3 -3
- package/eventcatalog/src/layouts/DirectoryLayout.astro +2 -2
- package/eventcatalog/src/layouts/DiscoverLayout.astro +3 -3
- package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +85 -63
- package/eventcatalog/src/layouts/VisualiserLayout.astro +3 -3
- package/eventcatalog/src/pages/_index.astro +530 -110
- package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.ts +64 -0
- package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/index.astro +29 -0
- package/eventcatalog/src/pages/directory/[type]/_index.data.ts +4 -4
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/_index.data.ts +1 -4
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/_index.data.ts +3 -3
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/index.astro +1 -5
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +362 -190
- package/eventcatalog/src/pages/docs/[type]/[id]/[version].md.ts +1 -1
- package/eventcatalog/src/pages/docs/[type]/[id]/index.astro +4 -4
- package/eventcatalog/src/pages/docs/[type]/[id]/language/_index.data.ts +1 -4
- package/eventcatalog/src/pages/docs/[type]/[id]/language/index.astro +3 -27
- package/eventcatalog/src/pages/docs/teams/[id]/_index.data.ts +2 -2
- package/eventcatalog/src/pages/docs/users/[id]/_index.data.ts +2 -2
- package/eventcatalog/src/pages/index.astro +14 -5
- package/eventcatalog/src/pages/nav-index.json.ts +30 -0
- package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/_index.data.ts +77 -0
- package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/index.astro +90 -0
- package/eventcatalog/src/pages/schemas/{index.astro → explorer/index.astro} +3 -3
- package/eventcatalog/src/pages/studio.astro +3 -3
- package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/_index.data.ts +4 -3
- package/eventcatalog/src/pages/visualiser/[type]/[id]/index.astro +2 -2
- package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/entity-map/_index.data.ts +4 -3
- package/eventcatalog/src/stores/favorites-store.ts +83 -0
- package/eventcatalog/src/stores/sidebar-store.ts +8 -0
- package/eventcatalog/src/utils/collections/changelogs.ts +7 -4
- package/eventcatalog/src/utils/{channels.ts → collections/channels.ts} +81 -31
- package/eventcatalog/src/utils/collections/commands.ts +134 -0
- package/eventcatalog/src/utils/collections/containers.ts +44 -33
- package/eventcatalog/src/utils/collections/domains.ts +204 -62
- package/eventcatalog/src/utils/{entities.ts → collections/entities.ts} +44 -24
- package/eventcatalog/src/utils/collections/events.ts +136 -0
- package/eventcatalog/src/utils/collections/flows.ts +59 -25
- package/eventcatalog/src/utils/{messages.ts → collections/messages.ts} +13 -4
- package/eventcatalog/src/utils/{queries.ts → collections/queries.ts} +49 -28
- package/eventcatalog/src/utils/collections/services.ts +100 -68
- package/eventcatalog/src/utils/collections/teams.ts +94 -0
- package/eventcatalog/src/utils/collections/users.ts +122 -0
- package/eventcatalog/src/utils/collections/util.ts +57 -1
- package/eventcatalog/src/utils/feature.ts +3 -1
- package/eventcatalog/src/utils/{collections/file-diffs.ts → file-diffs.ts} +1 -1
- package/eventcatalog/src/utils/node-graphs/container-node-graph.ts +2 -0
- package/eventcatalog/src/utils/node-graphs/domain-entity-map.ts +16 -6
- package/eventcatalog/src/utils/node-graphs/domains-canvas.ts +14 -10
- package/eventcatalog/src/utils/node-graphs/domains-node-graph.ts +36 -64
- package/eventcatalog/src/utils/node-graphs/flows-node-graph.ts +23 -19
- package/eventcatalog/src/utils/node-graphs/message-node-graph.ts +36 -49
- package/eventcatalog/src/utils/node-graphs/services-node-graph.ts +22 -18
- package/eventcatalog/src/utils/page-loaders/page-data-loader.ts +4 -4
- package/eventcatalog/tailwind.config.mjs +14 -0
- package/eventcatalog/tsconfig.json +2 -1
- package/package.json +7 -4
- package/eventcatalog/public/logo_old.png +0 -0
- package/eventcatalog/src/components/DiscoverInsight.astro +0 -61
- package/eventcatalog/src/components/Grids/ServiceGrid.tsx +0 -534
- package/eventcatalog/src/components/Lists/CustomSideBarSectionList.astro +0 -55
- package/eventcatalog/src/components/Lists/ProtocolList.tsx +0 -74
- package/eventcatalog/src/components/Lists/RepositoryList.astro +0 -37
- package/eventcatalog/src/components/Lists/SpecificationsList.astro +0 -67
- package/eventcatalog/src/components/SideBars/ChannelSideBar.astro +0 -204
- package/eventcatalog/src/components/SideBars/ContainerSideBar.astro +0 -180
- package/eventcatalog/src/components/SideBars/DomainSideBar.astro +0 -273
- package/eventcatalog/src/components/SideBars/EntitySideBar.astro +0 -139
- package/eventcatalog/src/components/SideBars/FlowSideBar.astro +0 -128
- package/eventcatalog/src/components/SideBars/MessageSideBar.astro +0 -248
- package/eventcatalog/src/components/SideBars/ServiceSideBar.astro +0 -294
- package/eventcatalog/src/components/SideNav/ListViewSideBar/components/CollapsibleGroup.tsx +0 -46
- package/eventcatalog/src/components/SideNav/ListViewSideBar/components/MessageList.tsx +0 -78
- package/eventcatalog/src/components/SideNav/ListViewSideBar/components/SpecificationList.tsx +0 -83
- package/eventcatalog/src/components/SideNav/ListViewSideBar/index.tsx +0 -1250
- package/eventcatalog/src/components/SideNav/ListViewSideBar/types.ts +0 -91
- package/eventcatalog/src/components/SideNav/ListViewSideBar/utils.ts +0 -201
- package/eventcatalog/src/components/SideNav/TreeView/getTreeView.ts +0 -190
- package/eventcatalog/src/components/SideNav/TreeView/index.tsx +0 -94
- package/eventcatalog/src/components/TreeView/index.tsx +0 -328
- package/eventcatalog/src/components/TreeView/styles.module.css +0 -264
- package/eventcatalog/src/components/TreeView/useSlots.ts +0 -95
- package/eventcatalog/src/pages/architecture/[type]/index.astro +0 -14
- package/eventcatalog/src/pages/architecture/architecture.astro +0 -101
- package/eventcatalog/src/pages/architecture/docs/[type]/index.astro +0 -14
- package/eventcatalog/src/utils/commands.ts +0 -112
- package/eventcatalog/src/utils/events.ts +0 -108
- package/eventcatalog/src/utils/generators/index.ts +0 -10
- package/eventcatalog/src/utils/teams.ts +0 -72
- package/eventcatalog/src/utils/users.ts +0 -72
|
@@ -1,534 +0,0 @@
|
|
|
1
|
-
import { useState, useMemo, useEffect, memo } from 'react';
|
|
2
|
-
import { ServerIcon, ChevronRightIcon, Squares2X2Icon, QueueListIcon, CircleStackIcon } from '@heroicons/react/24/outline';
|
|
3
|
-
import { RectangleGroupIcon } from '@heroicons/react/24/outline';
|
|
4
|
-
import { buildUrl, buildUrlWithParams } from '@utils/url-builder';
|
|
5
|
-
import type { CollectionEntry } from 'astro:content';
|
|
6
|
-
import type { CollectionMessageTypes } from '@types';
|
|
7
|
-
import { getCollectionStyles } from './utils';
|
|
8
|
-
import { SearchBar, TypeFilters, Pagination } from './components';
|
|
9
|
-
import type { ExtendedDomain } from './DomainGrid';
|
|
10
|
-
import { BoxIcon } from 'lucide-react';
|
|
11
|
-
|
|
12
|
-
// Message component for reuse
|
|
13
|
-
const Message = memo(({ message, collection }: { message: any; collection: string }) => {
|
|
14
|
-
const { Icon, color } = getCollectionStyles(message.collection);
|
|
15
|
-
return (
|
|
16
|
-
<a
|
|
17
|
-
href={buildUrl(`/docs/${message.collection}/${message.data.id}/${message.data.version}`)}
|
|
18
|
-
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"
|
|
19
|
-
>
|
|
20
|
-
<div className="bg-white border-r border-gray-200 px-2 py-1.5 rounded-l-md">
|
|
21
|
-
<Icon className={`h-3 w-3 text-${color}-500`} />
|
|
22
|
-
</div>
|
|
23
|
-
<span className="px-1 py-1 truncate max-w-[140px]">{message.data.name}</span>
|
|
24
|
-
</a>
|
|
25
|
-
);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
// Messages Container component
|
|
29
|
-
const MessagesContainer = memo(
|
|
30
|
-
({ messages, type, selectedTypes }: { messages: any[]; type: 'receives' | 'sends'; selectedTypes: string[] }) => {
|
|
31
|
-
const bgColor = type === 'receives' ? 'blue' : 'green';
|
|
32
|
-
const MAX_MESSAGES_DISPLAYED = 4;
|
|
33
|
-
|
|
34
|
-
const filteredMessages = messages?.filter(
|
|
35
|
-
(message: any) => selectedTypes.length === 0 || selectedTypes.includes(message.collection)
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
const messagesToShow = filteredMessages?.slice(0, MAX_MESSAGES_DISPLAYED);
|
|
39
|
-
const remainingMessagesCount = filteredMessages ? filteredMessages.length - MAX_MESSAGES_DISPLAYED : 0;
|
|
40
|
-
|
|
41
|
-
return (
|
|
42
|
-
<div className={`flex-1 h-full flex flex-col bg-${bgColor}-100 border border-${bgColor}-300 rounded-lg p-4`}>
|
|
43
|
-
<div className="space-y-2 flex-1">
|
|
44
|
-
{messagesToShow?.map((message: any) => (
|
|
45
|
-
<Message key={message.data.name} message={message} collection={message.collection} />
|
|
46
|
-
))}
|
|
47
|
-
{remainingMessagesCount > 0 && (
|
|
48
|
-
<div className="text-center py-1">
|
|
49
|
-
<p className="text-gray-500 text-[10px]">+ {remainingMessagesCount} more</p>
|
|
50
|
-
</div>
|
|
51
|
-
)}
|
|
52
|
-
{(!messages?.length ||
|
|
53
|
-
(selectedTypes.length > 0 && !messages?.some((message: any) => selectedTypes.includes(message.collection)))) && (
|
|
54
|
-
<div className="text-center py-4">
|
|
55
|
-
<p className="text-gray-500 text-[10px]">
|
|
56
|
-
{selectedTypes.length > 0
|
|
57
|
-
? `Service does not ${type} ${selectedTypes.join(' or ')}`
|
|
58
|
-
: `Service does not ${type} any messages`}
|
|
59
|
-
</p>
|
|
60
|
-
</div>
|
|
61
|
-
)}
|
|
62
|
-
</div>
|
|
63
|
-
</div>
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
// Service Card component
|
|
69
|
-
const ServiceCard = memo(({ service, urlParams, selectedTypes }: { service: any; urlParams: any; selectedTypes: string[] }) => {
|
|
70
|
-
return (
|
|
71
|
-
<a
|
|
72
|
-
href={buildUrlWithParams('/architecture/messages', {
|
|
73
|
-
serviceName: service.data.name,
|
|
74
|
-
serviceId: service.data.id,
|
|
75
|
-
domainId: urlParams?.domainId,
|
|
76
|
-
domainName: urlParams?.domainName,
|
|
77
|
-
})}
|
|
78
|
-
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 "
|
|
79
|
-
>
|
|
80
|
-
<div className="p-6">
|
|
81
|
-
<div className="flex items-center justify-between mb-3">
|
|
82
|
-
<div className="flex items-center gap-2 w-full">
|
|
83
|
-
<ServerIcon className="h-5 w-5 text-pink-500" />
|
|
84
|
-
<h3 className="text-lg font-semibold text-gray-900 truncate group-hover:underline transition-colors duration-200 w-full max-w-[90%]">
|
|
85
|
-
{service.data.name || service.data.id} (v{service.data.version})
|
|
86
|
-
</h3>
|
|
87
|
-
</div>
|
|
88
|
-
</div>
|
|
89
|
-
|
|
90
|
-
{service.data.summary && <p className="text-gray-600 text-sm line-clamp-2 min-h-[2.5rem]">{service.data.summary}</p>}
|
|
91
|
-
|
|
92
|
-
{!urlParams?.serviceName && (
|
|
93
|
-
<div className="flex items-center gap-4 mt-4">
|
|
94
|
-
<MessagesContainer messages={service.data.receives} type="receives" selectedTypes={selectedTypes} />
|
|
95
|
-
|
|
96
|
-
<div className="flex items-center gap-2 max-w-[200px]">
|
|
97
|
-
<div className="w-4 h-[2px] bg-blue-200"></div>
|
|
98
|
-
<div className="bg-white border-2 border-pink-100 rounded-lg p-4 shadow-sm">
|
|
99
|
-
<div className="flex flex-col items-center gap-3">
|
|
100
|
-
<ServerIcon className="h-8 w-8 text-pink-500" />
|
|
101
|
-
<div className="text-center">
|
|
102
|
-
<p className="text-sm font-medium text-gray-900">{service.data.name || service.data.id}</p>
|
|
103
|
-
<p className="text-xs text-gray-500">v{service.data.version}</p>
|
|
104
|
-
</div>
|
|
105
|
-
</div>
|
|
106
|
-
</div>
|
|
107
|
-
<div className="w-4 h-[2px] bg-emerald-200"></div>
|
|
108
|
-
</div>
|
|
109
|
-
|
|
110
|
-
<MessagesContainer messages={service.data.sends} type="sends" selectedTypes={selectedTypes} />
|
|
111
|
-
</div>
|
|
112
|
-
)}
|
|
113
|
-
|
|
114
|
-
{/* Container lists at the bottom */}
|
|
115
|
-
{((service.data.readsFrom && service.data.readsFrom.length > 0) ||
|
|
116
|
-
(service.data.writesTo && service.data.writesTo.length > 0)) && (
|
|
117
|
-
<div className="mt-4 pt-4 border-t border-gray-200 grid grid-cols-2 gap-4">
|
|
118
|
-
{/* Reads From */}
|
|
119
|
-
{service.data.readsFrom && service.data.readsFrom.length > 0 && (
|
|
120
|
-
<div className="space-y-2">
|
|
121
|
-
<div className="flex items-center gap-2">
|
|
122
|
-
<CircleStackIcon className="h-4 w-4 text-orange-500" />
|
|
123
|
-
<h4 className="text-xs font-semibold text-gray-700">Reads from</h4>
|
|
124
|
-
</div>
|
|
125
|
-
<div className="flex flex-wrap gap-1">
|
|
126
|
-
{service.data.readsFrom.slice(0, 3).map((container: any) => (
|
|
127
|
-
<a
|
|
128
|
-
key={container.id}
|
|
129
|
-
href={buildUrl(`/docs/containers/${container.data.id}/${container.data.version}`)}
|
|
130
|
-
className="group inline-flex items-center gap-1 px-2 py-1 bg-orange-100 border border-orange-300 rounded-md text-[11px] font-medium hover:bg-orange-200 transition-colors duration-200"
|
|
131
|
-
>
|
|
132
|
-
<CircleStackIcon className="h-3 w-3 text-orange-600" />
|
|
133
|
-
<span className="text-orange-800">{container.data.name}</span>
|
|
134
|
-
</a>
|
|
135
|
-
))}
|
|
136
|
-
{service.data.readsFrom.length > 3 && (
|
|
137
|
-
<span className="inline-flex items-center px-2 py-1 text-xs text-gray-500">
|
|
138
|
-
+ {service.data.readsFrom.length - 3} more
|
|
139
|
-
</span>
|
|
140
|
-
)}
|
|
141
|
-
</div>
|
|
142
|
-
</div>
|
|
143
|
-
)}
|
|
144
|
-
|
|
145
|
-
{/* Writes To */}
|
|
146
|
-
{service.data.writesTo && service.data.writesTo.length > 0 && (
|
|
147
|
-
<div className="space-y-2">
|
|
148
|
-
<div className="flex items-center gap-2">
|
|
149
|
-
<CircleStackIcon className="h-4 w-4 text-purple-500" />
|
|
150
|
-
<h4 className="text-xs font-semibold text-gray-700">Writes to</h4>
|
|
151
|
-
</div>
|
|
152
|
-
<div className="flex flex-wrap gap-1">
|
|
153
|
-
{service.data.writesTo.slice(0, 3).map((container: any) => (
|
|
154
|
-
<a
|
|
155
|
-
key={container.id}
|
|
156
|
-
href={buildUrl(`/docs/containers/${container.data.id}/${container.data.version}`)}
|
|
157
|
-
className="group inline-flex items-center gap-1 px-2 py-1 bg-purple-100 border border-purple-300 rounded-md text-[11px] font-medium hover:bg-purple-200 transition-colors duration-200"
|
|
158
|
-
>
|
|
159
|
-
<CircleStackIcon className="h-3 w-3 text-purple-600" />
|
|
160
|
-
<span className="text-purple-800">{container.data.name}</span>
|
|
161
|
-
</a>
|
|
162
|
-
))}
|
|
163
|
-
{service.data.writesTo.length > 3 && (
|
|
164
|
-
<span className="inline-flex items-center px-2 py-1 text-xs text-gray-500">
|
|
165
|
-
+ {service.data.writesTo.length - 3} more
|
|
166
|
-
</span>
|
|
167
|
-
)}
|
|
168
|
-
</div>
|
|
169
|
-
</div>
|
|
170
|
-
)}
|
|
171
|
-
</div>
|
|
172
|
-
)}
|
|
173
|
-
</div>
|
|
174
|
-
</a>
|
|
175
|
-
);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
// Domain Section component
|
|
179
|
-
const DomainSection = memo(
|
|
180
|
-
({
|
|
181
|
-
domain,
|
|
182
|
-
services,
|
|
183
|
-
urlParams,
|
|
184
|
-
selectedTypes,
|
|
185
|
-
isMultiColumn,
|
|
186
|
-
}: {
|
|
187
|
-
domain: any;
|
|
188
|
-
services: any[];
|
|
189
|
-
urlParams: any;
|
|
190
|
-
selectedTypes: string[];
|
|
191
|
-
isMultiColumn: boolean;
|
|
192
|
-
}) => {
|
|
193
|
-
const subdomains = domain.data.domains || [];
|
|
194
|
-
const allSubDomainServices = subdomains.map((subdomain: any) => subdomain.data.services || []).flat();
|
|
195
|
-
|
|
196
|
-
const servicesWithoutSubdomains = services.filter((service) => {
|
|
197
|
-
return !allSubDomainServices.some((s: any) => s.id === service.data.id);
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
return (
|
|
201
|
-
<div className="space-y-6">
|
|
202
|
-
{servicesWithoutSubdomains.length > 0 && (
|
|
203
|
-
<div
|
|
204
|
-
className={`grid gap-6 ${isMultiColumn ? 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-2 xl:grid-cols-2' : 'grid-cols-1'}`}
|
|
205
|
-
>
|
|
206
|
-
{servicesWithoutSubdomains.map((service) => (
|
|
207
|
-
<ServiceCard key={service.data.id} service={service} urlParams={urlParams} selectedTypes={selectedTypes} />
|
|
208
|
-
))}
|
|
209
|
-
</div>
|
|
210
|
-
)}
|
|
211
|
-
|
|
212
|
-
{subdomains.map((subdomainRef: any) => {
|
|
213
|
-
const subdomain = domain.data.domains?.find((d: any) => d.data.id === subdomainRef.data.id);
|
|
214
|
-
if (!subdomain) return null;
|
|
215
|
-
|
|
216
|
-
const subdomainServices = services.filter((service) =>
|
|
217
|
-
subdomain.data.services?.some((s: any) => s.id === service.data.id)
|
|
218
|
-
);
|
|
219
|
-
|
|
220
|
-
if (subdomainServices.length === 0) return null;
|
|
221
|
-
|
|
222
|
-
return (
|
|
223
|
-
<div key={subdomain.data.id} className="bg-orange-50 border-2 border-orange-400 rounded-lg p-6 space-y-4">
|
|
224
|
-
<div className="flex items-center justify-between">
|
|
225
|
-
<div className="flex items-center gap-2">
|
|
226
|
-
<RectangleGroupIcon className="h-5 w-5 text-orange-500" />
|
|
227
|
-
<h3 className="text-xl font-semibold text-gray-900">{subdomain.data.name} (Subdomain)</h3>
|
|
228
|
-
</div>
|
|
229
|
-
<div className="flex gap-2">
|
|
230
|
-
<a
|
|
231
|
-
href={buildUrl(`/visualiser/domains/${subdomain.data.id}`)}
|
|
232
|
-
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"
|
|
233
|
-
>
|
|
234
|
-
View in visualizer
|
|
235
|
-
</a>
|
|
236
|
-
<a
|
|
237
|
-
href={buildUrl(`/docs/domains/${subdomain.data.id}`)}
|
|
238
|
-
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"
|
|
239
|
-
>
|
|
240
|
-
Read documentation
|
|
241
|
-
</a>
|
|
242
|
-
</div>
|
|
243
|
-
</div>
|
|
244
|
-
|
|
245
|
-
{/* Entities */}
|
|
246
|
-
{subdomain.data.entities && subdomain.data.entities.length > 0 && (
|
|
247
|
-
<div className="space-y-2">
|
|
248
|
-
<div className="flex items-center gap-2">
|
|
249
|
-
<BoxIcon className="h-4 w-4 text-purple-500" />
|
|
250
|
-
<h4 className="text-xs font-semibold text-gray-700">Entities</h4>
|
|
251
|
-
</div>
|
|
252
|
-
<div className="flex flex-wrap gap-1">
|
|
253
|
-
{subdomain.data.entities.map((entity: any) => (
|
|
254
|
-
<a
|
|
255
|
-
key={entity.id}
|
|
256
|
-
href={buildUrl(`/docs/entities/${entity.id}`)}
|
|
257
|
-
className="group inline-flex items-center gap-1 px-2 py-1 bg-purple-100 border border-purple-300 rounded-md text-[11px] font-medium hover:bg-purple-200 transition-colors duration-200"
|
|
258
|
-
>
|
|
259
|
-
<BoxIcon className="h-3 w-3 text-purple-600" />
|
|
260
|
-
<span className="text-purple-800">{entity.id}</span>
|
|
261
|
-
</a>
|
|
262
|
-
))}
|
|
263
|
-
</div>
|
|
264
|
-
</div>
|
|
265
|
-
)}
|
|
266
|
-
|
|
267
|
-
<div
|
|
268
|
-
className={`grid gap-6 ${isMultiColumn ? 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-2 xl:grid-cols-2' : 'grid-cols-1'}`}
|
|
269
|
-
>
|
|
270
|
-
{subdomainServices.map((service) => (
|
|
271
|
-
<ServiceCard
|
|
272
|
-
key={service.data.id}
|
|
273
|
-
service={service}
|
|
274
|
-
urlParams={{
|
|
275
|
-
...urlParams,
|
|
276
|
-
domainId: subdomain.data.id,
|
|
277
|
-
domainName: `${subdomain.data.name} (Subdomain)`,
|
|
278
|
-
}}
|
|
279
|
-
selectedTypes={selectedTypes}
|
|
280
|
-
/>
|
|
281
|
-
))}
|
|
282
|
-
</div>
|
|
283
|
-
</div>
|
|
284
|
-
);
|
|
285
|
-
})}
|
|
286
|
-
</div>
|
|
287
|
-
);
|
|
288
|
-
}
|
|
289
|
-
);
|
|
290
|
-
|
|
291
|
-
interface ServiceGridProps {
|
|
292
|
-
services: CollectionEntry<'services'>[];
|
|
293
|
-
domains: ExtendedDomain[];
|
|
294
|
-
embeded: boolean;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Main ServiceGrid component
|
|
298
|
-
export default function ServiceGrid({ services, domains, embeded }: ServiceGridProps) {
|
|
299
|
-
const [searchQuery, setSearchQuery] = useState('');
|
|
300
|
-
const [currentPage, setCurrentPage] = useState(1);
|
|
301
|
-
const [selectedTypes, setSelectedTypes] = useState<CollectionMessageTypes[]>([]);
|
|
302
|
-
const [isMultiColumn, setIsMultiColumn] = useState(false);
|
|
303
|
-
const ITEMS_PER_PAGE = 16;
|
|
304
|
-
const [urlParams, setUrlParams] = useState<{
|
|
305
|
-
serviceIds?: string[];
|
|
306
|
-
domainId?: string;
|
|
307
|
-
domainName?: string;
|
|
308
|
-
serviceName?: string;
|
|
309
|
-
} | null>(null);
|
|
310
|
-
|
|
311
|
-
useEffect(() => {
|
|
312
|
-
const params = new URLSearchParams(window.location.search);
|
|
313
|
-
setUrlParams({
|
|
314
|
-
serviceIds: params.get('serviceIds')?.split(',').filter(Boolean),
|
|
315
|
-
domainId: params.get('domainId') || undefined,
|
|
316
|
-
domainName: params.get('domainName') || undefined,
|
|
317
|
-
serviceName: params.get('serviceName') || undefined,
|
|
318
|
-
});
|
|
319
|
-
}, []);
|
|
320
|
-
|
|
321
|
-
useEffect(() => {
|
|
322
|
-
if (typeof window !== 'undefined') {
|
|
323
|
-
const saved = localStorage.getItem('EventCatalog:ServiceColumnLayout');
|
|
324
|
-
if (saved !== null) {
|
|
325
|
-
setIsMultiColumn(saved === 'multi');
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
}, []);
|
|
329
|
-
|
|
330
|
-
const toggleColumnLayout = () => {
|
|
331
|
-
const newValue = !isMultiColumn;
|
|
332
|
-
setIsMultiColumn(newValue);
|
|
333
|
-
if (typeof window !== 'undefined') {
|
|
334
|
-
localStorage.setItem('EventCatalog:ServiceColumnLayout', newValue ? 'multi' : 'single');
|
|
335
|
-
}
|
|
336
|
-
};
|
|
337
|
-
|
|
338
|
-
const filteredAndSortedServices = useMemo(() => {
|
|
339
|
-
if (urlParams === null) return [];
|
|
340
|
-
|
|
341
|
-
let result = [...services];
|
|
342
|
-
|
|
343
|
-
if (urlParams.serviceIds?.length) {
|
|
344
|
-
result = result.filter(
|
|
345
|
-
(service) => urlParams.serviceIds?.includes(service.data.id) && !service.data.id.includes('/versioned/')
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
if (searchQuery) {
|
|
350
|
-
const query = searchQuery.toLowerCase();
|
|
351
|
-
result = result.filter(
|
|
352
|
-
(service) =>
|
|
353
|
-
service.data.name?.toLowerCase().includes(query) ||
|
|
354
|
-
service.data.summary?.toLowerCase().includes(query) ||
|
|
355
|
-
service.data.sends?.some((message: any) => message.data.name.toLowerCase().includes(query)) ||
|
|
356
|
-
service.data.receives?.some((message: any) => message.data.name.toLowerCase().includes(query))
|
|
357
|
-
);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
if (selectedTypes.length > 0) {
|
|
361
|
-
result = result.filter((service) => {
|
|
362
|
-
const hasMatchingSends = service.data.sends?.some((message: any) => selectedTypes.includes(message.collection));
|
|
363
|
-
const hasMatchingReceives = service.data.receives?.some((message: any) => selectedTypes.includes(message.collection));
|
|
364
|
-
return hasMatchingSends || hasMatchingReceives;
|
|
365
|
-
});
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
result.sort((a, b) => (a.data.name || a.data.id).localeCompare(b.data.name || b.data.id));
|
|
369
|
-
return result;
|
|
370
|
-
}, [services, searchQuery, urlParams, selectedTypes]);
|
|
371
|
-
|
|
372
|
-
const paginatedServices = useMemo(() => {
|
|
373
|
-
if (urlParams?.domainId || urlParams?.serviceIds?.length) {
|
|
374
|
-
return filteredAndSortedServices;
|
|
375
|
-
}
|
|
376
|
-
const startIndex = (currentPage - 1) * ITEMS_PER_PAGE;
|
|
377
|
-
return filteredAndSortedServices.slice(startIndex, startIndex + ITEMS_PER_PAGE);
|
|
378
|
-
}, [filteredAndSortedServices, currentPage, urlParams]);
|
|
379
|
-
|
|
380
|
-
const totalPages = useMemo(() => {
|
|
381
|
-
if (urlParams?.domainId || urlParams?.serviceIds?.length) return 1;
|
|
382
|
-
return Math.ceil(filteredAndSortedServices.length / ITEMS_PER_PAGE);
|
|
383
|
-
}, [filteredAndSortedServices.length, urlParams]);
|
|
384
|
-
|
|
385
|
-
useEffect(() => {
|
|
386
|
-
setCurrentPage(1);
|
|
387
|
-
}, [searchQuery, selectedTypes]);
|
|
388
|
-
|
|
389
|
-
return (
|
|
390
|
-
<div>
|
|
391
|
-
{/* Breadcrumb */}
|
|
392
|
-
<nav className="mb-4 flex items-center space-x-2 text-sm text-gray-500">
|
|
393
|
-
<a href={buildUrl('/architecture/domains')} className="hover:text-gray-700 hover:underline flex items-center gap-2">
|
|
394
|
-
<RectangleGroupIcon className="h-4 w-4" />
|
|
395
|
-
Domains
|
|
396
|
-
</a>
|
|
397
|
-
<ChevronRightIcon className="h-4 w-4" />
|
|
398
|
-
<a href={buildUrl('/architecture/services')} className="hover:text-gray-700 hover:underline flex items-center gap-2">
|
|
399
|
-
<ServerIcon className="h-4 w-4" />
|
|
400
|
-
Services
|
|
401
|
-
</a>
|
|
402
|
-
{urlParams?.domainId && (
|
|
403
|
-
<>
|
|
404
|
-
<ChevronRightIcon className="h-4 w-4" />
|
|
405
|
-
<span className="text-gray-900">{urlParams.domainId}</span>
|
|
406
|
-
</>
|
|
407
|
-
)}
|
|
408
|
-
</nav>
|
|
409
|
-
|
|
410
|
-
{/* Title Section */}
|
|
411
|
-
<div className="relative border-b border-gray-200 mb-4 pb-4">
|
|
412
|
-
<div className="md:flex md:items-start md:justify-between">
|
|
413
|
-
<div className="min-w-0 flex-1 max-w-lg">
|
|
414
|
-
<div className="flex items-center gap-2">
|
|
415
|
-
<h1 className="text-2xl font-bold leading-7 text-gray-900 sm:text-3xl sm:truncate">
|
|
416
|
-
{urlParams?.domainId ? `${urlParams.domainName} Architecture` : 'All Services'}
|
|
417
|
-
</h1>
|
|
418
|
-
</div>
|
|
419
|
-
<p className="mt-2 text-sm text-gray-500">
|
|
420
|
-
{urlParams?.domainId
|
|
421
|
-
? `Browse services and messages in the ${urlParams.domainId} domain`
|
|
422
|
-
: 'Browse and discover services in your event-driven architecture'}
|
|
423
|
-
</p>
|
|
424
|
-
</div>
|
|
425
|
-
|
|
426
|
-
<div className="mt-6 md:mt-0 md:ml-4 flex-shrink-0 flex items-center gap-3">
|
|
427
|
-
<SearchBar
|
|
428
|
-
searchQuery={searchQuery}
|
|
429
|
-
onSearchChange={setSearchQuery}
|
|
430
|
-
placeholder="Search services by name, summary, or messages..."
|
|
431
|
-
totalResults={filteredAndSortedServices.length}
|
|
432
|
-
totalItems={services.length}
|
|
433
|
-
/>
|
|
434
|
-
<button
|
|
435
|
-
onClick={toggleColumnLayout}
|
|
436
|
-
className="flex items-center justify-center p-2 bg-white border border-gray-300 rounded-md hover:bg-gray-50 transition-colors duration-200"
|
|
437
|
-
title={isMultiColumn ? 'Switch to single column' : 'Switch to multi column'}
|
|
438
|
-
>
|
|
439
|
-
{isMultiColumn ? (
|
|
440
|
-
<QueueListIcon className="h-5 w-5 text-gray-600" />
|
|
441
|
-
) : (
|
|
442
|
-
<Squares2X2Icon className="h-5 w-5 text-gray-600" />
|
|
443
|
-
)}
|
|
444
|
-
</button>
|
|
445
|
-
</div>
|
|
446
|
-
</div>
|
|
447
|
-
</div>
|
|
448
|
-
|
|
449
|
-
<div className="mb-8">
|
|
450
|
-
{/* Results count and pagination */}
|
|
451
|
-
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
|
452
|
-
<TypeFilters
|
|
453
|
-
selectedTypes={selectedTypes}
|
|
454
|
-
onTypeChange={setSelectedTypes}
|
|
455
|
-
filteredCount={filteredAndSortedServices.length}
|
|
456
|
-
totalCount={services.length}
|
|
457
|
-
/>
|
|
458
|
-
<div className="text-sm text-gray-500">
|
|
459
|
-
{urlParams?.domainId || urlParams?.serviceIds?.length ? (
|
|
460
|
-
<span>
|
|
461
|
-
Showing {filteredAndSortedServices.length} services in the {urlParams.domainId} domain
|
|
462
|
-
</span>
|
|
463
|
-
) : (
|
|
464
|
-
<span>
|
|
465
|
-
Showing {(currentPage - 1) * ITEMS_PER_PAGE + 1} to{' '}
|
|
466
|
-
{Math.min(currentPage * ITEMS_PER_PAGE, filteredAndSortedServices.length)} of {filteredAndSortedServices.length}{' '}
|
|
467
|
-
services
|
|
468
|
-
</span>
|
|
469
|
-
)}
|
|
470
|
-
</div>
|
|
471
|
-
{!(urlParams?.domainId || urlParams?.serviceIds?.length) && (
|
|
472
|
-
<Pagination
|
|
473
|
-
currentPage={currentPage}
|
|
474
|
-
totalPages={totalPages}
|
|
475
|
-
totalItems={filteredAndSortedServices.length}
|
|
476
|
-
itemsPerPage={ITEMS_PER_PAGE}
|
|
477
|
-
onPageChange={setCurrentPage}
|
|
478
|
-
/>
|
|
479
|
-
)}
|
|
480
|
-
</div>
|
|
481
|
-
</div>
|
|
482
|
-
|
|
483
|
-
{filteredAndSortedServices.length > 0 && (
|
|
484
|
-
<div className={`rounded-xl overflow-hidden ${urlParams?.domainId ? 'bg-yellow-50 p-8 border-2 border-yellow-400' : ''}`}>
|
|
485
|
-
{urlParams?.domainName ? (
|
|
486
|
-
domains
|
|
487
|
-
.filter((domain: ExtendedDomain) => domain.data.id === urlParams.domainId)
|
|
488
|
-
.map((domain: ExtendedDomain) => (
|
|
489
|
-
<DomainSection
|
|
490
|
-
key={domain.data.id}
|
|
491
|
-
domain={domain}
|
|
492
|
-
services={paginatedServices}
|
|
493
|
-
urlParams={urlParams}
|
|
494
|
-
selectedTypes={selectedTypes}
|
|
495
|
-
isMultiColumn={isMultiColumn}
|
|
496
|
-
/>
|
|
497
|
-
))
|
|
498
|
-
) : (
|
|
499
|
-
<div
|
|
500
|
-
className={`grid gap-6 ${isMultiColumn ? 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-2 xl:grid-cols-2' : 'grid-cols-1'}`}
|
|
501
|
-
>
|
|
502
|
-
{paginatedServices.map((service) => (
|
|
503
|
-
<ServiceCard key={service.data.id} service={service} urlParams={urlParams} selectedTypes={selectedTypes} />
|
|
504
|
-
))}
|
|
505
|
-
</div>
|
|
506
|
-
)}
|
|
507
|
-
</div>
|
|
508
|
-
)}
|
|
509
|
-
|
|
510
|
-
{filteredAndSortedServices.length === 0 && (
|
|
511
|
-
<div className="text-center py-12 bg-gray-50 rounded-lg">
|
|
512
|
-
<p className="text-gray-500 text-lg">
|
|
513
|
-
{selectedTypes.length > 0
|
|
514
|
-
? `No services found that ${selectedTypes.length > 1 ? 'handle' : 'handles'} ${selectedTypes.join(' or ')} messages`
|
|
515
|
-
: 'No services found matching your criteria'}
|
|
516
|
-
</p>
|
|
517
|
-
</div>
|
|
518
|
-
)}
|
|
519
|
-
|
|
520
|
-
{/* Bottom pagination */}
|
|
521
|
-
{!(urlParams?.domainId || urlParams?.serviceIds?.length) && (
|
|
522
|
-
<div className="mt-8 border-t border-gray-200">
|
|
523
|
-
<Pagination
|
|
524
|
-
currentPage={currentPage}
|
|
525
|
-
totalPages={totalPages}
|
|
526
|
-
totalItems={filteredAndSortedServices.length}
|
|
527
|
-
itemsPerPage={ITEMS_PER_PAGE}
|
|
528
|
-
onPageChange={setCurrentPage}
|
|
529
|
-
/>
|
|
530
|
-
</div>
|
|
531
|
-
)}
|
|
532
|
-
</div>
|
|
533
|
-
);
|
|
534
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import { buildUrl } from '@utils/url-builder';
|
|
3
|
-
import { getCollection } from 'astro:content';
|
|
4
|
-
import { getItemsFromCollectionByIdAndSemverOrLatest } from '@utils/collections/util';
|
|
5
|
-
import PillListFlat from './PillListFlat';
|
|
6
|
-
import { resourceToCollectionMap } from '@utils/collections/util';
|
|
7
|
-
interface Props {
|
|
8
|
-
section?: {
|
|
9
|
-
title?: string;
|
|
10
|
-
limit?: number;
|
|
11
|
-
items: {
|
|
12
|
-
id: string;
|
|
13
|
-
type: string;
|
|
14
|
-
version?: string;
|
|
15
|
-
}[];
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const { section } = Astro.props;
|
|
20
|
-
const title = section?.title || 'Custom Section';
|
|
21
|
-
const limit = section?.limit || 10;
|
|
22
|
-
const sectionItems = section?.items || [];
|
|
23
|
-
|
|
24
|
-
// Array to store resolved related resources
|
|
25
|
-
const resolvedResources = [];
|
|
26
|
-
|
|
27
|
-
// Fetch related resources
|
|
28
|
-
for (const resource of sectionItems) {
|
|
29
|
-
try {
|
|
30
|
-
// Use type assertion to ensure TypeScript understands this is a valid collection name
|
|
31
|
-
const collectionName = resourceToCollectionMap[resource.type as keyof typeof resourceToCollectionMap];
|
|
32
|
-
|
|
33
|
-
// Use getEntry instead of getCollection for single item lookup by ID
|
|
34
|
-
const allItemsInCollection = await getCollection(collectionName);
|
|
35
|
-
// @ts-ignore
|
|
36
|
-
const entryToMatchVersion = getItemsFromCollectionByIdAndSemverOrLatest(allItemsInCollection, resource.id, resource.version);
|
|
37
|
-
const entry = entryToMatchVersion[0];
|
|
38
|
-
|
|
39
|
-
if (entry) {
|
|
40
|
-
resolvedResources.push(entry);
|
|
41
|
-
}
|
|
42
|
-
} catch (error) {
|
|
43
|
-
console.error(`Failed to fetch related resource: ${resource.id} of type ${resource.type}`, error);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const sectionList = resolvedResources.map((p: any) => ({
|
|
48
|
-
label: `${p.data.name}`,
|
|
49
|
-
tag: `v${p.data.version}`,
|
|
50
|
-
collection: p.collection,
|
|
51
|
-
href: buildUrl(`/docs/${p.collection}/${p.data.id}/${p.data.version}`),
|
|
52
|
-
}));
|
|
53
|
-
---
|
|
54
|
-
|
|
55
|
-
<PillListFlat title={`${title} (${sectionList.length})`} pills={sectionList} color="orange" limit={limit} client:load />
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/react';
|
|
2
|
-
import { ChevronDownIcon } from '@heroicons/react/20/solid';
|
|
3
|
-
import { getIconForProtocol } from '@utils/protocols';
|
|
4
|
-
|
|
5
|
-
import './PillListFlat.styles.css';
|
|
6
|
-
|
|
7
|
-
interface Props {
|
|
8
|
-
title: string;
|
|
9
|
-
color: string;
|
|
10
|
-
icon?: any;
|
|
11
|
-
pills: {
|
|
12
|
-
label: string;
|
|
13
|
-
badge?: string;
|
|
14
|
-
href?: string;
|
|
15
|
-
tag?: string;
|
|
16
|
-
color?: string;
|
|
17
|
-
collection?: string;
|
|
18
|
-
description?: string;
|
|
19
|
-
icon?: string;
|
|
20
|
-
}[];
|
|
21
|
-
emptyMessage?: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const ProtocolList = ({ title, pills, emptyMessage, color = 'gray', ...props }: Props) => {
|
|
25
|
-
return (
|
|
26
|
-
<div>
|
|
27
|
-
<div className="mx-auto w-full max-w-lg divide-y divide-white/5 rounded-xl bg-white/5">
|
|
28
|
-
<Disclosure as="div" className="pb-8" defaultOpen={pills.length <= 10}>
|
|
29
|
-
<DisclosureButton className="group flex w-full items-center justify-start space-x-4">
|
|
30
|
-
<span className="text-sm text-black font-semibold group-data-[hover]:text-black/80 capitalize"> {title} </span>
|
|
31
|
-
<ChevronDownIcon className="size-5 ml-2 fill-black/60 group-data-[hover]:fill-black/50 group-data-[open]:rotate-180" />
|
|
32
|
-
</DisclosureButton>
|
|
33
|
-
<DisclosurePanel className="mt-2 text-sm/5 text-black/50">
|
|
34
|
-
<ul role="list" className="space-y-2">
|
|
35
|
-
{pills.map((item, index) => {
|
|
36
|
-
const href = item.href ?? '#';
|
|
37
|
-
const Icon = item.icon ? getIconForProtocol(item.icon) : null;
|
|
38
|
-
|
|
39
|
-
return (
|
|
40
|
-
<li
|
|
41
|
-
className=" has-tooltip rounded-md text-gray-600 group px-1 w-full hover:bg-gradient-to-l hover:from-purple-500 hover:to-purple-700 hover:text-white hover:font-normal "
|
|
42
|
-
key={`${item.href}-${index}`}
|
|
43
|
-
>
|
|
44
|
-
<a className={`leading-3`} href={href}>
|
|
45
|
-
<span className="space-x-2 flex items-center">
|
|
46
|
-
{Icon && <Icon className={`h-4 w-4`} />}
|
|
47
|
-
<span className="font-light text-sm truncate">
|
|
48
|
-
{item.label} {item.tag && <>({item.tag})</>}
|
|
49
|
-
</span>
|
|
50
|
-
{item.label.length > 24 && (
|
|
51
|
-
<span className="tooltip rounded relative shadow-lg p-1 font-normal text-xs bg-white text-black ml-[30px] mt-12">
|
|
52
|
-
{item.label} ({item.tag})
|
|
53
|
-
</span>
|
|
54
|
-
)}
|
|
55
|
-
</span>
|
|
56
|
-
{item.description && <span className="text-[9px] block ml-6 mt-1 leading-0">{item.description}</span>}
|
|
57
|
-
</a>
|
|
58
|
-
</li>
|
|
59
|
-
);
|
|
60
|
-
})}
|
|
61
|
-
{pills.length === 0 && emptyMessage && (
|
|
62
|
-
<li className="inline mr-2 leading-tight text-xs">
|
|
63
|
-
<span className="text-gray-400">{emptyMessage}</span>
|
|
64
|
-
</li>
|
|
65
|
-
)}
|
|
66
|
-
</ul>
|
|
67
|
-
</DisclosurePanel>
|
|
68
|
-
</Disclosure>
|
|
69
|
-
</div>
|
|
70
|
-
</div>
|
|
71
|
-
);
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
export default ProtocolList;
|