@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,1250 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect, useRef, useMemo, useCallback } from 'react';
|
|
2
|
-
import { ChevronDownIcon, ChevronDoubleDownIcon, ChevronDoubleUpIcon, XMarkIcon } from '@heroicons/react/24/outline';
|
|
3
|
-
import { buildUrl, buildUrlWithParams } from '@utils/url-builder';
|
|
4
|
-
import CollapsibleGroup from './components/CollapsibleGroup';
|
|
5
|
-
import MessageList from './components/MessageList';
|
|
6
|
-
import SpecificationsList from './components/SpecificationList';
|
|
7
|
-
import type { MessageItem, ServiceItem, ListViewSideBarProps, DomainItem, FlowItem, Resources } from './types';
|
|
8
|
-
import { PanelLeft } from 'lucide-react';
|
|
9
|
-
const STORAGE_KEY = 'EventCatalog:catalogSidebarCollapsedGroups';
|
|
10
|
-
const DEBOUNCE_DELAY = 300; // 300ms debounce delay
|
|
11
|
-
|
|
12
|
-
const HighlightedText = React.memo(({ text, searchTerm }: { text: string; searchTerm: string }) => {
|
|
13
|
-
if (!searchTerm) return <>{text}</>;
|
|
14
|
-
|
|
15
|
-
const regex = new RegExp(`(${searchTerm})`, 'gi');
|
|
16
|
-
const parts = text.split(regex);
|
|
17
|
-
|
|
18
|
-
return (
|
|
19
|
-
<>
|
|
20
|
-
{parts.map((part, index) =>
|
|
21
|
-
regex.test(part) ? (
|
|
22
|
-
<span key={index} className="bg-yellow-200 text-gray-900 font-semibold">
|
|
23
|
-
{part}
|
|
24
|
-
</span>
|
|
25
|
-
) : (
|
|
26
|
-
<span key={index}>{part}</span>
|
|
27
|
-
)
|
|
28
|
-
)}
|
|
29
|
-
</>
|
|
30
|
-
);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
export const getMessageColorByCollection = (collection: string) => {
|
|
34
|
-
if (collection === 'commands') return 'bg-blue-50 text-blue-600';
|
|
35
|
-
if (collection === 'queries') return 'bg-green-50 text-green-600';
|
|
36
|
-
if (collection === 'events') return 'bg-orange-50 text-orange-600';
|
|
37
|
-
if (collection === 'entities') return 'bg-purple-50 text-purple-600';
|
|
38
|
-
return 'text-gray-600';
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
export const getMessageCollectionName = (collection: string, item: any) => {
|
|
42
|
-
if (collection === 'commands') return 'Command';
|
|
43
|
-
if (collection === 'queries') return 'Query';
|
|
44
|
-
if (collection === 'events') return 'Event';
|
|
45
|
-
if (collection === 'entities' && item.data.aggregateRoot) return 'Entity (Root)';
|
|
46
|
-
if (collection === 'entities') return 'Entity';
|
|
47
|
-
return collection.slice(0, collection.length - 1).toUpperCase();
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const NoResultsFound = React.memo(({ searchTerm }: { searchTerm: string }) => (
|
|
51
|
-
<div className="px-4 py-6 text-center">
|
|
52
|
-
<div className="text-gray-400 text-sm mb-2">No results found for "{searchTerm}"</div>
|
|
53
|
-
<div className="text-gray-400 text-xs">
|
|
54
|
-
Try:
|
|
55
|
-
<ul className="mt-2 space-y-1 text-left list-disc pl-4">
|
|
56
|
-
<li>Checking for typos</li>
|
|
57
|
-
<li>Using fewer keywords</li>
|
|
58
|
-
<li>Using more general terms</li>
|
|
59
|
-
</ul>
|
|
60
|
-
</div>
|
|
61
|
-
</div>
|
|
62
|
-
));
|
|
63
|
-
|
|
64
|
-
const ServiceItem = React.memo(
|
|
65
|
-
({
|
|
66
|
-
item,
|
|
67
|
-
decodedCurrentPath,
|
|
68
|
-
collapsedGroups,
|
|
69
|
-
toggleGroupCollapse,
|
|
70
|
-
isVisualizer,
|
|
71
|
-
searchTerm,
|
|
72
|
-
}: {
|
|
73
|
-
item: ServiceItem;
|
|
74
|
-
decodedCurrentPath: string;
|
|
75
|
-
collapsedGroups: { [key: string]: boolean };
|
|
76
|
-
toggleGroupCollapse: (group: string) => void;
|
|
77
|
-
isVisualizer: boolean;
|
|
78
|
-
searchTerm: string;
|
|
79
|
-
}) => {
|
|
80
|
-
const readsAndWritesTo = item.writesTo.filter((writeTo) => item.readsFrom.some((readFrom) => readFrom.id === writeTo.id));
|
|
81
|
-
const resourceReads = item.readsFrom.filter((readFrom) => !readsAndWritesTo.some((writeTo) => writeTo.id === readFrom.id));
|
|
82
|
-
const resourceWrites = item.writesTo.filter((writeTo) => !readsAndWritesTo.some((readFrom) => readFrom.id === writeTo.id));
|
|
83
|
-
const hasData = item.writesTo.length > 0 || item.readsFrom.length > 0;
|
|
84
|
-
|
|
85
|
-
const sendsMessages = item.sends && item.sends.length > 0;
|
|
86
|
-
const receivesMessages = item.receives && item.receives.length > 0;
|
|
87
|
-
|
|
88
|
-
return (
|
|
89
|
-
<CollapsibleGroup
|
|
90
|
-
isCollapsed={collapsedGroups[item.href]}
|
|
91
|
-
onToggle={() => toggleGroupCollapse(item.href)}
|
|
92
|
-
title={
|
|
93
|
-
<button
|
|
94
|
-
onClick={(e) => {
|
|
95
|
-
e.stopPropagation();
|
|
96
|
-
toggleGroupCollapse(item.href);
|
|
97
|
-
}}
|
|
98
|
-
className="flex justify-between items-center pl-2 w-full text-xs"
|
|
99
|
-
title={item.label}
|
|
100
|
-
>
|
|
101
|
-
<span className="truncate text-xs font-bold">
|
|
102
|
-
<HighlightedText text={item.label} searchTerm={searchTerm} />
|
|
103
|
-
<span className="text-xs text-gray-400">{item.draft ? ' (DRAFT)' : ''}</span>
|
|
104
|
-
</span>
|
|
105
|
-
<span
|
|
106
|
-
style={{
|
|
107
|
-
color: item.sidebar?.color || '#9333ea',
|
|
108
|
-
backgroundColor: item.sidebar?.backgroundColor || '#faf5ff',
|
|
109
|
-
}}
|
|
110
|
-
className="ml-2 rounded bg-purple-50 px-2 py-0.5 text-[10px] font-medium text-purple-600"
|
|
111
|
-
>
|
|
112
|
-
{item.sidebar?.badge ? item.sidebar.badge?.toUpperCase() : 'SERVICE'}
|
|
113
|
-
</span>
|
|
114
|
-
</button>
|
|
115
|
-
}
|
|
116
|
-
>
|
|
117
|
-
<div className="space-y-0.5 border-gray-200/80 border-l pl-3 ml-[9px] mt-1">
|
|
118
|
-
<a
|
|
119
|
-
href={`${item.href}`}
|
|
120
|
-
data-active={decodedCurrentPath === item.href}
|
|
121
|
-
className={`flex items-center px-2 py-1.5 text-xs text-gray-600 hover:bg-purple-100 rounded-md ${
|
|
122
|
-
decodedCurrentPath === item.href ? 'bg-purple-100' : 'hover:bg-purple-100'
|
|
123
|
-
}`}
|
|
124
|
-
>
|
|
125
|
-
<span className="truncate">Overview</span>
|
|
126
|
-
</a>
|
|
127
|
-
{isVisualizer && hasData && (
|
|
128
|
-
<a
|
|
129
|
-
href={buildUrl(`/${item.href}/data`)}
|
|
130
|
-
data-active={decodedCurrentPath === `${item.href}/data`}
|
|
131
|
-
className={`flex items-center px-2 py-1.5 text-xs text-gray-600 hover:bg-purple-100 rounded-md ${
|
|
132
|
-
decodedCurrentPath === `${item.href}/data` ? 'bg-purple-100 ' : 'hover:bg-purple-100'
|
|
133
|
-
}`}
|
|
134
|
-
>
|
|
135
|
-
<span className="truncate">Data Diagram</span>
|
|
136
|
-
</a>
|
|
137
|
-
)}
|
|
138
|
-
{!isVisualizer && (
|
|
139
|
-
<a
|
|
140
|
-
href={buildUrlWithParams('/architecture/docs/messages', {
|
|
141
|
-
serviceName: item.name,
|
|
142
|
-
serviceId: item.id,
|
|
143
|
-
})}
|
|
144
|
-
data-active={window.location.href.includes(`serviceId=${item.id}`)}
|
|
145
|
-
className={`flex items-center px-2 py-1.5 text-xs text-gray-600 hover:bg-purple-100 rounded-md ${
|
|
146
|
-
window.location.href.includes(`serviceId=${item.id}`) ? 'bg-purple-100' : 'hover:bg-purple-100'
|
|
147
|
-
}`}
|
|
148
|
-
>
|
|
149
|
-
<span className="truncate flex items-center gap-1">Architecture</span>
|
|
150
|
-
</a>
|
|
151
|
-
)}
|
|
152
|
-
|
|
153
|
-
{!isVisualizer && item.specifications && item.specifications.length > 0 && (
|
|
154
|
-
<CollapsibleGroup
|
|
155
|
-
isCollapsed={collapsedGroups[`${item.href}-specifications`]}
|
|
156
|
-
onToggle={() => toggleGroupCollapse(`${item.href}-specifications`)}
|
|
157
|
-
title={
|
|
158
|
-
<button
|
|
159
|
-
onClick={(e) => {
|
|
160
|
-
e.stopPropagation();
|
|
161
|
-
toggleGroupCollapse(`${item.href}-specifications`);
|
|
162
|
-
}}
|
|
163
|
-
className="truncate underline ml-2 text-xs mb-1 py-1"
|
|
164
|
-
>
|
|
165
|
-
Specifications ({item.specifications?.length})
|
|
166
|
-
</button>
|
|
167
|
-
}
|
|
168
|
-
>
|
|
169
|
-
<SpecificationsList specifications={item.specifications} id={item.id} version={item.version} />
|
|
170
|
-
</CollapsibleGroup>
|
|
171
|
-
)}
|
|
172
|
-
|
|
173
|
-
{receivesMessages && (
|
|
174
|
-
<CollapsibleGroup
|
|
175
|
-
isCollapsed={collapsedGroups[`${item.href}-receives`]}
|
|
176
|
-
onToggle={() => toggleGroupCollapse(`${item.href}-receives`)}
|
|
177
|
-
title={
|
|
178
|
-
<button
|
|
179
|
-
onClick={(e) => {
|
|
180
|
-
e.stopPropagation();
|
|
181
|
-
toggleGroupCollapse(`${item.href}-receives`);
|
|
182
|
-
}}
|
|
183
|
-
className="truncate underline ml-2 text-xs mb-1 py-1"
|
|
184
|
-
>
|
|
185
|
-
Receives messages ({item.receives.length})
|
|
186
|
-
</button>
|
|
187
|
-
}
|
|
188
|
-
>
|
|
189
|
-
<MessageList messages={item.receives} decodedCurrentPath={decodedCurrentPath} searchTerm={searchTerm} />
|
|
190
|
-
</CollapsibleGroup>
|
|
191
|
-
)}
|
|
192
|
-
{sendsMessages && (
|
|
193
|
-
<CollapsibleGroup
|
|
194
|
-
isCollapsed={collapsedGroups[`${item.href}-sends`]}
|
|
195
|
-
onToggle={() => toggleGroupCollapse(`${item.href}-sends`)}
|
|
196
|
-
title={
|
|
197
|
-
<button
|
|
198
|
-
onClick={(e) => {
|
|
199
|
-
e.stopPropagation();
|
|
200
|
-
toggleGroupCollapse(`${item.href}-sends`);
|
|
201
|
-
}}
|
|
202
|
-
className="truncate underline ml-2 text-xs mb-1 py-1"
|
|
203
|
-
>
|
|
204
|
-
Sends messages ({item.sends.length})
|
|
205
|
-
</button>
|
|
206
|
-
}
|
|
207
|
-
>
|
|
208
|
-
<MessageList messages={item.sends} decodedCurrentPath={decodedCurrentPath} searchTerm={searchTerm} />
|
|
209
|
-
</CollapsibleGroup>
|
|
210
|
-
)}
|
|
211
|
-
{!isVisualizer && hasData && (
|
|
212
|
-
<CollapsibleGroup
|
|
213
|
-
isCollapsed={collapsedGroups[`${item.href}-data`]}
|
|
214
|
-
onToggle={() => toggleGroupCollapse(`${item.href}-data`)}
|
|
215
|
-
title={
|
|
216
|
-
<button
|
|
217
|
-
onClick={(e) => {
|
|
218
|
-
e.stopPropagation();
|
|
219
|
-
toggleGroupCollapse(`${item.href}-data`);
|
|
220
|
-
}}
|
|
221
|
-
className="truncate underline ml-2 text-xs mb-1 py-1"
|
|
222
|
-
>
|
|
223
|
-
Data Stores ({readsAndWritesTo.length + resourceWrites.length + resourceReads.length})
|
|
224
|
-
</button>
|
|
225
|
-
}
|
|
226
|
-
>
|
|
227
|
-
{readsAndWritesTo.length > 0 && (
|
|
228
|
-
<CollapsibleGroup
|
|
229
|
-
className="ml-4"
|
|
230
|
-
isCollapsed={collapsedGroups[`${item.href}-writesTo-data`]}
|
|
231
|
-
onToggle={() => toggleGroupCollapse(`${item.href}-writesTo-data`)}
|
|
232
|
-
title={
|
|
233
|
-
<button
|
|
234
|
-
onClick={(e) => {
|
|
235
|
-
e.stopPropagation();
|
|
236
|
-
toggleGroupCollapse(`${item.href}-writesTo-data`);
|
|
237
|
-
}}
|
|
238
|
-
className="truncate underline ml-2 text-xs mb-1 py-1"
|
|
239
|
-
>
|
|
240
|
-
Reads and writes to
|
|
241
|
-
</button>
|
|
242
|
-
}
|
|
243
|
-
>
|
|
244
|
-
<MessageList messages={readsAndWritesTo} decodedCurrentPath={decodedCurrentPath} searchTerm={searchTerm} />
|
|
245
|
-
</CollapsibleGroup>
|
|
246
|
-
)}
|
|
247
|
-
{resourceWrites.length > 0 && (
|
|
248
|
-
<CollapsibleGroup
|
|
249
|
-
className="ml-4"
|
|
250
|
-
isCollapsed={collapsedGroups[`${item.href}-writesTo-data`]}
|
|
251
|
-
onToggle={() => toggleGroupCollapse(`${item.href}-writesTo-data`)}
|
|
252
|
-
title={
|
|
253
|
-
<button
|
|
254
|
-
onClick={(e) => {
|
|
255
|
-
e.stopPropagation();
|
|
256
|
-
toggleGroupCollapse(`${item.href}-writesTo-data`);
|
|
257
|
-
}}
|
|
258
|
-
className="truncate underline ml-2 text-xs mb-1 py-1"
|
|
259
|
-
>
|
|
260
|
-
Writes to
|
|
261
|
-
</button>
|
|
262
|
-
}
|
|
263
|
-
>
|
|
264
|
-
<MessageList messages={resourceWrites} decodedCurrentPath={decodedCurrentPath} searchTerm={searchTerm} />
|
|
265
|
-
</CollapsibleGroup>
|
|
266
|
-
)}
|
|
267
|
-
{resourceReads.length > 0 && (
|
|
268
|
-
<CollapsibleGroup
|
|
269
|
-
className="ml-4"
|
|
270
|
-
isCollapsed={collapsedGroups[`${item.href}-readsFrom-data`]}
|
|
271
|
-
onToggle={() => toggleGroupCollapse(`${item.href}-readsFrom-data`)}
|
|
272
|
-
title={
|
|
273
|
-
<button
|
|
274
|
-
onClick={(e) => {
|
|
275
|
-
e.stopPropagation();
|
|
276
|
-
toggleGroupCollapse(`${item.href}-readsFrom-data`);
|
|
277
|
-
}}
|
|
278
|
-
className="truncate underline ml-2 text-xs mb-1 py-1"
|
|
279
|
-
>
|
|
280
|
-
Reads From
|
|
281
|
-
</button>
|
|
282
|
-
}
|
|
283
|
-
>
|
|
284
|
-
<MessageList messages={resourceReads} decodedCurrentPath={decodedCurrentPath} searchTerm={searchTerm} />
|
|
285
|
-
</CollapsibleGroup>
|
|
286
|
-
)}
|
|
287
|
-
</CollapsibleGroup>
|
|
288
|
-
)}
|
|
289
|
-
{!isVisualizer && item.entities.length > 0 && (
|
|
290
|
-
<CollapsibleGroup
|
|
291
|
-
isCollapsed={collapsedGroups[`${item.href}-entities`]}
|
|
292
|
-
onToggle={() => toggleGroupCollapse(`${item.href}-entities`)}
|
|
293
|
-
title={
|
|
294
|
-
<button
|
|
295
|
-
onClick={(e) => {
|
|
296
|
-
e.stopPropagation();
|
|
297
|
-
toggleGroupCollapse(`${item.href}-entities`);
|
|
298
|
-
}}
|
|
299
|
-
className="truncate underline ml-2 text-xs mb-1 py-1"
|
|
300
|
-
>
|
|
301
|
-
Entities ({item.entities.length})
|
|
302
|
-
</button>
|
|
303
|
-
}
|
|
304
|
-
>
|
|
305
|
-
<MessageList messages={item.entities} decodedCurrentPath={decodedCurrentPath} searchTerm={searchTerm} />
|
|
306
|
-
</CollapsibleGroup>
|
|
307
|
-
)}
|
|
308
|
-
</div>
|
|
309
|
-
</CollapsibleGroup>
|
|
310
|
-
);
|
|
311
|
-
}
|
|
312
|
-
);
|
|
313
|
-
|
|
314
|
-
const ListViewSideBar: React.FC<ListViewSideBarProps> = ({ resources, currentPath, showOrphanedMessages }) => {
|
|
315
|
-
const navRef = useRef<HTMLElement>(null);
|
|
316
|
-
const [data] = useState(resources);
|
|
317
|
-
const [searchTerm, setSearchTerm] = useState('');
|
|
318
|
-
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState('');
|
|
319
|
-
const [isInitialized, setIsInitialized] = useState(false);
|
|
320
|
-
const [isExpanded, setIsExpanded] = useState(true);
|
|
321
|
-
const [isSearchPinned, setIsSearchPinned] = useState(false);
|
|
322
|
-
const [lastScrollTop, setLastScrollTop] = useState(0);
|
|
323
|
-
const [collapsedGroups, setCollapsedGroups] = useState<{ [key: string]: boolean }>(() => {
|
|
324
|
-
if (typeof window !== 'undefined') {
|
|
325
|
-
const saved = window.localStorage.getItem(STORAGE_KEY);
|
|
326
|
-
const savedState = saved ? JSON.parse(saved) : {};
|
|
327
|
-
const currentPath = window.location.pathname;
|
|
328
|
-
|
|
329
|
-
// Default all sections to collapsed
|
|
330
|
-
const defaultCollapsedState: { [key: string]: boolean } = {
|
|
331
|
-
'all-services-group': true,
|
|
332
|
-
'flows-group': true,
|
|
333
|
-
'data-group': true,
|
|
334
|
-
'designs-group': true,
|
|
335
|
-
'messagesNotInService-group': true,
|
|
336
|
-
};
|
|
337
|
-
|
|
338
|
-
// Default all domains, services, and their subsections to collapsed
|
|
339
|
-
resources.domains?.forEach((domain: any) => {
|
|
340
|
-
const isDomainActive = currentPath.includes(domain.href);
|
|
341
|
-
defaultCollapsedState[domain.href] = !isDomainActive;
|
|
342
|
-
defaultCollapsedState[`${domain.href}-entities`] = true;
|
|
343
|
-
defaultCollapsedState[`${domain.href}-subdomains`] = true;
|
|
344
|
-
defaultCollapsedState[`${domain.href}-services`] = true;
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
resources.services?.forEach((service: any) => {
|
|
348
|
-
const isServiceActive = currentPath.includes(service.href);
|
|
349
|
-
defaultCollapsedState[service.href] = !isServiceActive;
|
|
350
|
-
defaultCollapsedState[`${service.href}-specifications`] = true;
|
|
351
|
-
defaultCollapsedState[`${service.href}-receives`] = true;
|
|
352
|
-
defaultCollapsedState[`${service.href}-sends`] = true;
|
|
353
|
-
defaultCollapsedState[`${service.href}-entities`] = true;
|
|
354
|
-
defaultCollapsedState[`${service.href}-data`] = true;
|
|
355
|
-
defaultCollapsedState[`${service.href}-writesTo-data`] = true;
|
|
356
|
-
defaultCollapsedState[`${service.href}-readsFrom-data`] = true;
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
setIsInitialized(true);
|
|
360
|
-
return { ...defaultCollapsedState, ...savedState };
|
|
361
|
-
}
|
|
362
|
-
return {};
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
const decodedCurrentPath = window.location.pathname;
|
|
366
|
-
const isVisualizer = window.location.pathname.includes('/visualiser/');
|
|
367
|
-
|
|
368
|
-
useEffect(() => {
|
|
369
|
-
const timer = setTimeout(() => {
|
|
370
|
-
setDebouncedSearchTerm(searchTerm.toLowerCase());
|
|
371
|
-
}, DEBOUNCE_DELAY);
|
|
372
|
-
|
|
373
|
-
return () => clearTimeout(timer);
|
|
374
|
-
}, [searchTerm]);
|
|
375
|
-
|
|
376
|
-
// Filter data based on search term
|
|
377
|
-
const filteredData: Resources = useMemo(() => {
|
|
378
|
-
if (!debouncedSearchTerm) return data;
|
|
379
|
-
|
|
380
|
-
const filterItem = (item: { label: string; id?: string }) => {
|
|
381
|
-
return (
|
|
382
|
-
item.label.toLowerCase().includes(debouncedSearchTerm) || (item.id && item.id.toLowerCase().includes(debouncedSearchTerm))
|
|
383
|
-
);
|
|
384
|
-
};
|
|
385
|
-
|
|
386
|
-
const filterMessages = (messages: MessageItem[]) => {
|
|
387
|
-
return messages.filter(
|
|
388
|
-
(message) =>
|
|
389
|
-
message.data.name.toLowerCase().includes(debouncedSearchTerm) || message.id.toLowerCase().includes(debouncedSearchTerm)
|
|
390
|
-
);
|
|
391
|
-
};
|
|
392
|
-
|
|
393
|
-
// Enhanced domain filtering that considers parent-subdomain relationships
|
|
394
|
-
const filterDomains = (domains: any[]) => {
|
|
395
|
-
const filteredDomains: any[] = [];
|
|
396
|
-
|
|
397
|
-
domains.forEach((domain: any) => {
|
|
398
|
-
const domainMatches = filterItem(domain);
|
|
399
|
-
|
|
400
|
-
// Check if this domain is a subdomain of another domain
|
|
401
|
-
const isSubdomain = domains.some((parentDomain: any) => {
|
|
402
|
-
const subdomains = parentDomain.domains || [];
|
|
403
|
-
return subdomains.some((subdomain: any) => subdomain.data.id === domain.id);
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
// If this is a parent domain, check if any of its subdomains match
|
|
407
|
-
let hasMatchingSubdomains = false;
|
|
408
|
-
if (!isSubdomain) {
|
|
409
|
-
const subdomains = domain.domains || [];
|
|
410
|
-
hasMatchingSubdomains = domains.some((potentialSubdomain: any) =>
|
|
411
|
-
subdomains.some((subdomain: any) => subdomain.data.id === potentialSubdomain.id && filterItem(potentialSubdomain))
|
|
412
|
-
);
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
// Include domain if:
|
|
416
|
-
// 1. The domain itself matches the search
|
|
417
|
-
// 2. It's a parent domain and has matching subdomains
|
|
418
|
-
// 3. It's a subdomain and matches the search
|
|
419
|
-
if (domainMatches || hasMatchingSubdomains || (isSubdomain && domainMatches)) {
|
|
420
|
-
filteredDomains.push(domain);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// If this is a subdomain that matches, also include its parent domain
|
|
424
|
-
if (isSubdomain && domainMatches) {
|
|
425
|
-
const parentDomain = domains.find((parentDomain: any) => {
|
|
426
|
-
const subdomains = parentDomain.domains || [];
|
|
427
|
-
return subdomains.some((subdomain: any) => subdomain.data.id === domain.id);
|
|
428
|
-
});
|
|
429
|
-
|
|
430
|
-
if (parentDomain && !filteredDomains.some((d: any) => d.id === parentDomain.id)) {
|
|
431
|
-
filteredDomains.push(parentDomain);
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
return filteredDomains;
|
|
437
|
-
};
|
|
438
|
-
|
|
439
|
-
return {
|
|
440
|
-
'context-map': data['context-map']?.filter(filterItem) || [],
|
|
441
|
-
domains: data.domains ? filterDomains(data.domains) : [],
|
|
442
|
-
services:
|
|
443
|
-
data.services
|
|
444
|
-
?.map((service: ServiceItem) => ({
|
|
445
|
-
...service,
|
|
446
|
-
sends: filterMessages(service.sends),
|
|
447
|
-
receives: filterMessages(service.receives),
|
|
448
|
-
isVisible:
|
|
449
|
-
filterItem(service) ||
|
|
450
|
-
service.sends.some(
|
|
451
|
-
(msg: MessageItem) =>
|
|
452
|
-
msg.data.name.toLowerCase().includes(debouncedSearchTerm) || msg.id.toLowerCase().includes(debouncedSearchTerm)
|
|
453
|
-
) ||
|
|
454
|
-
service.receives.some(
|
|
455
|
-
(msg: MessageItem) =>
|
|
456
|
-
msg.data.name.toLowerCase().includes(debouncedSearchTerm) || msg.id.toLowerCase().includes(debouncedSearchTerm)
|
|
457
|
-
),
|
|
458
|
-
}))
|
|
459
|
-
.filter((service: ServiceItem & { isVisible: boolean }) => service.isVisible) || [],
|
|
460
|
-
flows: data.flows?.filter(filterItem) || [],
|
|
461
|
-
designs: data.designs?.filter(filterItem) || [],
|
|
462
|
-
messagesNotInService:
|
|
463
|
-
data.messagesNotInService?.filter(
|
|
464
|
-
(msg: MessageItem) =>
|
|
465
|
-
msg.label.toLowerCase().includes(debouncedSearchTerm) || msg.id.toLowerCase().includes(debouncedSearchTerm)
|
|
466
|
-
) || [],
|
|
467
|
-
};
|
|
468
|
-
}, [data, debouncedSearchTerm]);
|
|
469
|
-
|
|
470
|
-
// Auto-expand groups when searching
|
|
471
|
-
useEffect(() => {
|
|
472
|
-
if (debouncedSearchTerm) {
|
|
473
|
-
// Expand all groups when searching
|
|
474
|
-
const newCollapsedState = { ...collapsedGroups };
|
|
475
|
-
Object.keys(newCollapsedState).forEach((key) => {
|
|
476
|
-
newCollapsedState[key] = false;
|
|
477
|
-
});
|
|
478
|
-
setCollapsedGroups(newCollapsedState);
|
|
479
|
-
}
|
|
480
|
-
}, [debouncedSearchTerm]);
|
|
481
|
-
|
|
482
|
-
// Handle scroll for sticky search bar
|
|
483
|
-
useEffect(() => {
|
|
484
|
-
const nav = navRef.current;
|
|
485
|
-
if (!nav) return;
|
|
486
|
-
|
|
487
|
-
const handleScroll = () => {
|
|
488
|
-
const scrollTop = nav.scrollTop;
|
|
489
|
-
const scrollThreshold = 50; // Pin after scrolling 50px
|
|
490
|
-
|
|
491
|
-
// Scrolling down past threshold
|
|
492
|
-
if (scrollTop > scrollThreshold && scrollTop > lastScrollTop) {
|
|
493
|
-
setIsSearchPinned(true);
|
|
494
|
-
}
|
|
495
|
-
// Scrolling up near the top
|
|
496
|
-
else if (scrollTop <= scrollThreshold) {
|
|
497
|
-
setIsSearchPinned(false);
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
setLastScrollTop(scrollTop);
|
|
501
|
-
};
|
|
502
|
-
|
|
503
|
-
nav.addEventListener('scroll', handleScroll);
|
|
504
|
-
return () => nav.removeEventListener('scroll', handleScroll);
|
|
505
|
-
}, [lastScrollTop]);
|
|
506
|
-
|
|
507
|
-
// Store collapsed groups in local storage
|
|
508
|
-
useEffect(() => {
|
|
509
|
-
if (typeof window !== 'undefined') {
|
|
510
|
-
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(collapsedGroups));
|
|
511
|
-
}
|
|
512
|
-
}, [collapsedGroups]);
|
|
513
|
-
|
|
514
|
-
// If we find a data-active element, scroll to it on mount and open its section
|
|
515
|
-
useEffect(() => {
|
|
516
|
-
const activeElement = document.querySelector('[data-active="true"]');
|
|
517
|
-
if (activeElement) {
|
|
518
|
-
// Add y offset to the scroll position
|
|
519
|
-
activeElement.scrollIntoView({ behavior: 'instant', block: 'center' });
|
|
520
|
-
|
|
521
|
-
// Check which section the active element belongs to and open it
|
|
522
|
-
const newCollapsedState = { ...collapsedGroups };
|
|
523
|
-
|
|
524
|
-
// Check if active page is in a domain
|
|
525
|
-
data.domains?.forEach((domain: any) => {
|
|
526
|
-
if (decodedCurrentPath.includes(domain.href)) {
|
|
527
|
-
newCollapsedState[domain.href] = false;
|
|
528
|
-
|
|
529
|
-
// Check if it's in domain entities
|
|
530
|
-
const isInDomainEntities = domain.entities?.some((entity: any) => decodedCurrentPath.includes(entity.href));
|
|
531
|
-
if (isInDomainEntities) {
|
|
532
|
-
newCollapsedState[`${domain.href}-entities`] = false;
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
// Check if it's in domain services
|
|
536
|
-
const isInDomainServices = domain.services?.some((service: any) => decodedCurrentPath.includes(service.data.id));
|
|
537
|
-
if (isInDomainServices) {
|
|
538
|
-
newCollapsedState[`${domain.href}-services`] = false;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
// Check if it's in a subdomain
|
|
542
|
-
const subdomains = domain.domains || [];
|
|
543
|
-
subdomains.forEach((subdomain: any) => {
|
|
544
|
-
const actualSubdomain = data.domains?.find((d: any) => d.id === subdomain.data.id);
|
|
545
|
-
if (actualSubdomain && decodedCurrentPath.includes(actualSubdomain.href)) {
|
|
546
|
-
newCollapsedState[`${domain.href}-subdomains`] = false;
|
|
547
|
-
newCollapsedState[actualSubdomain.href] = false;
|
|
548
|
-
|
|
549
|
-
// Check subdomain entities
|
|
550
|
-
const isInSubdomainEntities = actualSubdomain.entities?.some((entity: any) =>
|
|
551
|
-
decodedCurrentPath.includes(entity.href)
|
|
552
|
-
);
|
|
553
|
-
if (isInSubdomainEntities) {
|
|
554
|
-
newCollapsedState[`${actualSubdomain.href}-entities`] = false;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
// Check subdomain services
|
|
558
|
-
const isInSubdomainServices = actualSubdomain.services?.some((service: any) =>
|
|
559
|
-
decodedCurrentPath.includes(service.data.id)
|
|
560
|
-
);
|
|
561
|
-
if (isInSubdomainServices) {
|
|
562
|
-
newCollapsedState[`${actualSubdomain.href}-services`] = false;
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
});
|
|
566
|
-
}
|
|
567
|
-
});
|
|
568
|
-
|
|
569
|
-
// Check if active page is a service
|
|
570
|
-
data.services?.forEach((service: ServiceItem) => {
|
|
571
|
-
if (decodedCurrentPath.includes(service.href)) {
|
|
572
|
-
newCollapsedState['all-services-group'] = false;
|
|
573
|
-
newCollapsedState[service.href] = false;
|
|
574
|
-
|
|
575
|
-
// Open specific service sections if active
|
|
576
|
-
if (decodedCurrentPath.includes('/data')) {
|
|
577
|
-
newCollapsedState[`${service.href}-data`] = false;
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
});
|
|
581
|
-
|
|
582
|
-
// Check if active page is a flow
|
|
583
|
-
const isFlow = data.flows?.some((flow: FlowItem) => decodedCurrentPath === flow.href);
|
|
584
|
-
if (isFlow) {
|
|
585
|
-
newCollapsedState['flows-group'] = false;
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
// Check if active page is a container/data store
|
|
589
|
-
const isContainer = data.containers?.some((container: any) => decodedCurrentPath === container.href);
|
|
590
|
-
if (isContainer) {
|
|
591
|
-
newCollapsedState['data-group'] = false;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
// Check if active page is a design
|
|
595
|
-
const isDesign = data.designs?.some((design: any) => decodedCurrentPath === design.href);
|
|
596
|
-
if (isDesign) {
|
|
597
|
-
newCollapsedState['designs-group'] = false;
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
// Check if active page is an orphaned message
|
|
601
|
-
const isOrphanedMessage = data.messagesNotInService?.some((msg: MessageItem) => decodedCurrentPath === msg.href);
|
|
602
|
-
if (isOrphanedMessage) {
|
|
603
|
-
newCollapsedState['messagesNotInService-group'] = false;
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
setCollapsedGroups(newCollapsedState);
|
|
607
|
-
}
|
|
608
|
-
}, []);
|
|
609
|
-
|
|
610
|
-
const toggleGroupCollapse = useCallback((group: string) => {
|
|
611
|
-
setCollapsedGroups((prev) => ({
|
|
612
|
-
...prev,
|
|
613
|
-
[group]: !prev[group],
|
|
614
|
-
}));
|
|
615
|
-
}, []);
|
|
616
|
-
|
|
617
|
-
const handleSearchChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
|
618
|
-
setSearchTerm(e.target.value);
|
|
619
|
-
}, []);
|
|
620
|
-
|
|
621
|
-
const collapseAll = useCallback(() => {
|
|
622
|
-
const newCollapsedState: { [key: string]: boolean } = {};
|
|
623
|
-
|
|
624
|
-
// Collapse all domains
|
|
625
|
-
filteredData.domains?.forEach((domain: any) => {
|
|
626
|
-
newCollapsedState[domain.href] = true;
|
|
627
|
-
newCollapsedState[`${domain.href}-entities`] = true;
|
|
628
|
-
newCollapsedState[`${domain.href}-subdomains`] = true;
|
|
629
|
-
newCollapsedState[`${domain.href}-services`] = true;
|
|
630
|
-
});
|
|
631
|
-
|
|
632
|
-
// Collapse all services
|
|
633
|
-
filteredData.services?.forEach((service: any) => {
|
|
634
|
-
newCollapsedState[service.href] = true;
|
|
635
|
-
newCollapsedState[`${service.href}-specifications`] = true;
|
|
636
|
-
newCollapsedState[`${service.href}-receives`] = true;
|
|
637
|
-
newCollapsedState[`${service.href}-sends`] = true;
|
|
638
|
-
newCollapsedState[`${service.href}-entities`] = true;
|
|
639
|
-
});
|
|
640
|
-
|
|
641
|
-
setCollapsedGroups(newCollapsedState);
|
|
642
|
-
setIsExpanded(false);
|
|
643
|
-
}, [filteredData]);
|
|
644
|
-
|
|
645
|
-
const expandAll = useCallback(() => {
|
|
646
|
-
const newCollapsedState: { [key: string]: boolean } = {};
|
|
647
|
-
|
|
648
|
-
// Expand all domains
|
|
649
|
-
filteredData.domains?.forEach((domain: any) => {
|
|
650
|
-
newCollapsedState[domain.href] = false;
|
|
651
|
-
newCollapsedState[`${domain.href}-entities`] = false;
|
|
652
|
-
newCollapsedState[`${domain.href}-subdomains`] = false;
|
|
653
|
-
newCollapsedState[`${domain.href}-services`] = false;
|
|
654
|
-
});
|
|
655
|
-
|
|
656
|
-
// Expand all services
|
|
657
|
-
filteredData.services?.forEach((service: any) => {
|
|
658
|
-
newCollapsedState[service.href] = false;
|
|
659
|
-
newCollapsedState[`${service.href}-specifications`] = false;
|
|
660
|
-
newCollapsedState[`${service.href}-receives`] = false;
|
|
661
|
-
newCollapsedState[`${service.href}-sends`] = false;
|
|
662
|
-
newCollapsedState[`${service.href}-entities`] = false;
|
|
663
|
-
});
|
|
664
|
-
|
|
665
|
-
setCollapsedGroups(newCollapsedState);
|
|
666
|
-
setIsExpanded(true);
|
|
667
|
-
}, [filteredData]);
|
|
668
|
-
|
|
669
|
-
const toggleExpandCollapse = useCallback(() => {
|
|
670
|
-
if (isExpanded) {
|
|
671
|
-
collapseAll();
|
|
672
|
-
} else {
|
|
673
|
-
expandAll();
|
|
674
|
-
}
|
|
675
|
-
}, [isExpanded, collapseAll, expandAll]);
|
|
676
|
-
|
|
677
|
-
const hideSidebar = useCallback(() => {
|
|
678
|
-
// Dispatch custom event that the Astro layout will listen for
|
|
679
|
-
window.dispatchEvent(new CustomEvent('sidebarToggle', { detail: { action: 'hide' } }));
|
|
680
|
-
}, []);
|
|
681
|
-
|
|
682
|
-
const isDomainSubDomain = useMemo(() => {
|
|
683
|
-
return (domain: any) => {
|
|
684
|
-
const domains = data.domains || [];
|
|
685
|
-
return domains.some((d: any) => {
|
|
686
|
-
const subdomains = d.domains || [];
|
|
687
|
-
return subdomains.some((subdomain: any) => subdomain.data.id === domain.id);
|
|
688
|
-
});
|
|
689
|
-
};
|
|
690
|
-
}, [data.domains]);
|
|
691
|
-
|
|
692
|
-
// Helper function to get parent domains (domains that are not subdomains)
|
|
693
|
-
const getParentDomains = useMemo(() => {
|
|
694
|
-
return (domains: any[]) => {
|
|
695
|
-
return domains.filter((domain: any) => !isDomainSubDomain(domain));
|
|
696
|
-
};
|
|
697
|
-
}, [isDomainSubDomain]);
|
|
698
|
-
|
|
699
|
-
// Helper function to get subdomains for a specific parent domain
|
|
700
|
-
const getSubdomainsForParent = useMemo(() => {
|
|
701
|
-
return (parentDomain: any, allDomains: any[]) => {
|
|
702
|
-
const subdomains = parentDomain.domains || [];
|
|
703
|
-
return allDomains.filter((domain: any) => subdomains.some((subdomain: any) => subdomain.data.id === domain.id));
|
|
704
|
-
};
|
|
705
|
-
}, []);
|
|
706
|
-
|
|
707
|
-
// Helper function to get services for a specific domain (only direct services, not from subdomains)
|
|
708
|
-
const getServicesForDomain = useMemo(() => {
|
|
709
|
-
return (domain: any, allServices: ServiceItem[], allDomains: any[]) => {
|
|
710
|
-
const domainServices = domain.services || [];
|
|
711
|
-
const subdomains = getSubdomainsForParent(domain, allDomains);
|
|
712
|
-
|
|
713
|
-
// Get all service IDs from subdomains
|
|
714
|
-
const subdomainServiceIds = subdomains.flatMap((subdomain: any) => (subdomain.services || []).map((s: any) => s.data.id));
|
|
715
|
-
|
|
716
|
-
// Filter services that belong to this domain but NOT to any of its subdomains
|
|
717
|
-
return allServices.filter(
|
|
718
|
-
(service: ServiceItem) =>
|
|
719
|
-
domainServices.some((domainService: any) => domainService.data.id === service.id) &&
|
|
720
|
-
!subdomainServiceIds.includes(service.id)
|
|
721
|
-
);
|
|
722
|
-
};
|
|
723
|
-
}, [getSubdomainsForParent]);
|
|
724
|
-
|
|
725
|
-
// Component to render a single domain item
|
|
726
|
-
const DomainItem = React.memo(
|
|
727
|
-
({ item, isSubdomain = false, nestingLevel = 0 }: { item: any; isSubdomain?: boolean; nestingLevel?: number }) => {
|
|
728
|
-
const marginLeft = nestingLevel > 0 ? `ml-${nestingLevel * 4}` : '';
|
|
729
|
-
|
|
730
|
-
return (
|
|
731
|
-
<div className={`flex items-center ${marginLeft}`}>
|
|
732
|
-
<button
|
|
733
|
-
onClick={(e) => {
|
|
734
|
-
e.stopPropagation();
|
|
735
|
-
toggleGroupCollapse(item.href);
|
|
736
|
-
}}
|
|
737
|
-
className="p-1 hover:bg-gray-100 rounded-md"
|
|
738
|
-
title={item.label}
|
|
739
|
-
>
|
|
740
|
-
<div className={`transition-transform duration-150 ${collapsedGroups[item.href] ? '' : 'rotate-180'}`}>
|
|
741
|
-
<ChevronDownIcon className="h-3 w-3 text-gray-500" />
|
|
742
|
-
</div>
|
|
743
|
-
</button>
|
|
744
|
-
<button
|
|
745
|
-
onClick={(e) => {
|
|
746
|
-
e.stopPropagation();
|
|
747
|
-
toggleGroupCollapse(item.href);
|
|
748
|
-
}}
|
|
749
|
-
className={`flex-grow flex items-center justify-between px-2 py-0.5 text-xs font-bold rounded-md ${
|
|
750
|
-
decodedCurrentPath === item.href ? 'bg-purple-100' : 'hover:bg-purple-100'
|
|
751
|
-
}`}
|
|
752
|
-
title={item.label}
|
|
753
|
-
>
|
|
754
|
-
<span className="truncate">
|
|
755
|
-
<HighlightedText text={item.label} searchTerm={debouncedSearchTerm} />
|
|
756
|
-
</span>
|
|
757
|
-
<span className="text-yellow-600 ml-2 text-[10px] font-medium bg-yellow-50 px-2 py-0.5 rounded">
|
|
758
|
-
{isSubdomain ? 'SUBDOMAIN' : 'DOMAIN'}
|
|
759
|
-
</span>
|
|
760
|
-
</button>
|
|
761
|
-
</div>
|
|
762
|
-
);
|
|
763
|
-
}
|
|
764
|
-
);
|
|
765
|
-
|
|
766
|
-
// Component to render domain content (Overview, Architecture, etc.)
|
|
767
|
-
const DomainContent = React.memo(
|
|
768
|
-
({
|
|
769
|
-
item,
|
|
770
|
-
nestingLevel = 0,
|
|
771
|
-
className = '',
|
|
772
|
-
isSubdomain = false,
|
|
773
|
-
}: {
|
|
774
|
-
item: any;
|
|
775
|
-
nestingLevel?: number;
|
|
776
|
-
className?: string;
|
|
777
|
-
isSubdomain?: boolean;
|
|
778
|
-
}) => {
|
|
779
|
-
const marginLeft = nestingLevel > 0 ? `ml-${nestingLevel * 4}` : '';
|
|
780
|
-
const hasEntities = item.entities && item.entities.length > 0;
|
|
781
|
-
|
|
782
|
-
// Get services for this domain
|
|
783
|
-
const domainServices = getServicesForDomain(item, filteredData['services'] || [], filteredData['domains'] || []);
|
|
784
|
-
|
|
785
|
-
return (
|
|
786
|
-
<div
|
|
787
|
-
className={`overflow-hidden transition-[height] duration-150 ease-out ${collapsedGroups[item.href] ? 'h-0' : 'h-auto'} ${className}`}
|
|
788
|
-
>
|
|
789
|
-
<div className={`space-y-0.5 border-gray-200/80 border-l pl-4 mt-1 ${marginLeft ? marginLeft : 'ml-[9px]'}`}>
|
|
790
|
-
<a
|
|
791
|
-
href={`${item.href}`}
|
|
792
|
-
data-active={decodedCurrentPath === item.href}
|
|
793
|
-
className={`flex items-center px-2 py-1.5 text-xs text-gray-600 hover:bg-purple-100 rounded-md ${
|
|
794
|
-
decodedCurrentPath === item.href ? 'bg-purple-100 ' : 'hover:bg-purple-100'
|
|
795
|
-
}`}
|
|
796
|
-
title={`${item.label} - Overview`}
|
|
797
|
-
>
|
|
798
|
-
<span className="truncate">Overview</span>
|
|
799
|
-
</a>
|
|
800
|
-
|
|
801
|
-
{isVisualizer && hasEntities && (
|
|
802
|
-
<a
|
|
803
|
-
href={buildUrl(`/${item.href}/entity-map`)}
|
|
804
|
-
data-active={decodedCurrentPath === `${item.href}/entity-map`}
|
|
805
|
-
className={`flex items-center px-2 py-1.5 text-xs text-gray-600 hover:bg-purple-100 rounded-md ${
|
|
806
|
-
decodedCurrentPath === `${item.href}/entity-map` ? 'bg-purple-100 ' : 'hover:bg-purple-100'
|
|
807
|
-
}`}
|
|
808
|
-
>
|
|
809
|
-
<span className="truncate">Entity Map</span>
|
|
810
|
-
</a>
|
|
811
|
-
)}
|
|
812
|
-
{!isVisualizer && (
|
|
813
|
-
<a
|
|
814
|
-
href={buildUrlWithParams('/architecture/docs/services', {
|
|
815
|
-
serviceIds: item.services.map((service: any) => service.data.id).join(','),
|
|
816
|
-
domainId: item.id,
|
|
817
|
-
domainName: item.name,
|
|
818
|
-
})}
|
|
819
|
-
data-active={window.location.href.includes(`domainId=${item.id}`)}
|
|
820
|
-
className={`flex items-center px-2 py-1.5 text-xs text-gray-600 hover:bg-purple-100 rounded-md ${
|
|
821
|
-
window.location.href.includes(`domainId=${item.id}`) ? 'bg-purple-100 ' : 'hover:bg-purple-100'
|
|
822
|
-
}`}
|
|
823
|
-
>
|
|
824
|
-
<span className="truncate">Architecture</span>
|
|
825
|
-
</a>
|
|
826
|
-
)}
|
|
827
|
-
{!isVisualizer && (
|
|
828
|
-
<a
|
|
829
|
-
href={buildUrl(`/docs/domains/${item.id}/language`)}
|
|
830
|
-
data-active={decodedCurrentPath.includes(`/docs/domains/${item.id}/language`)}
|
|
831
|
-
className={`flex items-center px-2 py-1.5 text-xs text-gray-600 hover:bg-purple-100 rounded-md ${
|
|
832
|
-
decodedCurrentPath.includes(`/docs/domains/${item.id}/language`) ? 'bg-purple-100 ' : 'hover:bg-purple-100'
|
|
833
|
-
}`}
|
|
834
|
-
>
|
|
835
|
-
<span className="truncate">Ubiquitous Language</span>
|
|
836
|
-
</a>
|
|
837
|
-
)}
|
|
838
|
-
|
|
839
|
-
{/* Render services before entities */}
|
|
840
|
-
{domainServices.length > 0 && (
|
|
841
|
-
<CollapsibleGroup
|
|
842
|
-
isCollapsed={collapsedGroups[`${item.href}-services`]}
|
|
843
|
-
onToggle={() => toggleGroupCollapse(`${item.href}-services`)}
|
|
844
|
-
title={
|
|
845
|
-
<button
|
|
846
|
-
onClick={(e) => {
|
|
847
|
-
e.stopPropagation();
|
|
848
|
-
toggleGroupCollapse(`${item.href}-services`);
|
|
849
|
-
}}
|
|
850
|
-
className="truncate underline ml-2 text-xs mb-1 py-1"
|
|
851
|
-
>
|
|
852
|
-
Services ({domainServices.length})
|
|
853
|
-
</button>
|
|
854
|
-
}
|
|
855
|
-
>
|
|
856
|
-
<div className="space-y-2 pl-4">
|
|
857
|
-
{domainServices.map((service: ServiceItem) => (
|
|
858
|
-
<ServiceItem
|
|
859
|
-
key={service.href}
|
|
860
|
-
item={service}
|
|
861
|
-
decodedCurrentPath={decodedCurrentPath}
|
|
862
|
-
collapsedGroups={collapsedGroups}
|
|
863
|
-
toggleGroupCollapse={toggleGroupCollapse}
|
|
864
|
-
isVisualizer={isVisualizer}
|
|
865
|
-
searchTerm={debouncedSearchTerm}
|
|
866
|
-
/>
|
|
867
|
-
))}
|
|
868
|
-
</div>
|
|
869
|
-
</CollapsibleGroup>
|
|
870
|
-
)}
|
|
871
|
-
|
|
872
|
-
{item.entities.length > 0 && !isVisualizer && (
|
|
873
|
-
<CollapsibleGroup
|
|
874
|
-
isCollapsed={collapsedGroups[`${item.href}-entities`]}
|
|
875
|
-
onToggle={() => toggleGroupCollapse(`${item.href}-entities`)}
|
|
876
|
-
title={
|
|
877
|
-
<button
|
|
878
|
-
onClick={(e) => {
|
|
879
|
-
e.stopPropagation();
|
|
880
|
-
toggleGroupCollapse(`${item.href}-entities`);
|
|
881
|
-
}}
|
|
882
|
-
className="truncate underline ml-2 text-xs mb-1 py-1"
|
|
883
|
-
>
|
|
884
|
-
Entities ({item.entities.length})
|
|
885
|
-
</button>
|
|
886
|
-
}
|
|
887
|
-
>
|
|
888
|
-
<MessageList messages={item.entities} decodedCurrentPath={decodedCurrentPath} searchTerm={debouncedSearchTerm} />
|
|
889
|
-
</CollapsibleGroup>
|
|
890
|
-
)}
|
|
891
|
-
</div>
|
|
892
|
-
</div>
|
|
893
|
-
);
|
|
894
|
-
}
|
|
895
|
-
);
|
|
896
|
-
|
|
897
|
-
if (!isInitialized) return null;
|
|
898
|
-
|
|
899
|
-
const hasNoResults =
|
|
900
|
-
debouncedSearchTerm &&
|
|
901
|
-
!filteredData['context-map']?.length &&
|
|
902
|
-
!filteredData.domains?.length &&
|
|
903
|
-
!filteredData.services?.length &&
|
|
904
|
-
!filteredData.flows?.length &&
|
|
905
|
-
!filteredData.messagesNotInService?.length;
|
|
906
|
-
|
|
907
|
-
return (
|
|
908
|
-
<nav ref={navRef} className="space-y-4 text-gray-800 px-3 py-4 overflow-auto h-full">
|
|
909
|
-
<div
|
|
910
|
-
className={`flex gap-2 transition-all duration-200 ${
|
|
911
|
-
isSearchPinned ? 'sticky -top-5 z-10 bg-white shadow-md -mx-3 px-3 py-2 border-b border-gray-200' : ''
|
|
912
|
-
}`}
|
|
913
|
-
>
|
|
914
|
-
<input
|
|
915
|
-
type="text"
|
|
916
|
-
value={searchTerm}
|
|
917
|
-
onChange={handleSearchChange}
|
|
918
|
-
placeholder="Quick search..."
|
|
919
|
-
className="flex-1 p-2 text-sm rounded-md border border-gray-200 h-[30px]"
|
|
920
|
-
/>
|
|
921
|
-
<div className="flex gap-1">
|
|
922
|
-
<button
|
|
923
|
-
onClick={toggleExpandCollapse}
|
|
924
|
-
title={isExpanded ? 'Collapse All' : 'Expand All'}
|
|
925
|
-
className="px-2 py-1 bg-gray-100 hover:bg-gray-200 rounded-md border border-gray-200 h-[30px] flex items-center justify-center"
|
|
926
|
-
>
|
|
927
|
-
{isExpanded ? (
|
|
928
|
-
<ChevronDoubleUpIcon className="h-4 w-4 text-gray-600" />
|
|
929
|
-
) : (
|
|
930
|
-
<ChevronDoubleDownIcon className="h-4 w-4 text-gray-600" />
|
|
931
|
-
)}
|
|
932
|
-
</button>
|
|
933
|
-
<button
|
|
934
|
-
onClick={hideSidebar}
|
|
935
|
-
title="Hide Sidebar"
|
|
936
|
-
className="px-2 py-1 bg-gray-100 hover:bg-gray-200 rounded-md border border-gray-200 h-[30px] flex items-center justify-center"
|
|
937
|
-
>
|
|
938
|
-
<PanelLeft className="h-4 w-4 text-gray-600" />
|
|
939
|
-
</button>
|
|
940
|
-
</div>
|
|
941
|
-
</div>
|
|
942
|
-
<div className="space-y-2 divide-y divide-gray-200/80">
|
|
943
|
-
{hasNoResults ? (
|
|
944
|
-
<NoResultsFound searchTerm={debouncedSearchTerm} />
|
|
945
|
-
) : (
|
|
946
|
-
<>
|
|
947
|
-
{/* Bounded Context Map (Visualiser only) */}
|
|
948
|
-
{filteredData['context-map'] && filteredData.domains && filteredData.domains.length > 0 && (
|
|
949
|
-
<div className="pt-0">
|
|
950
|
-
<ul className="space-y-1">
|
|
951
|
-
{filteredData['context-map'].map((item: any) => (
|
|
952
|
-
<li key={item.href}>
|
|
953
|
-
<a
|
|
954
|
-
href={item.href}
|
|
955
|
-
data-active={decodedCurrentPath === item.href}
|
|
956
|
-
className={`flex items-center justify-between px-2 py-0.5 text-xs font-bold rounded-md ${
|
|
957
|
-
decodedCurrentPath === item.href ? 'bg-purple-100 text-purple-900' : 'hover:bg-purple-100'
|
|
958
|
-
}`}
|
|
959
|
-
>
|
|
960
|
-
<span className="truncate flex flex-col items-start">
|
|
961
|
-
<HighlightedText text={item.label} searchTerm={debouncedSearchTerm} />
|
|
962
|
-
<span className="text-[10px] text-gray-500 font-light">Explore integrations between domains</span>
|
|
963
|
-
</span>
|
|
964
|
-
<span className="text-blue-600 ml-2 text-[10px] font-medium bg-blue-50 px-2 py-0.5 rounded">DOMAINS</span>
|
|
965
|
-
</a>
|
|
966
|
-
</li>
|
|
967
|
-
))}
|
|
968
|
-
</ul>
|
|
969
|
-
</div>
|
|
970
|
-
)}
|
|
971
|
-
|
|
972
|
-
{/* Domains */}
|
|
973
|
-
{filteredData['domains'] && (
|
|
974
|
-
<div className={`${isVisualizer ? 'pt-4 pb-2' : 'p-0'}`}>
|
|
975
|
-
<ul className="space-y-2">
|
|
976
|
-
{getParentDomains(filteredData['domains'] || []).map((parentDomain: any) => {
|
|
977
|
-
const subdomains = getSubdomainsForParent(parentDomain, filteredData['domains'] || []);
|
|
978
|
-
|
|
979
|
-
return (
|
|
980
|
-
<li key={parentDomain.href} className="space-y-0" data-active={decodedCurrentPath === parentDomain.href}>
|
|
981
|
-
<DomainItem item={parentDomain} isSubdomain={false} />
|
|
982
|
-
<DomainContent item={parentDomain} />
|
|
983
|
-
|
|
984
|
-
{/* Render nested subdomains */}
|
|
985
|
-
{subdomains.length > 0 && !collapsedGroups[parentDomain.href] && (
|
|
986
|
-
<div className="space-y-0.5 border-gray-200/80 border-l pl-4 ml-[9px] mt-2">
|
|
987
|
-
<CollapsibleGroup
|
|
988
|
-
isCollapsed={collapsedGroups[`${parentDomain.href}-subdomains`]}
|
|
989
|
-
onToggle={() => toggleGroupCollapse(`${parentDomain.href}-subdomains`)}
|
|
990
|
-
title={
|
|
991
|
-
<button
|
|
992
|
-
onClick={(e) => {
|
|
993
|
-
e.stopPropagation();
|
|
994
|
-
toggleGroupCollapse(`${parentDomain.href}-subdomains`);
|
|
995
|
-
}}
|
|
996
|
-
className="truncate underline ml-2 text-xs mb-1 py-1"
|
|
997
|
-
>
|
|
998
|
-
Subdomains ({subdomains.length})
|
|
999
|
-
</button>
|
|
1000
|
-
}
|
|
1001
|
-
>
|
|
1002
|
-
<div className="space-y-2">
|
|
1003
|
-
{subdomains.map((subdomain: any) => (
|
|
1004
|
-
<div
|
|
1005
|
-
key={subdomain.href}
|
|
1006
|
-
className="space-y-0"
|
|
1007
|
-
data-active={decodedCurrentPath === subdomain.href}
|
|
1008
|
-
>
|
|
1009
|
-
<DomainItem item={subdomain} isSubdomain={true} nestingLevel={1} />
|
|
1010
|
-
<DomainContent item={subdomain} nestingLevel={3} className="ml-6" isSubdomain={true} />
|
|
1011
|
-
</div>
|
|
1012
|
-
))}
|
|
1013
|
-
</div>
|
|
1014
|
-
</CollapsibleGroup>
|
|
1015
|
-
</div>
|
|
1016
|
-
)}
|
|
1017
|
-
</li>
|
|
1018
|
-
);
|
|
1019
|
-
})}
|
|
1020
|
-
</ul>
|
|
1021
|
-
</div>
|
|
1022
|
-
)}
|
|
1023
|
-
|
|
1024
|
-
{/* All Services Group */}
|
|
1025
|
-
{filteredData['services'] && filteredData['services'].length > 0 && (
|
|
1026
|
-
<div className="pt-4 pb-2">
|
|
1027
|
-
<CollapsibleGroup
|
|
1028
|
-
isCollapsed={collapsedGroups['all-services-group']}
|
|
1029
|
-
onToggle={() => toggleGroupCollapse('all-services-group')}
|
|
1030
|
-
title={
|
|
1031
|
-
<button
|
|
1032
|
-
onClick={(e) => {
|
|
1033
|
-
e.stopPropagation();
|
|
1034
|
-
toggleGroupCollapse('all-services-group');
|
|
1035
|
-
}}
|
|
1036
|
-
className="flex justify-between items-center pl-2 w-full text-xs"
|
|
1037
|
-
>
|
|
1038
|
-
<span className="truncate text-xs font-bold">All Services ({filteredData['services'].length})</span>
|
|
1039
|
-
<span className="ml-2 rounded bg-purple-50 px-2 py-0.5 text-[10px] font-medium text-purple-600">
|
|
1040
|
-
SERVICES
|
|
1041
|
-
</span>
|
|
1042
|
-
</button>
|
|
1043
|
-
}
|
|
1044
|
-
>
|
|
1045
|
-
<div className="space-y-4 border-gray-200/80 border-l pl-3 ml-[9px] mt-3">
|
|
1046
|
-
{filteredData['services'].map((item: any) => {
|
|
1047
|
-
// Ensure service is collapsed by default if not in collapsedGroups
|
|
1048
|
-
if (collapsedGroups[item.href] === undefined) {
|
|
1049
|
-
collapsedGroups[item.href] = true;
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
return (
|
|
1053
|
-
<ServiceItem
|
|
1054
|
-
key={item.href}
|
|
1055
|
-
item={item}
|
|
1056
|
-
decodedCurrentPath={decodedCurrentPath}
|
|
1057
|
-
collapsedGroups={collapsedGroups}
|
|
1058
|
-
toggleGroupCollapse={toggleGroupCollapse}
|
|
1059
|
-
isVisualizer={isVisualizer}
|
|
1060
|
-
searchTerm={debouncedSearchTerm}
|
|
1061
|
-
/>
|
|
1062
|
-
);
|
|
1063
|
-
})}
|
|
1064
|
-
</div>
|
|
1065
|
-
</CollapsibleGroup>
|
|
1066
|
-
</div>
|
|
1067
|
-
)}
|
|
1068
|
-
|
|
1069
|
-
{/* Flows Group */}
|
|
1070
|
-
{filteredData['flows'] && filteredData['flows'].length > 0 && (
|
|
1071
|
-
<div className="pt-4 pb-2">
|
|
1072
|
-
<CollapsibleGroup
|
|
1073
|
-
isCollapsed={collapsedGroups['flows-group']}
|
|
1074
|
-
onToggle={() => toggleGroupCollapse('flows-group')}
|
|
1075
|
-
title={
|
|
1076
|
-
<button
|
|
1077
|
-
onClick={(e) => {
|
|
1078
|
-
e.stopPropagation();
|
|
1079
|
-
toggleGroupCollapse('flows-group');
|
|
1080
|
-
}}
|
|
1081
|
-
className="flex justify-between items-center pl-2 w-full text-xs"
|
|
1082
|
-
>
|
|
1083
|
-
<span className="truncate text-xs font-bold">All Flows ({filteredData['flows'].length})</span>
|
|
1084
|
-
<span className="ml-2 rounded bg-teal-50 px-2 py-0.5 text-[10px] font-medium text-teal-600">FLOWS</span>
|
|
1085
|
-
</button>
|
|
1086
|
-
}
|
|
1087
|
-
>
|
|
1088
|
-
<div className="space-y-2 border-gray-200/80 border-l pl-3 ml-[9px] mt-3">
|
|
1089
|
-
{filteredData['flows'].map((item: any) => (
|
|
1090
|
-
<div key={item.href} data-active={decodedCurrentPath === item.href}>
|
|
1091
|
-
<a
|
|
1092
|
-
href={item.href}
|
|
1093
|
-
data-active={decodedCurrentPath === item.href}
|
|
1094
|
-
className={`flex items-center justify-between px-2 py-0.5 text-xs font-thin rounded-md ${
|
|
1095
|
-
decodedCurrentPath === item.href ? 'bg-purple-100 text-purple-900' : 'hover:bg-purple-100'
|
|
1096
|
-
}`}
|
|
1097
|
-
title={item.label}
|
|
1098
|
-
>
|
|
1099
|
-
<span className="truncate">
|
|
1100
|
-
<HighlightedText text={item.label} searchTerm={debouncedSearchTerm} />
|
|
1101
|
-
</span>
|
|
1102
|
-
<span className={`ml-2 text-[10px] font-medium px-2 uppercase py-0.5 rounded bg-teal-50 text-teal-600`}>
|
|
1103
|
-
FLOW
|
|
1104
|
-
</span>
|
|
1105
|
-
</a>
|
|
1106
|
-
</div>
|
|
1107
|
-
))}
|
|
1108
|
-
</div>
|
|
1109
|
-
</CollapsibleGroup>
|
|
1110
|
-
</div>
|
|
1111
|
-
)}
|
|
1112
|
-
|
|
1113
|
-
{/* Data Group */}
|
|
1114
|
-
{filteredData['containers'] && filteredData['containers'].length > 0 && (
|
|
1115
|
-
<div className="pt-4 pb-2">
|
|
1116
|
-
<CollapsibleGroup
|
|
1117
|
-
isCollapsed={collapsedGroups['data-group']}
|
|
1118
|
-
onToggle={() => toggleGroupCollapse('data-group')}
|
|
1119
|
-
title={
|
|
1120
|
-
<button
|
|
1121
|
-
onClick={(e) => {
|
|
1122
|
-
e.stopPropagation();
|
|
1123
|
-
toggleGroupCollapse('data-group');
|
|
1124
|
-
}}
|
|
1125
|
-
className="flex justify-between items-center pl-2 w-full text-xs"
|
|
1126
|
-
>
|
|
1127
|
-
<span className="truncate text-xs font-bold">All Data Stores ({filteredData['containers'].length})</span>
|
|
1128
|
-
<span className="ml-2 rounded bg-blue-50 px-2 py-0.5 text-[10px] font-medium text-blue-600">DATA</span>
|
|
1129
|
-
</button>
|
|
1130
|
-
}
|
|
1131
|
-
>
|
|
1132
|
-
<div className="space-y-2 border-gray-200/80 border-l pl-3 ml-[9px] mt-3">
|
|
1133
|
-
{filteredData['containers'].map((item: any) => (
|
|
1134
|
-
<div key={item.href} data-active={decodedCurrentPath === item.href}>
|
|
1135
|
-
<a
|
|
1136
|
-
href={item.href}
|
|
1137
|
-
data-active={decodedCurrentPath === item.href}
|
|
1138
|
-
className={`flex items-center justify-between px-2 py-0.5 text-xs font-thin rounded-md ${
|
|
1139
|
-
decodedCurrentPath === item.href ? 'bg-purple-100 text-purple-900' : 'hover:bg-purple-100'
|
|
1140
|
-
}`}
|
|
1141
|
-
title={item.label}
|
|
1142
|
-
>
|
|
1143
|
-
<span className="truncate">
|
|
1144
|
-
<HighlightedText text={item.label} searchTerm={debouncedSearchTerm} />
|
|
1145
|
-
</span>
|
|
1146
|
-
<span className={`ml-2 text-[10px] font-medium px-2 uppercase py-0.5 rounded bg-blue-50 text-blue-600`}>
|
|
1147
|
-
DATA
|
|
1148
|
-
</span>
|
|
1149
|
-
</a>
|
|
1150
|
-
</div>
|
|
1151
|
-
))}
|
|
1152
|
-
</div>
|
|
1153
|
-
</CollapsibleGroup>
|
|
1154
|
-
</div>
|
|
1155
|
-
)}
|
|
1156
|
-
|
|
1157
|
-
{filteredData['designs'] && filteredData['designs'].length > 0 && (
|
|
1158
|
-
<div className="pt-4 pb-2">
|
|
1159
|
-
<CollapsibleGroup
|
|
1160
|
-
isCollapsed={collapsedGroups['designs-group']}
|
|
1161
|
-
onToggle={() => toggleGroupCollapse('designs-group')}
|
|
1162
|
-
title={
|
|
1163
|
-
<button
|
|
1164
|
-
onClick={(e) => {
|
|
1165
|
-
e.stopPropagation();
|
|
1166
|
-
toggleGroupCollapse('designs-group');
|
|
1167
|
-
}}
|
|
1168
|
-
className="flex justify-between items-center pl-2 w-full text-xs"
|
|
1169
|
-
>
|
|
1170
|
-
<span className="truncate text-xs font-bold">All Designs ({filteredData['designs'].length})</span>
|
|
1171
|
-
<span className="ml-2 rounded bg-teal-50 px-2 py-0.5 text-[10px] font-medium text-teal-600">DESIGNS</span>
|
|
1172
|
-
</button>
|
|
1173
|
-
}
|
|
1174
|
-
>
|
|
1175
|
-
<div className="space-y-2 border-gray-200/80 border-l pl-3 ml-[9px] mt-3">
|
|
1176
|
-
{filteredData['designs'].map((item: any) => (
|
|
1177
|
-
<div key={item.href} data-active={decodedCurrentPath === item.href}>
|
|
1178
|
-
<a
|
|
1179
|
-
href={item.href}
|
|
1180
|
-
data-active={decodedCurrentPath === item.href}
|
|
1181
|
-
className={`flex items-center justify-between px-2 py-0.5 text-xs font-thin rounded-md ${
|
|
1182
|
-
decodedCurrentPath === item.href ? 'bg-purple-100 text-purple-900' : 'hover:bg-purple-100'
|
|
1183
|
-
}`}
|
|
1184
|
-
title={item.label}
|
|
1185
|
-
>
|
|
1186
|
-
<span className="truncate">
|
|
1187
|
-
<HighlightedText text={item.label} searchTerm={debouncedSearchTerm} />
|
|
1188
|
-
</span>
|
|
1189
|
-
<span className={`ml-2 text-[10px] font-medium px-2 uppercase py-0.5 rounded bg-teal-50 text-teal-600`}>
|
|
1190
|
-
DESIGN
|
|
1191
|
-
</span>
|
|
1192
|
-
</a>
|
|
1193
|
-
</div>
|
|
1194
|
-
))}
|
|
1195
|
-
</div>
|
|
1196
|
-
</CollapsibleGroup>
|
|
1197
|
-
</div>
|
|
1198
|
-
)}
|
|
1199
|
-
|
|
1200
|
-
{filteredData['messagesNotInService'] && filteredData['messagesNotInService'].length > 0 && showOrphanedMessages && (
|
|
1201
|
-
<div className="pt-4 pb-2">
|
|
1202
|
-
<CollapsibleGroup
|
|
1203
|
-
isCollapsed={collapsedGroups['messagesNotInService-group']}
|
|
1204
|
-
onToggle={() => toggleGroupCollapse('messagesNotInService-group')}
|
|
1205
|
-
title={
|
|
1206
|
-
<button
|
|
1207
|
-
onClick={(e) => {
|
|
1208
|
-
e.stopPropagation();
|
|
1209
|
-
toggleGroupCollapse('messagesNotInService-group');
|
|
1210
|
-
}}
|
|
1211
|
-
className="flex justify-between items-center pl-2 w-full text-xs"
|
|
1212
|
-
>
|
|
1213
|
-
<span className="truncate text-xs font-bold">Orphaned Messages</span>
|
|
1214
|
-
</button>
|
|
1215
|
-
}
|
|
1216
|
-
>
|
|
1217
|
-
<div className="space-y-2 border-gray-200/80 border-l pl-3 ml-[9px] mt-3">
|
|
1218
|
-
{filteredData['messagesNotInService'].map((item: any) => (
|
|
1219
|
-
<div key={item.href} data-active={decodedCurrentPath === item.href}>
|
|
1220
|
-
<a
|
|
1221
|
-
href={item.href}
|
|
1222
|
-
data-active={decodedCurrentPath === item.href}
|
|
1223
|
-
className={`flex items-center justify-between px-2 py-0.5 text-xs font-thin rounded-md ${
|
|
1224
|
-
decodedCurrentPath === item.href ? 'bg-purple-100 text-purple-900' : 'hover:bg-purple-100'
|
|
1225
|
-
}`}
|
|
1226
|
-
title={item.label}
|
|
1227
|
-
>
|
|
1228
|
-
<span className="truncate">
|
|
1229
|
-
<HighlightedText text={item.label} searchTerm={debouncedSearchTerm} />
|
|
1230
|
-
</span>
|
|
1231
|
-
<span
|
|
1232
|
-
className={`ml-2 text-[10px] font-medium px-2 uppercase py-0.5 rounded ${getMessageColorByCollection(item.collection)}`}
|
|
1233
|
-
>
|
|
1234
|
-
{getMessageCollectionName(item.collection, item)}
|
|
1235
|
-
</span>
|
|
1236
|
-
</a>
|
|
1237
|
-
</div>
|
|
1238
|
-
))}
|
|
1239
|
-
</div>
|
|
1240
|
-
</CollapsibleGroup>
|
|
1241
|
-
</div>
|
|
1242
|
-
)}
|
|
1243
|
-
</>
|
|
1244
|
-
)}
|
|
1245
|
-
</div>
|
|
1246
|
-
</nav>
|
|
1247
|
-
);
|
|
1248
|
-
};
|
|
1249
|
-
|
|
1250
|
-
export default React.memo(ListViewSideBar);
|