@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,390 +1,414 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { memo, useMemo, useState } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
ServerIcon,
|
|
4
|
-
EnvelopeIcon,
|
|
5
4
|
RectangleGroupIcon,
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
BoltIcon,
|
|
6
|
+
ChatBubbleLeftIcon,
|
|
7
|
+
MagnifyingGlassIcon,
|
|
8
8
|
CircleStackIcon,
|
|
9
|
+
ChevronDownIcon,
|
|
10
|
+
ChevronUpIcon,
|
|
11
|
+
ArrowsPointingOutIcon,
|
|
9
12
|
} from '@heroicons/react/24/outline';
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
export interface ExtendedDomain extends CollectionEntry<'domains'> {
|
|
18
|
-
sends: CollectionEntry<CollectionMessageTypes>[];
|
|
19
|
-
receives: CollectionEntry<CollectionMessageTypes>[];
|
|
20
|
-
services: CollectionEntry<'services'>[];
|
|
21
|
-
domains: CollectionEntry<'domains'>[];
|
|
22
|
-
}
|
|
13
|
+
import { buildUrl } from '@utils/url-builder';
|
|
14
|
+
import { BoxIcon, ArrowRight, ArrowLeft } from 'lucide-react';
|
|
15
|
+
|
|
16
|
+
// ============================================
|
|
17
|
+
// Types
|
|
18
|
+
// ============================================
|
|
23
19
|
|
|
24
20
|
interface DomainGridProps {
|
|
25
|
-
|
|
26
|
-
embeded: boolean;
|
|
21
|
+
domain: any;
|
|
27
22
|
}
|
|
28
23
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
// Filter by search query
|
|
54
|
-
if (searchQuery) {
|
|
55
|
-
const query = searchQuery.toLowerCase();
|
|
56
|
-
result = result.filter(
|
|
57
|
-
(domain) =>
|
|
58
|
-
domain.data.name?.toLowerCase().includes(query) ||
|
|
59
|
-
domain.data.summary?.toLowerCase().includes(query) ||
|
|
60
|
-
domain.data.services?.some((service: any) => service.data.name.toLowerCase().includes(query)) ||
|
|
61
|
-
domain.sends?.some((message: any) => message.data.name.toLowerCase().includes(query)) ||
|
|
62
|
-
domain.receives?.some((message: any) => message.data.name.toLowerCase().includes(query))
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Sort by name by default
|
|
67
|
-
result.sort((a, b) => (a.data.name || a.data.id).localeCompare(b.data.name || b.data.id));
|
|
68
|
-
|
|
69
|
-
return result;
|
|
70
|
-
}, [domains, searchQuery]);
|
|
24
|
+
// ============================================
|
|
25
|
+
// Helper functions
|
|
26
|
+
// ============================================
|
|
27
|
+
|
|
28
|
+
const getMessageIcon = (collection: string) => {
|
|
29
|
+
switch (collection) {
|
|
30
|
+
case 'events':
|
|
31
|
+
return { Icon: BoltIcon, color: 'orange' };
|
|
32
|
+
case 'commands':
|
|
33
|
+
return { Icon: ChatBubbleLeftIcon, color: 'blue' };
|
|
34
|
+
case 'queries':
|
|
35
|
+
return { Icon: MagnifyingGlassIcon, color: 'green' };
|
|
36
|
+
default:
|
|
37
|
+
return { Icon: BoltIcon, color: 'gray' };
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// ============================================
|
|
42
|
+
// Simple Sub-components
|
|
43
|
+
// ============================================
|
|
44
|
+
|
|
45
|
+
const EntityBadge = memo(({ entity }: { entity: any }) => {
|
|
46
|
+
const id = entity?.data?.id || entity?.id;
|
|
47
|
+
const name = entity?.data?.name || entity?.name || id;
|
|
71
48
|
|
|
72
49
|
return (
|
|
73
|
-
<
|
|
74
|
-
{
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
50
|
+
<a
|
|
51
|
+
href={buildUrl(`/docs/entities/${id}`)}
|
|
52
|
+
className="inline-flex items-center gap-1.5 px-2.5 py-1 bg-purple-100 border border-purple-300 rounded-md text-xs font-medium hover:bg-purple-200 transition-colors"
|
|
53
|
+
>
|
|
54
|
+
<BoxIcon className="h-3.5 w-3.5 text-purple-600" />
|
|
55
|
+
<span className="text-purple-800">{name}</span>
|
|
56
|
+
</a>
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const MessageBadge = memo(({ message }: { message: any }) => {
|
|
61
|
+
const data = message?.data || message;
|
|
62
|
+
const collection = message?.collection || 'events';
|
|
63
|
+
const { Icon, color } = getMessageIcon(collection);
|
|
64
|
+
const id = data?.id || message?.id;
|
|
65
|
+
const name = data?.name || data?.id || id;
|
|
66
|
+
const version = data?.version;
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<a
|
|
70
|
+
href={buildUrl(`/docs/${collection}/${id}/${version}`)}
|
|
71
|
+
className="flex items-center gap-1.5 px-2 py-1 bg-white border border-gray-200 rounded text-[11px] font-medium hover:bg-gray-50 transition-colors"
|
|
72
|
+
>
|
|
73
|
+
<Icon className={`h-3 w-3 text-${color}-500`} />
|
|
74
|
+
<span className="text-gray-700 truncate max-w-[120px]">{name}</span>
|
|
75
|
+
</a>
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const ContainerBadge = memo(({ container, type }: { container: any; type: 'reads' | 'writes' }) => {
|
|
80
|
+
const data = container?.data || container;
|
|
81
|
+
const id = data?.id || container?.id;
|
|
82
|
+
const name = data?.name || id;
|
|
83
|
+
const version = data?.version;
|
|
84
|
+
const colorClass = type === 'reads' ? 'orange' : 'purple';
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<a
|
|
88
|
+
href={buildUrl(`/docs/containers/${id}/${version}`)}
|
|
89
|
+
className={`inline-flex items-center gap-1.5 px-2 py-1 bg-${colorClass}-100 border border-${colorClass}-300 rounded text-[11px] font-medium hover:bg-${colorClass}-200 transition-colors`}
|
|
90
|
+
>
|
|
91
|
+
<CircleStackIcon className={`h-3 w-3 text-${colorClass}-600`} />
|
|
92
|
+
<span className={`text-${colorClass}-800`}>{name}</span>
|
|
93
|
+
</a>
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const ServiceCard = memo(({ service }: { service: any }) => {
|
|
98
|
+
const data = service?.data || service;
|
|
99
|
+
if (!data?.id) return null;
|
|
100
|
+
|
|
101
|
+
const receives = data.receives || [];
|
|
102
|
+
const sends = data.sends || [];
|
|
103
|
+
const readsFrom = data.readsFrom || [];
|
|
104
|
+
const writesTo = data.writesTo || [];
|
|
105
|
+
const hasMessages = receives.length > 0 || sends.length > 0;
|
|
106
|
+
const hasContainers = readsFrom.length > 0 || writesTo.length > 0;
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<div className="bg-white border-2 border-dashed border-pink-400 rounded-lg p-4">
|
|
110
|
+
{/* Service Header */}
|
|
111
|
+
<div className="flex items-center justify-between">
|
|
112
|
+
<a
|
|
113
|
+
href={buildUrl(`/architecture/services/${data.id}/${data.version}`)}
|
|
114
|
+
className="flex items-center gap-2 hover:underline"
|
|
115
|
+
>
|
|
116
|
+
<ServerIcon className="h-5 w-5 text-pink-500" />
|
|
117
|
+
<span className="font-semibold text-gray-900">{data.name || data.id}</span>
|
|
118
|
+
<span className="text-xs text-gray-500">v{data.version}</span>
|
|
119
|
+
</a>
|
|
120
|
+
<a
|
|
121
|
+
href={buildUrl(`/architecture/services/${data.id}/${data.version}`)}
|
|
122
|
+
className="p-1 hover:bg-pink-100 rounded-md transition-colors duration-200"
|
|
123
|
+
title="Expand service architecture"
|
|
124
|
+
onClick={(e) => e.stopPropagation()}
|
|
125
|
+
>
|
|
126
|
+
<ArrowsPointingOutIcon className="h-4 w-4 text-gray-500 hover:text-pink-600" />
|
|
127
|
+
</a>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
{data.summary && <p className="mt-2 text-sm text-gray-600 line-clamp-2">{data.summary}</p>}
|
|
131
|
+
|
|
132
|
+
{/* Message Flow Diagram */}
|
|
133
|
+
{hasMessages && (
|
|
134
|
+
<div className="mt-4 flex items-stretch gap-3">
|
|
135
|
+
{/* Receives (Inbound) */}
|
|
136
|
+
<div className="flex-1 bg-blue-50 border border-blue-200 rounded-lg p-3">
|
|
137
|
+
<div className="flex items-center gap-1.5 mb-2">
|
|
138
|
+
<ArrowRight className="h-3.5 w-3.5 text-blue-500" />
|
|
139
|
+
<span className="text-xs font-semibold text-blue-700 uppercase">Inbound Messages</span>
|
|
140
|
+
<span className="text-xs text-blue-500">({receives.length})</span>
|
|
141
|
+
</div>
|
|
142
|
+
{receives.length > 0 ? (
|
|
143
|
+
<div className="space-y-1.5">
|
|
144
|
+
{receives.slice(0, 4).map((msg: any, idx: number) => {
|
|
145
|
+
const msgId = msg?.data?.id || msg?.id;
|
|
146
|
+
return msgId ? <MessageBadge key={`${msgId}-${idx}`} message={msg} /> : null;
|
|
147
|
+
})}
|
|
148
|
+
{receives.length > 4 && <p className="text-[10px] text-gray-500 text-center">+{receives.length - 4} more</p>}
|
|
149
|
+
</div>
|
|
150
|
+
) : (
|
|
151
|
+
<p className="text-[10px] text-gray-400 italic">No incoming messages</p>
|
|
152
|
+
)}
|
|
89
153
|
</div>
|
|
90
154
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
totalResults={filteredDomains.length}
|
|
97
|
-
totalItems={domains.length}
|
|
98
|
-
/>
|
|
99
|
-
<button
|
|
100
|
-
onClick={toggleColumnLayout}
|
|
101
|
-
className="flex items-center justify-center p-2 bg-white border border-gray-300 rounded-md hover:bg-gray-50 transition-colors duration-200"
|
|
102
|
-
title={isMultiColumn ? 'Switch to single column' : 'Switch to multi column'}
|
|
103
|
-
>
|
|
104
|
-
{isMultiColumn ? (
|
|
105
|
-
<QueueListIcon className="h-5 w-5 text-gray-600" />
|
|
106
|
-
) : (
|
|
107
|
-
<Squares2X2Icon className="h-5 w-5 text-gray-600" />
|
|
108
|
-
)}
|
|
109
|
-
</button>
|
|
155
|
+
{/* Service Icon (Center) */}
|
|
156
|
+
<div className="flex items-center">
|
|
157
|
+
<div className="bg-pink-100 border-2 border-pink-300 rounded-lg p-3">
|
|
158
|
+
<ServerIcon className="h-6 w-6 text-pink-500" />
|
|
159
|
+
</div>
|
|
110
160
|
</div>
|
|
111
|
-
</div>
|
|
112
|
-
</div>
|
|
113
161
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
<
|
|
128
|
-
<RectangleGroupIcon className="h-5 w-5 text-orange-500" />
|
|
129
|
-
<h3 className="text-lg font-semibold text-gray-900 truncate group-hover:underline transition-colors duration-200">
|
|
130
|
-
{domain.data.name || domain.data.id}
|
|
131
|
-
</h3>
|
|
132
|
-
</div>
|
|
133
|
-
<span className="ml-2 shrink-0 inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-orange-50 text-orange-700">
|
|
134
|
-
v{domain.data.version}
|
|
135
|
-
</span>
|
|
162
|
+
{/* Sends (Outbound) */}
|
|
163
|
+
<div className="flex-1 bg-green-50 border border-green-200 rounded-lg p-3">
|
|
164
|
+
<div className="flex items-center gap-1.5 mb-2">
|
|
165
|
+
<ArrowLeft className="h-3.5 w-3.5 text-green-500" />
|
|
166
|
+
<span className="text-xs font-semibold text-green-700 uppercase">Outbound Messages</span>
|
|
167
|
+
<span className="text-xs text-green-500">({sends.length})</span>
|
|
168
|
+
</div>
|
|
169
|
+
{sends.length > 0 ? (
|
|
170
|
+
<div className="space-y-1.5">
|
|
171
|
+
{sends.slice(0, 4).map((msg: any, idx: number) => {
|
|
172
|
+
const msgId = msg?.data?.id || msg?.id;
|
|
173
|
+
return msgId ? <MessageBadge key={`${msgId}-${idx}`} message={msg} /> : null;
|
|
174
|
+
})}
|
|
175
|
+
{sends.length > 4 && <p className="text-[10px] text-gray-500 text-center">+{sends.length - 4} more</p>}
|
|
136
176
|
</div>
|
|
177
|
+
) : (
|
|
178
|
+
<p className="text-[10px] text-gray-400 italic">No outgoing messages</p>
|
|
179
|
+
)}
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
)}
|
|
137
183
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
<
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
</div>
|
|
155
|
-
<div className="flex items-center gap-2 bg-white rounded-lg px-3 py-2 border border-gray-200 ">
|
|
156
|
-
<EnvelopeIcon className="h-4 w-4 text-blue-500" />
|
|
157
|
-
<div>
|
|
158
|
-
<p className="text-sm font-medium text-gray-900">
|
|
159
|
-
{(domain.sends?.length || 0) + (domain.receives?.length || 0)} Messages
|
|
160
|
-
</p>
|
|
161
|
-
</div>
|
|
162
|
-
</div>
|
|
163
|
-
{domain.data.entities && domain.data.entities.length > 0 && (
|
|
164
|
-
<div className="flex items-center gap-2 bg-white rounded-lg px-3 py-2 border border-gray-200 ">
|
|
165
|
-
<BoxIcon className="h-4 w-4 text-purple-500" />
|
|
166
|
-
<div>
|
|
167
|
-
<p className="text-sm font-medium text-gray-900">{domain.data.entities?.length} Entities</p>
|
|
168
|
-
</div>
|
|
169
|
-
</div>
|
|
170
|
-
)}
|
|
184
|
+
{/* Container Relationships */}
|
|
185
|
+
{hasContainers && (
|
|
186
|
+
<div className="mt-4 pt-4 border-t border-gray-200 grid grid-cols-2 gap-4">
|
|
187
|
+
{/* Reads From */}
|
|
188
|
+
{readsFrom.length > 0 && (
|
|
189
|
+
<div>
|
|
190
|
+
<div className="flex items-center gap-1.5 mb-2">
|
|
191
|
+
<CircleStackIcon className="h-3.5 w-3.5 text-orange-500" />
|
|
192
|
+
<span className="text-xs font-semibold text-gray-700">Reads from</span>
|
|
193
|
+
</div>
|
|
194
|
+
<div className="flex flex-wrap gap-1.5">
|
|
195
|
+
{readsFrom.slice(0, 3).map((container: any, idx: number) => {
|
|
196
|
+
const containerId = container?.data?.id || container?.id;
|
|
197
|
+
return containerId ? <ContainerBadge key={`${containerId}-${idx}`} container={container} type="reads" /> : null;
|
|
198
|
+
})}
|
|
199
|
+
{readsFrom.length > 3 && <span className="text-[10px] text-gray-500">+{readsFrom.length - 3} more</span>}
|
|
171
200
|
</div>
|
|
201
|
+
</div>
|
|
202
|
+
)}
|
|
172
203
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
</div>
|
|
189
|
-
|
|
190
|
-
<div className="flex gap-4">
|
|
191
|
-
<div className="flex items-center gap-2 bg-white rounded-lg px-3 py-2 border border-pink-200">
|
|
192
|
-
<ServerIcon className="h-4 w-4 text-pink-500" />
|
|
193
|
-
<div className="flex">
|
|
194
|
-
<p className="text-sm font-medium text-gray-900">{subdomain.data.services?.length || 0} Services</p>
|
|
195
|
-
</div>
|
|
196
|
-
</div>
|
|
197
|
-
<div className="flex items-center gap-2 bg-white rounded-lg px-3 py-2 border border-gray-200">
|
|
198
|
-
<EnvelopeIcon className="h-4 w-4 text-blue-500" />
|
|
199
|
-
<div>
|
|
200
|
-
<p className="text-sm font-medium text-gray-900">
|
|
201
|
-
{(subdomain.sends?.length || 0) + (subdomain.receives?.length || 0)} Messages
|
|
202
|
-
</p>
|
|
203
|
-
</div>
|
|
204
|
-
</div>
|
|
205
|
-
</div>
|
|
206
|
-
</div>
|
|
207
|
-
))}
|
|
208
|
-
|
|
209
|
-
{/* Services and their messages */}
|
|
210
|
-
{domain.data.services?.slice(0, 2).map((service: any) => (
|
|
211
|
-
<div
|
|
212
|
-
key={service.data.id}
|
|
213
|
-
className="block space-y-2 bg-white border-2 border-dashed border-pink-400 p-4 rounded-lg transition-colors duration-200"
|
|
214
|
-
>
|
|
215
|
-
<div className="flex items-center justify-between">
|
|
216
|
-
<div className="flex items-center gap-2">
|
|
217
|
-
<ServerIcon className="h-4 w-4 text-pink-500" />
|
|
218
|
-
<h4 className="text-sm font-medium text-gray-900">{service.data.name || service.data.id}</h4>
|
|
219
|
-
</div>
|
|
220
|
-
<span className="text-xs text-gray-500">v{service.data.version}</span>
|
|
221
|
-
</div>
|
|
222
|
-
|
|
223
|
-
<div className="flex items-center gap-4">
|
|
224
|
-
<div className="flex-1 h-full flex flex-col bg-blue-50 border border-blue-300 rounded-lg p-3">
|
|
225
|
-
<div className="space-y-1.5 flex-1">
|
|
226
|
-
{service.data.receives?.slice(0, 3).map((message: any) => {
|
|
227
|
-
const { Icon, color } = getCollectionStyles(message.collection);
|
|
228
|
-
return (
|
|
229
|
-
<div
|
|
230
|
-
key={`${message.id}-${message.version}`}
|
|
231
|
-
className="group flex border border-gray-200 items-center gap-1 rounded-md text-[11px] font-medium bg-white"
|
|
232
|
-
>
|
|
233
|
-
<div className="bg-white border-r border-gray-200 px-2 py-1.5 rounded-l-md">
|
|
234
|
-
<Icon className={`h-3 w-3 text-${color}-500`} />
|
|
235
|
-
</div>
|
|
236
|
-
<span className="px-1 py-1 truncate max-w-[140px]">{message.id}</span>
|
|
237
|
-
</div>
|
|
238
|
-
);
|
|
239
|
-
})}
|
|
240
|
-
{service.data.receives && service.data.receives.length > 3 && (
|
|
241
|
-
<div className="text-center py-1">
|
|
242
|
-
<p className="text-gray-500 text-[10px]">+ {service.data.receives.length - 3} more</p>
|
|
243
|
-
</div>
|
|
244
|
-
)}
|
|
245
|
-
{!service.data.receives?.length && (
|
|
246
|
-
<div className="text-center py-6">
|
|
247
|
-
<p className="text-gray-500 text-xs">No messages received</p>
|
|
248
|
-
</div>
|
|
249
|
-
)}
|
|
250
|
-
</div>
|
|
251
|
-
</div>
|
|
252
|
-
|
|
253
|
-
<div className="flex items-center gap-2">
|
|
254
|
-
<div className="w-4 h-[2px] bg-blue-200"></div>
|
|
255
|
-
<div className="bg-white border-2 border-gray-300 rounded-lg p-2 shadow-sm">
|
|
256
|
-
<div className="flex flex-col items-center gap-2">
|
|
257
|
-
<ServerIcon className="h-6 w-6 text-pink-500" />
|
|
258
|
-
<div className="text-center">
|
|
259
|
-
<p className="text-xs font-medium text-gray-900">{service.data.name || service.data.id}</p>
|
|
260
|
-
<p className="text-[10px] text-gray-500">v{service.data.version}</p>
|
|
261
|
-
</div>
|
|
262
|
-
</div>
|
|
263
|
-
</div>
|
|
264
|
-
<div className="w-4 h-[2px] bg-emerald-200"></div>
|
|
265
|
-
</div>
|
|
266
|
-
|
|
267
|
-
<div className="flex-1 h-full flex flex-col bg-green-100 border border-green-300 rounded-lg p-3">
|
|
268
|
-
<div className="space-y-1.5 flex-1">
|
|
269
|
-
{service.data.sends?.slice(0, 3).map((message: any) => {
|
|
270
|
-
const { Icon, color } = getCollectionStyles(message.collection);
|
|
271
|
-
return (
|
|
272
|
-
<div
|
|
273
|
-
key={`${message.id}-${message.version}`}
|
|
274
|
-
className="group flex border border-gray-200 items-center gap-1 rounded-md text-[11px] font-medium bg-white"
|
|
275
|
-
>
|
|
276
|
-
<div className="bg-white border-r border-gray-200 px-2 py-1.5 rounded-l-md">
|
|
277
|
-
<Icon className={`h-3 w-3 text-${color}-500`} />
|
|
278
|
-
</div>
|
|
279
|
-
|
|
280
|
-
<span className="px-1 py-1 truncate max-w-[140px]">{message.id}</span>
|
|
281
|
-
</div>
|
|
282
|
-
);
|
|
283
|
-
})}
|
|
284
|
-
{service.data.sends && service.data.sends.length > 3 && (
|
|
285
|
-
<div className="text-center py-1">
|
|
286
|
-
<p className="text-gray-500 text-xs">+ {service.data.sends.length - 3} more</p>
|
|
287
|
-
</div>
|
|
288
|
-
)}
|
|
289
|
-
{!service.data.sends?.length && (
|
|
290
|
-
<div className="text-center py-6">
|
|
291
|
-
<p className="text-gray-500 text-xs">No messages sent</p>
|
|
292
|
-
</div>
|
|
293
|
-
)}
|
|
294
|
-
</div>
|
|
295
|
-
</div>
|
|
296
|
-
</div>
|
|
297
|
-
|
|
298
|
-
{/* Container lists at the bottom */}
|
|
299
|
-
{((service.data.readsFrom && service.data.readsFrom.length > 0) ||
|
|
300
|
-
(service.data.writesTo && service.data.writesTo.length > 0)) && (
|
|
301
|
-
<div className="mt-3 pt-3 border-t border-gray-200 grid grid-cols-2 gap-4">
|
|
302
|
-
{/* Reads From */}
|
|
303
|
-
{service.data.readsFrom && service.data.readsFrom.length > 0 && (
|
|
304
|
-
<div className="space-y-2">
|
|
305
|
-
<div className="flex items-center gap-2">
|
|
306
|
-
<CircleStackIcon className="h-4 w-4 text-orange-500" />
|
|
307
|
-
<h4 className="text-xs font-semibold text-gray-700">Reads from</h4>
|
|
308
|
-
</div>
|
|
309
|
-
<div className="flex flex-wrap gap-1">
|
|
310
|
-
{service.data.readsFrom.slice(0, 3).map((container: any) => (
|
|
311
|
-
<span
|
|
312
|
-
key={container.id}
|
|
313
|
-
className="group inline-flex items-center gap-1 px-2 py-1 bg-orange-100 border border-orange-300 rounded-md text-[11px] font-medium hover:bg-orange-200 transition-colors duration-200"
|
|
314
|
-
>
|
|
315
|
-
<CircleStackIcon className="h-3 w-3 text-orange-600" />
|
|
316
|
-
<span className="text-orange-800">{container.id}</span>
|
|
317
|
-
</span>
|
|
318
|
-
))}
|
|
319
|
-
{service.data.readsFrom.length > 3 && (
|
|
320
|
-
<span className="inline-flex items-center px-2 py-1 text-xs text-gray-500">
|
|
321
|
-
+ {service.data.readsFrom.length - 3} more
|
|
322
|
-
</span>
|
|
323
|
-
)}
|
|
324
|
-
</div>
|
|
325
|
-
</div>
|
|
326
|
-
)}
|
|
327
|
-
|
|
328
|
-
{/* Writes To */}
|
|
329
|
-
{service.data.writesTo && service.data.writesTo.length > 0 && (
|
|
330
|
-
<div className="space-y-2">
|
|
331
|
-
<div className="flex items-center gap-2">
|
|
332
|
-
<CircleStackIcon className="h-4 w-4 text-purple-500" />
|
|
333
|
-
<h4 className="text-xs font-semibold text-gray-700">Writes to</h4>
|
|
334
|
-
</div>
|
|
335
|
-
<div className="flex flex-wrap gap-1">
|
|
336
|
-
{service.data.writesTo.slice(0, 3).map((container: any) => (
|
|
337
|
-
<span
|
|
338
|
-
key={container.id}
|
|
339
|
-
className="group inline-flex items-center gap-1 px-2 py-1 bg-purple-100 border border-purple-300 rounded-md text-[11px] font-medium hover:bg-purple-200 transition-colors duration-200"
|
|
340
|
-
>
|
|
341
|
-
<CircleStackIcon className="h-3 w-3 text-purple-600" />
|
|
342
|
-
<span className="text-purple-800">{container.id}</span>
|
|
343
|
-
</span>
|
|
344
|
-
))}
|
|
345
|
-
{service.data.writesTo.length > 3 && (
|
|
346
|
-
<span className="inline-flex items-center px-2 py-1 text-xs text-gray-500">
|
|
347
|
-
+ {service.data.writesTo.length - 3} more
|
|
348
|
-
</span>
|
|
349
|
-
)}
|
|
350
|
-
</div>
|
|
351
|
-
</div>
|
|
352
|
-
)}
|
|
353
|
-
</div>
|
|
354
|
-
)}
|
|
355
|
-
</div>
|
|
356
|
-
))}
|
|
357
|
-
{domain.data.domains && domain.data.domains.length > 2 && (
|
|
358
|
-
<div className="block space-y-2 bg-white border-2 border-dashed border-orange-400 p-4 rounded-lg transition-colors duration-200">
|
|
359
|
-
<div className="flex items-center justify-between">
|
|
360
|
-
<div className="flex items-center gap-2">
|
|
361
|
-
<RectangleGroupIcon className="h-4 w-4 text-orange-500/70" />
|
|
362
|
-
<h4 className="text-sm font-medium text-gray-600">+{domain.data.domains.length - 2} more subdomains</h4>
|
|
363
|
-
</div>
|
|
364
|
-
</div>
|
|
365
|
-
</div>
|
|
366
|
-
)}
|
|
367
|
-
{domain.data.services && domain.data.services.length > 2 && (
|
|
368
|
-
<div className="block space-y-2 bg-white border-2 border-dashed border-pink-400 p-4 rounded-lg transition-colors duration-200">
|
|
369
|
-
<div className="flex items-center justify-between">
|
|
370
|
-
<div className="flex items-center gap-2">
|
|
371
|
-
<ServerIcon className="h-4 w-4 text-pink-500/70" />
|
|
372
|
-
<h4 className="text-sm font-medium text-gray-600">+{domain.data.services.length - 2} more services</h4>
|
|
373
|
-
</div>
|
|
374
|
-
</div>
|
|
375
|
-
</div>
|
|
376
|
-
)}
|
|
204
|
+
{/* Writes To */}
|
|
205
|
+
{writesTo.length > 0 && (
|
|
206
|
+
<div>
|
|
207
|
+
<div className="flex items-center gap-1.5 mb-2">
|
|
208
|
+
<CircleStackIcon className="h-3.5 w-3.5 text-purple-500" />
|
|
209
|
+
<span className="text-xs font-semibold text-gray-700">Writes to</span>
|
|
210
|
+
</div>
|
|
211
|
+
<div className="flex flex-wrap gap-1.5">
|
|
212
|
+
{writesTo.slice(0, 3).map((container: any, idx: number) => {
|
|
213
|
+
const containerId = container?.data?.id || container?.id;
|
|
214
|
+
return containerId ? (
|
|
215
|
+
<ContainerBadge key={`${containerId}-${idx}`} container={container} type="writes" />
|
|
216
|
+
) : null;
|
|
217
|
+
})}
|
|
218
|
+
{writesTo.length > 3 && <span className="text-[10px] text-gray-500">+{writesTo.length - 3} more</span>}
|
|
377
219
|
</div>
|
|
378
220
|
</div>
|
|
221
|
+
)}
|
|
222
|
+
</div>
|
|
223
|
+
)}
|
|
224
|
+
</div>
|
|
225
|
+
);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
const SubdomainSection = memo(({ subdomain }: { subdomain: any }) => {
|
|
229
|
+
const data = subdomain?.data || subdomain;
|
|
230
|
+
const [isCollapsed, setIsCollapsed] = useState(false);
|
|
231
|
+
|
|
232
|
+
if (!data?.id) return null;
|
|
233
|
+
|
|
234
|
+
const services = data.services || [];
|
|
235
|
+
const entities = data.entities || [];
|
|
236
|
+
|
|
237
|
+
return (
|
|
238
|
+
<div className="bg-orange-50 border-2 border-orange-400 rounded-lg p-6">
|
|
239
|
+
{/* Subdomain Header */}
|
|
240
|
+
<div className="flex items-center justify-between mb-4">
|
|
241
|
+
<div className="flex items-center gap-2">
|
|
242
|
+
<RectangleGroupIcon className="h-5 w-5 text-orange-500" />
|
|
243
|
+
<h3 className="text-lg font-semibold text-gray-900">{data.name || data.id}</h3>
|
|
244
|
+
<span className="text-xs text-gray-500">v{data.version}</span>
|
|
245
|
+
<span className="px-2 py-0.5 bg-orange-200 text-orange-800 text-xs rounded">Subdomain</span>
|
|
246
|
+
</div>
|
|
247
|
+
<div className="flex gap-2">
|
|
248
|
+
<button
|
|
249
|
+
onClick={() => setIsCollapsed(!isCollapsed)}
|
|
250
|
+
className="p-1 hover:bg-orange-200 rounded-md transition-colors cursor-pointer text-gray-500 hover:text-gray-700"
|
|
251
|
+
>
|
|
252
|
+
{isCollapsed ? <ChevronDownIcon className="h-5 w-5" /> : <ChevronUpIcon className="h-5 w-5" />}
|
|
253
|
+
</button>
|
|
254
|
+
<a
|
|
255
|
+
href={buildUrl(`/architecture/domains/${data.id}/${data.version}`)}
|
|
256
|
+
className="p-1 hover:bg-orange-200 rounded-md transition-colors cursor-pointer text-gray-500 hover:text-gray-700"
|
|
257
|
+
title="Expand domain architecture"
|
|
258
|
+
onClick={(e) => e.stopPropagation()}
|
|
259
|
+
>
|
|
260
|
+
<ArrowsPointingOutIcon className="h-5 w-5" />
|
|
379
261
|
</a>
|
|
380
|
-
|
|
262
|
+
</div>
|
|
381
263
|
</div>
|
|
382
264
|
|
|
383
|
-
{
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
265
|
+
{!isCollapsed && (
|
|
266
|
+
<>
|
|
267
|
+
{/* Subdomain Entities */}
|
|
268
|
+
{entities.length > 0 && (
|
|
269
|
+
<div className="mb-4">
|
|
270
|
+
<h4 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2">Entities</h4>
|
|
271
|
+
<div className="flex flex-wrap gap-2">
|
|
272
|
+
{entities.map((entity: any) => {
|
|
273
|
+
const entityId = entity?.data?.id || entity?.id;
|
|
274
|
+
return entityId ? <EntityBadge key={entityId} entity={entity} /> : null;
|
|
275
|
+
})}
|
|
276
|
+
</div>
|
|
277
|
+
</div>
|
|
278
|
+
)}
|
|
279
|
+
|
|
280
|
+
{/* Subdomain Services */}
|
|
281
|
+
{services.length > 0 && (
|
|
282
|
+
<div>
|
|
283
|
+
<h4 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2">Services</h4>
|
|
284
|
+
<div className="grid gap-4 xl:grid-cols-2">
|
|
285
|
+
{services.map((service: any) => {
|
|
286
|
+
const serviceId = service?.data?.id || service?.id;
|
|
287
|
+
// Ensure we pass the service down with its messages populated
|
|
288
|
+
return serviceId ? <ServiceCard key={serviceId} service={service} /> : null;
|
|
289
|
+
})}
|
|
290
|
+
</div>
|
|
291
|
+
</div>
|
|
292
|
+
)}
|
|
293
|
+
|
|
294
|
+
{entities.length === 0 && services.length === 0 && (
|
|
295
|
+
<p className="text-sm text-gray-500 italic">No entities or services in this subdomain</p>
|
|
296
|
+
)}
|
|
297
|
+
</>
|
|
387
298
|
)}
|
|
388
299
|
</div>
|
|
389
300
|
);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// ============================================
|
|
304
|
+
// Main Component
|
|
305
|
+
// ============================================
|
|
306
|
+
|
|
307
|
+
export default function DomainGrid({ domain }: DomainGridProps) {
|
|
308
|
+
const data = domain?.data;
|
|
309
|
+
if (!data) return <div>No domain data</div>;
|
|
310
|
+
|
|
311
|
+
const subdomains = data.domains || [];
|
|
312
|
+
const entities = data.entities || [];
|
|
313
|
+
const services = data.services || [];
|
|
314
|
+
|
|
315
|
+
// Get services that are NOT in any subdomain
|
|
316
|
+
const subdomainServiceIds = useMemo(
|
|
317
|
+
() =>
|
|
318
|
+
new Set(
|
|
319
|
+
subdomains.flatMap((sd: any) => {
|
|
320
|
+
const sdData = sd?.data || sd;
|
|
321
|
+
return (sdData?.services || []).map((s: any) => s?.data?.id || s?.id);
|
|
322
|
+
})
|
|
323
|
+
),
|
|
324
|
+
[subdomains]
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
const topLevelServices = useMemo(
|
|
328
|
+
() =>
|
|
329
|
+
services.filter((s: any) => {
|
|
330
|
+
const sId = s?.data?.id || s?.id;
|
|
331
|
+
return sId && !subdomainServiceIds.has(sId);
|
|
332
|
+
}),
|
|
333
|
+
[services, subdomainServiceIds]
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
return (
|
|
337
|
+
<div className="space-y-6">
|
|
338
|
+
{/* Domain Container - Yellow */}
|
|
339
|
+
<div className="bg-yellow-50 border-2 border-yellow-400 rounded-xl p-6">
|
|
340
|
+
{/* Domain Header */}
|
|
341
|
+
<div className="flex items-center justify-between mb-4">
|
|
342
|
+
<div className="flex items-center gap-3">
|
|
343
|
+
<RectangleGroupIcon className="h-7 w-7 text-yellow-600" />
|
|
344
|
+
<h1 className="text-2xl font-bold text-gray-900">{data.name || data.id}</h1>
|
|
345
|
+
<span className="px-2 py-0.5 bg-yellow-200 text-yellow-800 text-xs font-medium rounded">v{data.version}</span>
|
|
346
|
+
<span className="px-2 py-0.5 bg-yellow-300 text-yellow-900 text-xs font-medium rounded">Domain</span>
|
|
347
|
+
</div>
|
|
348
|
+
<div className="flex gap-3">
|
|
349
|
+
<a
|
|
350
|
+
href={buildUrl(`/docs/domains/${data.id}/${data.version}`)}
|
|
351
|
+
className="text-sm bg-white px-3 py-1.5 rounded border border-gray-300 hover:bg-gray-50 transition-colors"
|
|
352
|
+
>
|
|
353
|
+
View docs
|
|
354
|
+
</a>
|
|
355
|
+
<a
|
|
356
|
+
href={buildUrl(`/visualiser/domains/${data.id}/${data.version}`)}
|
|
357
|
+
className="text-sm bg-white px-3 py-1.5 rounded border border-gray-300 hover:bg-gray-50 transition-colors"
|
|
358
|
+
>
|
|
359
|
+
Visualizer
|
|
360
|
+
</a>
|
|
361
|
+
</div>
|
|
362
|
+
</div>
|
|
363
|
+
|
|
364
|
+
{data.summary && <p className="text-gray-600 mb-4">{data.summary}</p>}
|
|
365
|
+
|
|
366
|
+
{/* Domain Entities */}
|
|
367
|
+
{entities.length > 0 && (
|
|
368
|
+
<div className="mb-6">
|
|
369
|
+
<h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2">Entities</h3>
|
|
370
|
+
<div className="flex flex-wrap gap-2">
|
|
371
|
+
{entities.map((entity: any) => {
|
|
372
|
+
const entityId = entity?.data?.id || entity?.id;
|
|
373
|
+
return entityId ? <EntityBadge key={entityId} entity={entity} /> : null;
|
|
374
|
+
})}
|
|
375
|
+
</div>
|
|
376
|
+
</div>
|
|
377
|
+
)}
|
|
378
|
+
|
|
379
|
+
{/* Top-level Services (not in subdomains) */}
|
|
380
|
+
{topLevelServices.length > 0 && (
|
|
381
|
+
<div className="mb-6">
|
|
382
|
+
<h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-3">Services</h3>
|
|
383
|
+
<div className="grid gap-4 xl:grid-cols-2">
|
|
384
|
+
{topLevelServices.map((service: any) => {
|
|
385
|
+
const serviceId = service?.data?.id || service?.id;
|
|
386
|
+
return serviceId ? <ServiceCard key={serviceId} service={service} /> : null;
|
|
387
|
+
})}
|
|
388
|
+
</div>
|
|
389
|
+
</div>
|
|
390
|
+
)}
|
|
391
|
+
|
|
392
|
+
{/* Subdomains - nested inside domain */}
|
|
393
|
+
{subdomains.length > 0 && (
|
|
394
|
+
<div>
|
|
395
|
+
<h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-3">Subdomains</h3>
|
|
396
|
+
<div className="space-y-4">
|
|
397
|
+
{subdomains.map((subdomain: any) => {
|
|
398
|
+
const subdomainId = subdomain?.data?.id || subdomain?.id;
|
|
399
|
+
return subdomainId ? <SubdomainSection key={subdomainId} subdomain={subdomain} /> : null;
|
|
400
|
+
})}
|
|
401
|
+
</div>
|
|
402
|
+
</div>
|
|
403
|
+
)}
|
|
404
|
+
|
|
405
|
+
{/* Empty state */}
|
|
406
|
+
{entities.length === 0 && services.length === 0 && subdomains.length === 0 && (
|
|
407
|
+
<div className="text-center py-8">
|
|
408
|
+
<p className="text-gray-500">This domain has no entities, services, or subdomains defined.</p>
|
|
409
|
+
</div>
|
|
410
|
+
)}
|
|
411
|
+
</div>
|
|
412
|
+
</div>
|
|
413
|
+
);
|
|
390
414
|
}
|