@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,248 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import type { CollectionEntry } from 'astro:content';
|
|
3
|
-
import PillListFlat from '@components/Lists/PillListFlat';
|
|
4
|
-
import OwnersList from '@components/Lists/OwnersList';
|
|
5
|
-
import type { CollectionMessageTypes } from '@types';
|
|
6
|
-
import * as path from 'path';
|
|
7
|
-
import VersionList from '@components/Lists/VersionList.astro';
|
|
8
|
-
import { buildUrl } from '@utils/url-builder';
|
|
9
|
-
import { FileDownIcon, ScrollText, Workflow, RssIcon } from 'lucide-react';
|
|
10
|
-
import RepositoryList from '@components/Lists/RepositoryList.astro';
|
|
11
|
-
import { getOwner } from '@utils/collections/owners';
|
|
12
|
-
import CustomSideBarSectionList from '@components/Lists/CustomSideBarSectionList.astro';
|
|
13
|
-
import config from '@config';
|
|
14
|
-
import { getSchemaFormatFromURL } from '@utils/collections/schemas';
|
|
15
|
-
import { isChangelogEnabled } from '@utils/feature';
|
|
16
|
-
interface Props {
|
|
17
|
-
message: CollectionEntry<CollectionMessageTypes>;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const { message } = Astro.props;
|
|
21
|
-
|
|
22
|
-
const producers = (message.data.producers as CollectionEntry<'services'>[]) || [];
|
|
23
|
-
const consumers = (message.data.consumers as CollectionEntry<'services'>[]) || [];
|
|
24
|
-
const channels = (message.data.messageChannels as CollectionEntry<'channels'>[]) || [];
|
|
25
|
-
|
|
26
|
-
const ownersRaw = message.data?.owners || [];
|
|
27
|
-
const owners = await Promise.all<ReturnType<typeof getOwner>>(ownersRaw.map(getOwner));
|
|
28
|
-
const filteredOwners = owners.filter((o) => o !== undefined);
|
|
29
|
-
|
|
30
|
-
const resourceGroups = message.data?.resourceGroups || [];
|
|
31
|
-
|
|
32
|
-
const attachments = message.data.attachments || [];
|
|
33
|
-
|
|
34
|
-
const attachmentsList = attachments.map((a) => {
|
|
35
|
-
const attachmentIsURL = typeof a === 'string';
|
|
36
|
-
return {
|
|
37
|
-
label: attachmentIsURL ? a : (a.title ?? a.url),
|
|
38
|
-
href: attachmentIsURL ? a : a.url,
|
|
39
|
-
icon: attachmentIsURL ? 'ExternalLinkIcon' : (a.icon ?? 'ExternalLinkIcon'),
|
|
40
|
-
target: '_blank' as const,
|
|
41
|
-
subgroup: attachmentIsURL ? undefined : (a.type ?? ''),
|
|
42
|
-
};
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
const producerList = producers.map((p) => ({
|
|
46
|
-
label: `${p.data.name}`,
|
|
47
|
-
tag: `v${p.data.version}`,
|
|
48
|
-
collection: p.collection,
|
|
49
|
-
href: buildUrl(`/docs/services/${p.data.id}/${p.data.version}`),
|
|
50
|
-
}));
|
|
51
|
-
|
|
52
|
-
const consumerList = consumers.map((p) => ({
|
|
53
|
-
label: `${p.data.name}`,
|
|
54
|
-
tag: `v${p.data.version}`,
|
|
55
|
-
collection: p.collection,
|
|
56
|
-
href: buildUrl(`/docs/services/${p.data.id}/${p.data.version}`),
|
|
57
|
-
}));
|
|
58
|
-
|
|
59
|
-
const ownersList = filteredOwners.map((o) => ({
|
|
60
|
-
label: o.data.name,
|
|
61
|
-
type: o.collection,
|
|
62
|
-
badge: o.collection === 'users' ? o.data.role : 'Team',
|
|
63
|
-
avatarUrl: o.collection === 'users' ? o.data.avatarUrl : '',
|
|
64
|
-
href: buildUrl(`/docs/${o.collection}/${o.data.id}`),
|
|
65
|
-
}));
|
|
66
|
-
|
|
67
|
-
const channelList = channels.map((c) => ({
|
|
68
|
-
label: `${c.data.name}`,
|
|
69
|
-
tag: `v${c.data.version}`,
|
|
70
|
-
collection: c.collection,
|
|
71
|
-
href: buildUrl(`/docs/channels/${c.data.id}/${c.data.version}`),
|
|
72
|
-
}));
|
|
73
|
-
|
|
74
|
-
const getType = (type: string) => {
|
|
75
|
-
switch (type) {
|
|
76
|
-
case 'queries':
|
|
77
|
-
return 'Query';
|
|
78
|
-
default:
|
|
79
|
-
return message.collection.slice(0, -1);
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const getProducerEmptyMessage = (type: string) => {
|
|
84
|
-
const value = type.toLowerCase();
|
|
85
|
-
switch (value) {
|
|
86
|
-
case 'query':
|
|
87
|
-
case 'command':
|
|
88
|
-
return `This ${value} does not get invoked by any services.`;
|
|
89
|
-
default:
|
|
90
|
-
return `This ${value} does not get produced by any services.`;
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
const getConsumerEmptyMessage = (type: string) => {
|
|
95
|
-
const value = type.toLowerCase();
|
|
96
|
-
switch (value) {
|
|
97
|
-
case 'query':
|
|
98
|
-
case 'command':
|
|
99
|
-
return `This ${value} does not invoke any service.`;
|
|
100
|
-
default:
|
|
101
|
-
return `This ${value} does not get consumed by any services.`;
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
const type = getType(message.collection);
|
|
106
|
-
|
|
107
|
-
const isRSSEnabled = config.rss?.enabled;
|
|
108
|
-
|
|
109
|
-
// @ts-ignore
|
|
110
|
-
const publicPath = message?.catalog?.publicPath;
|
|
111
|
-
|
|
112
|
-
const schemaFilePath = message?.data?.schemaPath;
|
|
113
|
-
const schemaURL = path.join(publicPath, schemaFilePath || '');
|
|
114
|
-
|
|
115
|
-
const shouldRenderSideBarSection = (section: string) => {
|
|
116
|
-
if (!message.data.detailsPanel) {
|
|
117
|
-
return true;
|
|
118
|
-
}
|
|
119
|
-
// @ts-ignore
|
|
120
|
-
return message.data.detailsPanel[section]?.visible ?? true;
|
|
121
|
-
};
|
|
122
|
-
---
|
|
123
|
-
|
|
124
|
-
<aside class="sticky top-28 left-0 space-y-8 h-full overflow-y-auto py-4">
|
|
125
|
-
<div class="">
|
|
126
|
-
{
|
|
127
|
-
resourceGroups
|
|
128
|
-
.filter((section) => section.items.length > 0 && section.sidebar)
|
|
129
|
-
.map((section) => <CustomSideBarSectionList section={section} />)
|
|
130
|
-
}
|
|
131
|
-
{
|
|
132
|
-
producerList.length > 0 && shouldRenderSideBarSection('producers') && (
|
|
133
|
-
<PillListFlat
|
|
134
|
-
color="pink"
|
|
135
|
-
title={`Producers (${producerList.length})`}
|
|
136
|
-
pills={producerList}
|
|
137
|
-
emptyMessage={getProducerEmptyMessage(type)}
|
|
138
|
-
client:load
|
|
139
|
-
/>
|
|
140
|
-
)
|
|
141
|
-
}
|
|
142
|
-
{
|
|
143
|
-
consumerList.length > 0 && shouldRenderSideBarSection('consumers') && (
|
|
144
|
-
<PillListFlat
|
|
145
|
-
color="pink"
|
|
146
|
-
title={`Consumers (${consumerList.length})`}
|
|
147
|
-
pills={consumerList}
|
|
148
|
-
emptyMessage={getConsumerEmptyMessage(type)}
|
|
149
|
-
client:load
|
|
150
|
-
/>
|
|
151
|
-
)
|
|
152
|
-
}
|
|
153
|
-
{
|
|
154
|
-
channelList.length > 0 && shouldRenderSideBarSection('channels') && (
|
|
155
|
-
<PillListFlat color="pink" title={`Channels (${channelList.length})`} pills={channelList} client:load />
|
|
156
|
-
)
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
{
|
|
160
|
-
message.data.versions && shouldRenderSideBarSection('versions') && (
|
|
161
|
-
<VersionList
|
|
162
|
-
title={`Versions (${message.data.versions?.length})`}
|
|
163
|
-
versions={message.data.versions}
|
|
164
|
-
collectionItem={message}
|
|
165
|
-
/>
|
|
166
|
-
)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
{
|
|
170
|
-
message.data.attachments && shouldRenderSideBarSection('attachments') && (
|
|
171
|
-
<PillListFlat
|
|
172
|
-
title={`Attachments (${attachmentsList.length})`}
|
|
173
|
-
pills={attachmentsList}
|
|
174
|
-
emptyMessage={`This ${type} does not have any attachments.`}
|
|
175
|
-
color="pink"
|
|
176
|
-
client:load
|
|
177
|
-
/>
|
|
178
|
-
)
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
{
|
|
182
|
-
ownersList.length > 0 && shouldRenderSideBarSection('owners') && (
|
|
183
|
-
<OwnersList
|
|
184
|
-
title={`Owners (${ownersList.length})`}
|
|
185
|
-
owners={ownersList}
|
|
186
|
-
emptyMessage={`This ${type} does not have any documented owners.`}
|
|
187
|
-
client:load
|
|
188
|
-
/>
|
|
189
|
-
)
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
{
|
|
193
|
-
message.data.repository && shouldRenderSideBarSection('repository') && (
|
|
194
|
-
<RepositoryList repository={message.data.repository?.url} language={message.data.repository?.language} />
|
|
195
|
-
)
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
{
|
|
199
|
-
isRSSEnabled && (
|
|
200
|
-
<div class="mx-auto pb-8 w-full max-w-lg divide-y divide-white/5 rounded-xl bg-white/5">
|
|
201
|
-
<span class="text-sm text-black font-semibold group-data-[hover]:text-black/80 capitalize">{type} RSS Feed</span>
|
|
202
|
-
<ul role="list" class="space-y-2 mt-2">
|
|
203
|
-
<li class="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 ">
|
|
204
|
-
<a class={`flex items-center space-x-2`} target="_blank" href={buildUrl(`/rss/${message.collection}/rss.xml`)}>
|
|
205
|
-
<RssIcon className="h-4 w-4 text-gray-800 group-hover:text-white" strokeWidth={1} />
|
|
206
|
-
<span class="font-light text-sm truncate">RSS Feed</span>
|
|
207
|
-
</a>
|
|
208
|
-
</li>
|
|
209
|
-
</ul>
|
|
210
|
-
</div>
|
|
211
|
-
)
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
<div class="space-y-2">
|
|
215
|
-
{
|
|
216
|
-
message?.data?.schemaPath && (
|
|
217
|
-
<a
|
|
218
|
-
href={buildUrl(schemaURL, true)}
|
|
219
|
-
download={`${message.data.name}(${message.data.version})-${schemaFilePath}`}
|
|
220
|
-
class="flex items-center justify-center space-x-2 text-center rounded-md w-full bg-white px-3.5 py-2.5 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-100/60 hover:text-primary"
|
|
221
|
-
>
|
|
222
|
-
<FileDownIcon strokeWidth={2} size={16} />
|
|
223
|
-
<span>Download {getSchemaFormatFromURL(schemaURL)?.toUpperCase()} schema</span>
|
|
224
|
-
</a>
|
|
225
|
-
)
|
|
226
|
-
}
|
|
227
|
-
<a
|
|
228
|
-
href={buildUrl(`/visualiser/${message.collection}/${message.data.id}/${message.data.version}`)}
|
|
229
|
-
class="flex items-center justify-center space-x-2 text-center rounded-md w-full bg-white px-3.5 py-2.5 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-100/60 hover:text-primary"
|
|
230
|
-
>
|
|
231
|
-
<Workflow strokeWidth={2} size={16} />
|
|
232
|
-
<span class="block">View in visualiser</span>
|
|
233
|
-
</a>
|
|
234
|
-
|
|
235
|
-
{
|
|
236
|
-
isChangelogEnabled() && shouldRenderSideBarSection('changelog') && (
|
|
237
|
-
<a
|
|
238
|
-
href={buildUrl(`/docs/${message.collection}/${message.data.id}/${message.data.latestVersion}/changelog`)}
|
|
239
|
-
class="flex items-center space-x-2 justify-center text-center rounded-md w-full bg-white px-3.5 py-2.5 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-100/60 hover:text-primary"
|
|
240
|
-
>
|
|
241
|
-
<ScrollText strokeWidth={2} size={16} />
|
|
242
|
-
<span class="block">Read changelog</span>
|
|
243
|
-
</a>
|
|
244
|
-
)
|
|
245
|
-
}
|
|
246
|
-
</div>
|
|
247
|
-
</div>
|
|
248
|
-
</aside>
|
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import OwnersList from '@components/Lists/OwnersList';
|
|
3
|
-
import PillListFlat from '@components/Lists/PillListFlat';
|
|
4
|
-
import RepositoryList from '@components/Lists/RepositoryList.astro';
|
|
5
|
-
import SpecificationsList from '@components/Lists/SpecificationsList.astro';
|
|
6
|
-
import CustomSideBarSectionList from '@components/Lists/CustomSideBarSectionList.astro';
|
|
7
|
-
import VersionList from '@components/Lists/VersionList.astro';
|
|
8
|
-
import { buildUrl } from '@utils/url-builder';
|
|
9
|
-
import { getOwner } from '@utils/collections/owners';
|
|
10
|
-
import type { CollectionEntry } from 'astro:content';
|
|
11
|
-
import { ScrollText, Workflow, FileDownIcon, Code, Link, RssIcon } from 'lucide-react';
|
|
12
|
-
import { join } from 'node:path';
|
|
13
|
-
import config from '@config';
|
|
14
|
-
import { getDomainsForService } from '@utils/collections/domains';
|
|
15
|
-
import { isChangelogEnabled } from '@utils/feature';
|
|
16
|
-
interface Props {
|
|
17
|
-
service: CollectionEntry<'services'>;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const { service } = Astro.props;
|
|
21
|
-
|
|
22
|
-
// @ts-ignore
|
|
23
|
-
const sends = (service.data.sends as CollectionEntry<'events'>[]) || [];
|
|
24
|
-
// @ts-ignore
|
|
25
|
-
const receives = (service.data.receives as CollectionEntry<'events'>[]) || [];
|
|
26
|
-
|
|
27
|
-
// @ts-ignore
|
|
28
|
-
const writesTo = (service.data.writesTo as CollectionEntry<'containers'>[]) || [];
|
|
29
|
-
// @ts-ignore
|
|
30
|
-
const readsFrom = (service.data.readsFrom as CollectionEntry<'containers'>[]) || [];
|
|
31
|
-
|
|
32
|
-
// @ts-ignore
|
|
33
|
-
const entities = (service.data.entities as CollectionEntry<'entities'>[]) || [];
|
|
34
|
-
|
|
35
|
-
const ownersRaw = service.data?.owners || [];
|
|
36
|
-
const owners = await Promise.all<ReturnType<typeof getOwner>>(ownersRaw.map(getOwner));
|
|
37
|
-
const filteredOwners = owners.filter((o) => o !== undefined);
|
|
38
|
-
|
|
39
|
-
const resourceGroups = service.data?.resourceGroups || [];
|
|
40
|
-
|
|
41
|
-
const domainsServiceBelongsTo = await getDomainsForService(service);
|
|
42
|
-
|
|
43
|
-
const attachments = service.data.attachments || [];
|
|
44
|
-
|
|
45
|
-
const attachmentsList = attachments.map((a) => {
|
|
46
|
-
const attachmentIsURL = typeof a === 'string';
|
|
47
|
-
return {
|
|
48
|
-
label: attachmentIsURL ? a : (a.title ?? a.url),
|
|
49
|
-
href: attachmentIsURL ? a : a.url,
|
|
50
|
-
icon: attachmentIsURL ? 'ExternalLinkIcon' : (a.icon ?? 'ExternalLinkIcon'),
|
|
51
|
-
target: '_blank' as const,
|
|
52
|
-
subgroup: attachmentIsURL ? undefined : (a.type ?? ''),
|
|
53
|
-
};
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const sendsList = sends
|
|
57
|
-
.sort((a, b) => a.collection.localeCompare(b.collection))
|
|
58
|
-
.map((p) => ({
|
|
59
|
-
label: p.data.name,
|
|
60
|
-
badge: p.collection,
|
|
61
|
-
color: p.collection === 'events' ? 'orange' : 'blue',
|
|
62
|
-
collection: p.collection,
|
|
63
|
-
tag: `v${p.data.version}`,
|
|
64
|
-
href: buildUrl(`/docs/${p.collection}/${p.data.id}/${p.data.version}`),
|
|
65
|
-
}));
|
|
66
|
-
|
|
67
|
-
const receivesList = receives
|
|
68
|
-
.sort((a, b) => a.collection.localeCompare(b.collection))
|
|
69
|
-
.map((p) => ({
|
|
70
|
-
label: p.data.name,
|
|
71
|
-
badge: p.collection,
|
|
72
|
-
color: p.collection === 'events' ? 'orange' : 'blue',
|
|
73
|
-
tag: `v${p.data.version}`,
|
|
74
|
-
collection: p.collection,
|
|
75
|
-
href: buildUrl(`/docs/${p.collection}/${p.data.id}/${p.data.version}`),
|
|
76
|
-
}));
|
|
77
|
-
|
|
78
|
-
const writesToList = writesTo.map((p) => ({
|
|
79
|
-
label: p.data.name,
|
|
80
|
-
badge: p.collection,
|
|
81
|
-
color: p.collection === 'containers' ? 'green' : 'blue',
|
|
82
|
-
collection: p.collection,
|
|
83
|
-
tag: `v${p.data.version}`,
|
|
84
|
-
href: buildUrl(`/docs/${p.collection}/${p.data.id}/${p.data.version}`),
|
|
85
|
-
}));
|
|
86
|
-
|
|
87
|
-
const readsFromList = readsFrom.map((p) => ({
|
|
88
|
-
label: p.data.name,
|
|
89
|
-
badge: p.collection,
|
|
90
|
-
color: p.collection === 'containers' ? 'green' : 'blue',
|
|
91
|
-
collection: p.collection,
|
|
92
|
-
tag: `v${p.data.version}`,
|
|
93
|
-
href: buildUrl(`/docs/${p.collection}/${p.data.id}/${p.data.version}`),
|
|
94
|
-
}));
|
|
95
|
-
|
|
96
|
-
const ownersList = filteredOwners.map((o) => ({
|
|
97
|
-
label: o.data.name,
|
|
98
|
-
type: o.collection,
|
|
99
|
-
badge: o.collection === 'users' ? o.data.role : 'Team',
|
|
100
|
-
avatarUrl: o.collection === 'users' ? o.data.avatarUrl : '',
|
|
101
|
-
href: buildUrl(`/docs/${o.collection}/${o.data.id}`),
|
|
102
|
-
}));
|
|
103
|
-
|
|
104
|
-
const domainList = domainsServiceBelongsTo.map((d) => ({
|
|
105
|
-
label: d.data.name,
|
|
106
|
-
badge: d.collection,
|
|
107
|
-
tag: `v${d.data.version}`,
|
|
108
|
-
collection: d.collection,
|
|
109
|
-
href: buildUrl(`/docs/${d.collection}/${d.data.id}/${d.data.version}`),
|
|
110
|
-
}));
|
|
111
|
-
|
|
112
|
-
const entityList = entities.map((p) => ({
|
|
113
|
-
label: p.data.name,
|
|
114
|
-
badge: p.collection,
|
|
115
|
-
tag: `v${p.data.version}`,
|
|
116
|
-
collection: p.collection,
|
|
117
|
-
href: buildUrl(`/docs/${p.collection}/${p.data.id}/${p.data.version}`),
|
|
118
|
-
}));
|
|
119
|
-
|
|
120
|
-
const isRSSEnabled = config.rss?.enabled;
|
|
121
|
-
|
|
122
|
-
// @ts-ignore
|
|
123
|
-
const publicPath = service?.catalog?.publicPath;
|
|
124
|
-
const schemaFilePath = service?.data?.schemaPath;
|
|
125
|
-
const schemaURL = join(publicPath, schemaFilePath || '');
|
|
126
|
-
|
|
127
|
-
const shouldRenderSideBarSection = (section: string) => {
|
|
128
|
-
if (!service.data.detailsPanel) {
|
|
129
|
-
return true;
|
|
130
|
-
}
|
|
131
|
-
// @ts-ignore
|
|
132
|
-
return service.data.detailsPanel[section]?.visible ?? true;
|
|
133
|
-
};
|
|
134
|
-
---
|
|
135
|
-
|
|
136
|
-
<aside class="sticky top-28 left-0 h-full overflow-y-auto pr-6 py-4">
|
|
137
|
-
<div id="sidebar-cta-portal" class="">
|
|
138
|
-
{
|
|
139
|
-
resourceGroups
|
|
140
|
-
.filter((section) => section.items.length > 0 && section.sidebar)
|
|
141
|
-
.map((section) => <CustomSideBarSectionList section={section} />)
|
|
142
|
-
}
|
|
143
|
-
{
|
|
144
|
-
domainList.length > 0 && shouldRenderSideBarSection('domains') && (
|
|
145
|
-
<PillListFlat
|
|
146
|
-
title={`Domains (${domainList.length})`}
|
|
147
|
-
pills={domainList}
|
|
148
|
-
emptyMessage={`This service does not belong to any domains.`}
|
|
149
|
-
color="orange"
|
|
150
|
-
client:load
|
|
151
|
-
/>
|
|
152
|
-
)
|
|
153
|
-
}
|
|
154
|
-
{
|
|
155
|
-
shouldRenderSideBarSection('messages') && (
|
|
156
|
-
<>
|
|
157
|
-
<PillListFlat
|
|
158
|
-
title={`Receives Messages (${receivesList.length})`}
|
|
159
|
-
pills={receivesList}
|
|
160
|
-
emptyMessage={`This service does not receive any messages.`}
|
|
161
|
-
color="orange"
|
|
162
|
-
client:load
|
|
163
|
-
/>
|
|
164
|
-
<PillListFlat
|
|
165
|
-
title={`Sends Messages (${sendsList.length})`}
|
|
166
|
-
pills={sendsList}
|
|
167
|
-
emptyMessage={`This service does not send any messages.`}
|
|
168
|
-
color="orange"
|
|
169
|
-
client:load
|
|
170
|
-
/>
|
|
171
|
-
</>
|
|
172
|
-
)
|
|
173
|
-
}
|
|
174
|
-
<>
|
|
175
|
-
<PillListFlat
|
|
176
|
-
title={`Writes to (${writesToList.length})`}
|
|
177
|
-
pills={writesToList}
|
|
178
|
-
emptyMessage={`This service does not receive any messages.`}
|
|
179
|
-
color="orange"
|
|
180
|
-
client:load
|
|
181
|
-
/>
|
|
182
|
-
</>
|
|
183
|
-
<>
|
|
184
|
-
<PillListFlat
|
|
185
|
-
title={`Reads from (${readsFromList.length})`}
|
|
186
|
-
pills={readsFromList}
|
|
187
|
-
emptyMessage={`This service does not read from any containers.`}
|
|
188
|
-
color="orange"
|
|
189
|
-
client:load
|
|
190
|
-
/>
|
|
191
|
-
</>
|
|
192
|
-
{
|
|
193
|
-
service.data.versions && shouldRenderSideBarSection('versions') && (
|
|
194
|
-
<VersionList versions={service.data.versions} collectionItem={service} />
|
|
195
|
-
)
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
{
|
|
199
|
-
service.data.attachments && shouldRenderSideBarSection('attachments') && (
|
|
200
|
-
<PillListFlat
|
|
201
|
-
title={`Attachments (${attachmentsList.length})`}
|
|
202
|
-
pills={attachmentsList}
|
|
203
|
-
emptyMessage={`This service does not have any attachments.`}
|
|
204
|
-
color="pink"
|
|
205
|
-
client:load
|
|
206
|
-
/>
|
|
207
|
-
)
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
{
|
|
211
|
-
service.data.specifications && shouldRenderSideBarSection('specifications') && (
|
|
212
|
-
<SpecificationsList collectionItem={service} />
|
|
213
|
-
)
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
{
|
|
217
|
-
entities.length > 0 && shouldRenderSideBarSection('entities') && (
|
|
218
|
-
<PillListFlat
|
|
219
|
-
title={`Entities (${entities.length})`}
|
|
220
|
-
pills={entityList}
|
|
221
|
-
emptyMessage={`This service does not contain any entities.`}
|
|
222
|
-
color="pink"
|
|
223
|
-
client:load
|
|
224
|
-
/>
|
|
225
|
-
)
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
{
|
|
229
|
-
shouldRenderSideBarSection('owners') && (
|
|
230
|
-
<OwnersList
|
|
231
|
-
title={`Owners (${ownersList.length})`}
|
|
232
|
-
owners={ownersList}
|
|
233
|
-
emptyMessage={`This service does not have any documented owners.`}
|
|
234
|
-
client:load
|
|
235
|
-
/>
|
|
236
|
-
)
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
{
|
|
240
|
-
service.data.repository && shouldRenderSideBarSection('repository') && (
|
|
241
|
-
<RepositoryList repository={service.data.repository?.url} language={service.data.repository?.language} />
|
|
242
|
-
)
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
{
|
|
246
|
-
isRSSEnabled && (
|
|
247
|
-
<div class="mx-auto pb-4 w-full max-w-lg divide-y divide-white/5 rounded-xl bg-white/5 border-b border-gray-100 mb-4">
|
|
248
|
-
<span class="text-sm text-black font-semibold group-data-[hover]:text-black/80 capitalize">Services RSS Feed</span>
|
|
249
|
-
<ul role="list" class="space-y-2 mt-2">
|
|
250
|
-
<li class="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 ">
|
|
251
|
-
<a class={`flex items-center space-x-2`} target="_blank" href={buildUrl(`/rss/services/rss.xml`)}>
|
|
252
|
-
<RssIcon className="h-4 w-4 text-gray-800 group-hover:text-white" strokeWidth={1} />
|
|
253
|
-
<span class="font-light text-sm truncate">RSS</span>
|
|
254
|
-
</a>
|
|
255
|
-
</li>
|
|
256
|
-
</ul>
|
|
257
|
-
</div>
|
|
258
|
-
)
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
<div class="space-y-2">
|
|
262
|
-
{
|
|
263
|
-
service?.data?.schemaPath && (
|
|
264
|
-
<a
|
|
265
|
-
href={buildUrl(schemaURL, true)}
|
|
266
|
-
download={`${service.data.name}(${service.data.version})-${schemaFilePath}`}
|
|
267
|
-
class="flex items-center justify-center space-x-2 text-center rounded-md w-full bg-white px-3.5 py-2.5 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-100/60 hover:text-primary"
|
|
268
|
-
>
|
|
269
|
-
<FileDownIcon strokeWidth={2} size={16} />
|
|
270
|
-
<span>Download schema</span>
|
|
271
|
-
</a>
|
|
272
|
-
)
|
|
273
|
-
}
|
|
274
|
-
<a
|
|
275
|
-
href={buildUrl(`/visualiser/${service.collection}/${service.data.id}/${service.data.version}`)}
|
|
276
|
-
class="flex items-center justify-center space-x-2 text-center rounded-md w-full bg-white px-3.5 py-2.5 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-100/60 hover:text-primary"
|
|
277
|
-
>
|
|
278
|
-
<Workflow strokeWidth={2} size={16} />
|
|
279
|
-
<span class="block">View in visualiser</span>
|
|
280
|
-
</a>
|
|
281
|
-
{
|
|
282
|
-
isChangelogEnabled() && shouldRenderSideBarSection('changelog') && (
|
|
283
|
-
<a
|
|
284
|
-
href={buildUrl(`/docs/${service.collection}/${service.data.id}/${service.data.latestVersion}/changelog`)}
|
|
285
|
-
class="flex items-center space-x-2 justify-center text-center rounded-md w-full bg-white px-3.5 py-2.5 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-100/60 hover:text-primary"
|
|
286
|
-
>
|
|
287
|
-
<ScrollText strokeWidth={2} size={16} />
|
|
288
|
-
<span class="block">Read changelog</span>
|
|
289
|
-
</a>
|
|
290
|
-
)
|
|
291
|
-
}
|
|
292
|
-
</div>
|
|
293
|
-
</div>
|
|
294
|
-
</aside>
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { ChevronDownIcon } from '@heroicons/react/24/outline';
|
|
3
|
-
|
|
4
|
-
interface CollapsibleGroupProps {
|
|
5
|
-
isCollapsed: boolean;
|
|
6
|
-
onToggle: () => void;
|
|
7
|
-
title: React.ReactNode;
|
|
8
|
-
children: React.ReactNode;
|
|
9
|
-
className?: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const CollapsibleGroup: React.FC<CollapsibleGroupProps> = ({ isCollapsed, onToggle, title, children, className = '' }) => (
|
|
13
|
-
<div className={className}>
|
|
14
|
-
<div className="flex items-center">
|
|
15
|
-
<button
|
|
16
|
-
onClick={(e) => {
|
|
17
|
-
e.stopPropagation();
|
|
18
|
-
onToggle();
|
|
19
|
-
}}
|
|
20
|
-
className="p-1 hover:bg-gray-100 rounded-md"
|
|
21
|
-
>
|
|
22
|
-
<div className={`transition-transform duration-150 ${isCollapsed ? '' : 'rotate-180'}`}>
|
|
23
|
-
<ChevronDownIcon className="h-3 w-3 text-gray-500" />
|
|
24
|
-
</div>
|
|
25
|
-
</button>
|
|
26
|
-
{typeof title === 'string' ? (
|
|
27
|
-
<button
|
|
28
|
-
onClick={(e) => {
|
|
29
|
-
e.stopPropagation();
|
|
30
|
-
onToggle();
|
|
31
|
-
}}
|
|
32
|
-
className="flex-grow flex items-center justify-between px-2 py-0.5 text-xs font-bold rounded-md"
|
|
33
|
-
>
|
|
34
|
-
{title}
|
|
35
|
-
</button>
|
|
36
|
-
) : (
|
|
37
|
-
title
|
|
38
|
-
)}
|
|
39
|
-
</div>
|
|
40
|
-
<div className={`overflow-hidden transition-[height] duration-150 ease-out ${isCollapsed ? 'h-0' : 'h-auto'}`}>
|
|
41
|
-
{children}
|
|
42
|
-
</div>
|
|
43
|
-
</div>
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
export default CollapsibleGroup;
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { getMessageColorByCollection, getMessageCollectionName } from '../index';
|
|
3
|
-
|
|
4
|
-
interface MessageListProps {
|
|
5
|
-
messages: any[];
|
|
6
|
-
decodedCurrentPath: string;
|
|
7
|
-
searchTerm?: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const HighlightedText = React.memo(({ text, searchTerm }: { text: string; searchTerm?: string }) => {
|
|
11
|
-
if (!searchTerm) return <>{text}</>;
|
|
12
|
-
|
|
13
|
-
const regex = new RegExp(`(${searchTerm})`, 'gi');
|
|
14
|
-
const parts = text.split(regex);
|
|
15
|
-
|
|
16
|
-
return (
|
|
17
|
-
<>
|
|
18
|
-
{parts.map((part, index) =>
|
|
19
|
-
regex.test(part) ? (
|
|
20
|
-
<span key={index} className="bg-yellow-200 text-gray-900 font-semibold">
|
|
21
|
-
{part}
|
|
22
|
-
</span>
|
|
23
|
-
) : (
|
|
24
|
-
<span key={index}>{part}</span>
|
|
25
|
-
)
|
|
26
|
-
)}
|
|
27
|
-
</>
|
|
28
|
-
);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
const getMessageColorByLabelOrCollection = (collection: string, badge?: string) => {
|
|
32
|
-
if (!badge) {
|
|
33
|
-
return getMessageColorByCollection(collection);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Will try and match the label against HTTP verbs
|
|
37
|
-
const httpVerbs = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS', 'TRACE', 'CONNECT'];
|
|
38
|
-
if (badge && httpVerbs.includes(badge.toUpperCase())) {
|
|
39
|
-
if (badge.toUpperCase() === 'GET') return 'bg-blue-50 text-blue-600';
|
|
40
|
-
if (badge.toUpperCase() === 'POST') return 'bg-green-50 text-green-600';
|
|
41
|
-
if (badge.toUpperCase() === 'PUT') return 'bg-yellow-50 text-yellow-600';
|
|
42
|
-
if (badge.toUpperCase() === 'DELETE') return 'bg-red-50 text-red-600';
|
|
43
|
-
if (badge.toUpperCase() === 'PATCH') return 'bg-purple-50 text-purple-600';
|
|
44
|
-
if (badge.toUpperCase() === 'HEAD') return 'bg-gray-50 text-gray-600';
|
|
45
|
-
if (badge.toUpperCase() === 'OPTIONS') return 'bg-orange-50 text-orange-600';
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return getMessageColorByCollection(collection);
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const MessageList: React.FC<MessageListProps> = ({ messages, decodedCurrentPath, searchTerm }) => (
|
|
52
|
-
<ul className="space-y-0.5 border-l border-gray-200/80 ml-[9px] pl-4">
|
|
53
|
-
{messages.map((message: any) => (
|
|
54
|
-
<li key={message.id} data-active={decodedCurrentPath === message.href}>
|
|
55
|
-
<a
|
|
56
|
-
href={message.href}
|
|
57
|
-
data-active={decodedCurrentPath === message.href}
|
|
58
|
-
className={`flex items-center justify-between px-2 py-1.5 text-xs text-gray-600 hover:bg-purple-100 rounded-md ${
|
|
59
|
-
decodedCurrentPath.includes(message.href) ? 'bg-purple-100 ' : 'hover:bg-purple-100'
|
|
60
|
-
}`}
|
|
61
|
-
title={message.data?.sidebar?.label || message.data.name}
|
|
62
|
-
>
|
|
63
|
-
<span className="truncate">
|
|
64
|
-
<HighlightedText text={message.data?.sidebar?.label || message.data.name} searchTerm={searchTerm} />
|
|
65
|
-
<span className="text-xs text-gray-400">{message.data.draft ? ' (DRAFT)' : ''}</span>
|
|
66
|
-
</span>
|
|
67
|
-
<span
|
|
68
|
-
className={`ml-2 text-[10px] flex items-center gap-1 font-medium px-2 uppercase py-0.5 rounded ${getMessageColorByLabelOrCollection(message.collection, message.data?.sidebar?.badge)} ${message.data?.sidebar?.backgroundColor}`}
|
|
69
|
-
>
|
|
70
|
-
{message.data?.sidebar?.badge || getMessageCollectionName(message.collection, message)}
|
|
71
|
-
</span>
|
|
72
|
-
</a>
|
|
73
|
-
</li>
|
|
74
|
-
))}
|
|
75
|
-
</ul>
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
export default MessageList;
|