@eventcatalog/core 2.62.1 → 2.64.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/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-4BYDUGYI.js → chunk-6AMZOBWI.js} +1 -1
- package/dist/{chunk-GZ2SVHEA.js → chunk-CWGFHLMX.js} +1 -1
- package/dist/{chunk-IWFL6VRS.js → chunk-PLMTJHGH.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +1 -1
- package/dist/eventcatalog.config.d.cts +34 -0
- package/dist/eventcatalog.config.d.ts +34 -0
- package/dist/eventcatalog.js +3 -3
- package/eventcatalog/astro.config.mjs +2 -1
- package/eventcatalog/public/icons/avro.svg +21 -0
- package/eventcatalog/public/icons/json-schema.svg +6 -0
- package/eventcatalog/public/icons/proto.svg +10 -0
- package/eventcatalog/src/components/Grids/utils.tsx +5 -3
- package/eventcatalog/src/components/MDX/RemoteFile.astro +5 -11
- package/eventcatalog/src/components/MDX/SchemaViewer/SchemaViewerRoot.astro +41 -6
- package/eventcatalog/src/components/SchemaExplorer/ApiAccessSection.tsx +139 -0
- package/eventcatalog/src/components/SchemaExplorer/AvroSchemaViewer.tsx +423 -0
- package/eventcatalog/src/components/SchemaExplorer/DiffViewer.tsx +102 -0
- package/eventcatalog/src/components/SchemaExplorer/JSONSchemaViewer.tsx +740 -0
- package/eventcatalog/src/components/SchemaExplorer/OwnersSection.tsx +56 -0
- package/eventcatalog/src/components/SchemaExplorer/Pagination.tsx +33 -0
- package/eventcatalog/src/components/SchemaExplorer/ProducersConsumersSection.tsx +91 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaCodeModal.tsx +93 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaContentViewer.tsx +130 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsHeader.tsx +181 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +232 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaExplorer.tsx +415 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaFilters.tsx +174 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaListItem.tsx +73 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaViewerModal.tsx +77 -0
- package/eventcatalog/src/components/SchemaExplorer/VersionHistoryModal.tsx +72 -0
- package/eventcatalog/src/components/SchemaExplorer/types.ts +45 -0
- package/eventcatalog/src/components/SchemaExplorer/utils.ts +81 -0
- package/eventcatalog/src/components/SideNav/ListViewSideBar/index.tsx +33 -2
- package/eventcatalog/src/components/Tables/Table.tsx +10 -2
- package/eventcatalog/src/components/Tables/columns/ContainersTableColumns.tsx +10 -8
- package/eventcatalog/src/components/Tables/columns/DomainTableColumns.tsx +8 -6
- package/eventcatalog/src/components/Tables/columns/FlowTableColumns.tsx +9 -7
- package/eventcatalog/src/components/Tables/columns/MessageTableColumns.tsx +11 -9
- package/eventcatalog/src/components/Tables/columns/ServiceTableColumns.tsx +9 -7
- package/eventcatalog/src/components/Tables/columns/SharedColumns.tsx +4 -2
- package/eventcatalog/src/components/Tables/columns/TeamsTableColumns.tsx +12 -8
- package/eventcatalog/src/components/Tables/columns/UserTableColumns.tsx +14 -9
- package/eventcatalog/src/components/Tables/columns/index.tsx +9 -8
- package/eventcatalog/src/content.config.ts +3 -2
- package/eventcatalog/src/enterprise/custom-documentation/pages/docs/custom/index.astro +1 -0
- package/eventcatalog/src/layouts/DirectoryLayout.astro +21 -22
- package/eventcatalog/src/layouts/DiscoverLayout.astro +26 -12
- package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +10 -0
- package/eventcatalog/src/pages/api/schemas/[collection]/[id]/[version]/index.ts +45 -0
- package/eventcatalog/src/pages/api/schemas/services/[id]/[version]/[specification]/index.ts +51 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +1 -0
- package/eventcatalog/src/pages/docs/llm/schemas.txt.ts +86 -0
- package/eventcatalog/src/pages/schemas/index.astro +175 -0
- package/eventcatalog/src/types/index.ts +9 -0
- package/eventcatalog/src/utils/files.ts +9 -0
- package/package.json +1 -1
- package/eventcatalog/src/components/MDX/SchemaViewer/SchemaProperty.astro +0 -204
- package/eventcatalog/src/components/MDX/SchemaViewer/SchemaViewer.astro +0 -705
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ChevronUpIcon, ChevronDownIcon } from '@heroicons/react/24/outline';
|
|
2
|
+
import { UserIcon, UserGroupIcon } from '@heroicons/react/20/solid';
|
|
3
|
+
import type { SchemaItem, Owner } from './types';
|
|
4
|
+
|
|
5
|
+
interface OwnersSectionProps {
|
|
6
|
+
message: SchemaItem;
|
|
7
|
+
isExpanded: boolean;
|
|
8
|
+
onToggle: () => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default function OwnersSection({ message, isExpanded, onToggle }: OwnersSectionProps) {
|
|
12
|
+
const owners = message.data.owners || [];
|
|
13
|
+
|
|
14
|
+
if (owners.length === 0) return null;
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div className="flex-shrink-0 border-b border-gray-200">
|
|
18
|
+
<button
|
|
19
|
+
onClick={onToggle}
|
|
20
|
+
className="w-full flex items-center justify-between px-4 py-1.5 text-left hover:bg-gray-50 transition-colors"
|
|
21
|
+
>
|
|
22
|
+
<div className="flex items-center gap-2">
|
|
23
|
+
<UserIcon className="h-4 w-4 text-gray-600" />
|
|
24
|
+
<span className="text-xs font-semibold text-gray-900">Owners</span>
|
|
25
|
+
<span className="inline-flex items-center rounded-full bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-700">
|
|
26
|
+
{owners.length}
|
|
27
|
+
</span>
|
|
28
|
+
</div>
|
|
29
|
+
{isExpanded ? <ChevronUpIcon className="h-4 w-4 text-gray-600" /> : <ChevronDownIcon className="h-4 w-4 text-gray-600" />}
|
|
30
|
+
</button>
|
|
31
|
+
|
|
32
|
+
{isExpanded && (
|
|
33
|
+
<div className="px-4 pb-2 bg-gray-50">
|
|
34
|
+
<div className="flex flex-wrap gap-2">
|
|
35
|
+
{owners.map((owner: Owner, idx: number) => {
|
|
36
|
+
const Icon = owner.type === 'users' ? UserIcon : UserGroupIcon;
|
|
37
|
+
return (
|
|
38
|
+
<a
|
|
39
|
+
key={`${owner.id}-${idx}`}
|
|
40
|
+
href={owner.href}
|
|
41
|
+
className="inline-flex items-center gap-1.5 pl-1 pr-3 py-1 text-xs font-medium text-gray-700 bg-white border border-gray-200 rounded-full hover:border-gray-300 hover:shadow-sm transition-all"
|
|
42
|
+
title={owner.name}
|
|
43
|
+
>
|
|
44
|
+
<div className="flex items-center justify-center w-5 h-5 bg-gradient-to-b from-purple-500 to-purple-600 rounded-full">
|
|
45
|
+
<Icon className="h-3 w-3 text-white" />
|
|
46
|
+
</div>
|
|
47
|
+
<span className="font-medium">{owner.name}</span>
|
|
48
|
+
</a>
|
|
49
|
+
);
|
|
50
|
+
})}
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
)}
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
interface PaginationProps {
|
|
2
|
+
currentPage: number;
|
|
3
|
+
totalPages: number;
|
|
4
|
+
onPageChange: (page: number) => void;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export default function Pagination({ currentPage, totalPages, onPageChange }: PaginationProps) {
|
|
8
|
+
if (totalPages <= 1) return null;
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<div className="flex-shrink-0 border-t border-gray-200 p-3 bg-gray-50">
|
|
12
|
+
<div className="flex items-center justify-between text-xs">
|
|
13
|
+
<button
|
|
14
|
+
onClick={() => onPageChange(Math.max(1, currentPage - 1))}
|
|
15
|
+
disabled={currentPage === 1}
|
|
16
|
+
className="px-3 py-1.5 text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
17
|
+
>
|
|
18
|
+
Previous
|
|
19
|
+
</button>
|
|
20
|
+
<span className="text-gray-600">
|
|
21
|
+
Page {currentPage} of {totalPages}
|
|
22
|
+
</span>
|
|
23
|
+
<button
|
|
24
|
+
onClick={() => onPageChange(Math.min(totalPages, currentPage + 1))}
|
|
25
|
+
disabled={currentPage === totalPages}
|
|
26
|
+
className="px-3 py-1.5 text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
27
|
+
>
|
|
28
|
+
Next
|
|
29
|
+
</button>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { ChevronUpIcon, ChevronDownIcon } from '@heroicons/react/24/outline';
|
|
2
|
+
import { ServerIcon } from '@heroicons/react/20/solid';
|
|
3
|
+
import { buildUrl } from '@utils/url-builder';
|
|
4
|
+
import type { SchemaItem, Producer, Consumer } from './types';
|
|
5
|
+
|
|
6
|
+
interface ProducersConsumersSectionProps {
|
|
7
|
+
message: SchemaItem;
|
|
8
|
+
isExpanded: boolean;
|
|
9
|
+
onToggle: () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default function ProducersConsumersSection({ message, isExpanded, onToggle }: ProducersConsumersSectionProps) {
|
|
13
|
+
const producers = message.data.producers || [];
|
|
14
|
+
const consumers = message.data.consumers || [];
|
|
15
|
+
const totalCount = producers.length + consumers.length;
|
|
16
|
+
|
|
17
|
+
if (totalCount === 0) return null;
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div className="flex-shrink-0 border-b border-gray-200">
|
|
21
|
+
<button
|
|
22
|
+
onClick={onToggle}
|
|
23
|
+
className="w-full flex items-center justify-between px-4 py-1.5 text-left hover:bg-gray-50 transition-colors"
|
|
24
|
+
>
|
|
25
|
+
<div className="flex items-center gap-2">
|
|
26
|
+
<svg
|
|
27
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
28
|
+
className="h-4 w-4 text-gray-600"
|
|
29
|
+
fill="none"
|
|
30
|
+
viewBox="0 0 24 24"
|
|
31
|
+
stroke="currentColor"
|
|
32
|
+
>
|
|
33
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
|
|
34
|
+
</svg>
|
|
35
|
+
<span className="text-xs font-semibold text-gray-900">Producers & Consumers</span>
|
|
36
|
+
<span className="inline-flex items-center rounded-full bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-700">
|
|
37
|
+
{totalCount} services
|
|
38
|
+
</span>
|
|
39
|
+
</div>
|
|
40
|
+
{isExpanded ? <ChevronUpIcon className="h-4 w-4 text-gray-600" /> : <ChevronDownIcon className="h-4 w-4 text-gray-600" />}
|
|
41
|
+
</button>
|
|
42
|
+
|
|
43
|
+
{isExpanded && (
|
|
44
|
+
<div className="px-4 pb-2 bg-gray-50">
|
|
45
|
+
{producers.length > 0 && (
|
|
46
|
+
<div className="mb-3">
|
|
47
|
+
<h4 className="text-xs font-semibold text-gray-900 mb-2">Producers ({producers.length})</h4>
|
|
48
|
+
<div className="flex flex-wrap gap-2">
|
|
49
|
+
{producers.map((producer: Producer, idx: number) => (
|
|
50
|
+
<a
|
|
51
|
+
key={`${producer.id}-${idx}`}
|
|
52
|
+
href={buildUrl(`/docs/services/${producer.id}/${producer.version}`)}
|
|
53
|
+
className="inline-flex items-center gap-1.5 pl-1 pr-3 py-1 text-xs font-medium text-gray-700 bg-white border border-gray-200 rounded-full hover:border-gray-300 hover:shadow-sm transition-all"
|
|
54
|
+
title={`View ${producer.id}`}
|
|
55
|
+
>
|
|
56
|
+
<div className="flex items-center justify-center w-5 h-5 bg-gradient-to-b from-pink-500 to-pink-600 rounded-full">
|
|
57
|
+
<ServerIcon className="h-3 w-3 text-white" />
|
|
58
|
+
</div>
|
|
59
|
+
<span className="font-medium">{producer.id}</span>
|
|
60
|
+
<span className="text-gray-500 text-[11px]">v{producer.version}</span>
|
|
61
|
+
</a>
|
|
62
|
+
))}
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
)}
|
|
66
|
+
{consumers.length > 0 && (
|
|
67
|
+
<div>
|
|
68
|
+
<h4 className="text-xs font-semibold text-gray-900 mb-2">Consumers ({consumers.length})</h4>
|
|
69
|
+
<div className="flex flex-wrap gap-2">
|
|
70
|
+
{consumers.map((consumer: Consumer, idx: number) => (
|
|
71
|
+
<a
|
|
72
|
+
key={`${consumer.id}-${idx}`}
|
|
73
|
+
href={buildUrl(`/docs/services/${consumer.id}/${consumer.version}`)}
|
|
74
|
+
className="inline-flex items-center gap-1.5 pl-1 pr-3 py-1 text-xs font-medium text-gray-700 bg-white border border-gray-200 rounded-full hover:border-gray-300 hover:shadow-sm transition-all"
|
|
75
|
+
title={`View ${consumer.id}`}
|
|
76
|
+
>
|
|
77
|
+
<div className="flex items-center justify-center w-5 h-5 bg-gradient-to-b from-pink-500 to-pink-600 rounded-full">
|
|
78
|
+
<ServerIcon className="h-3 w-3 text-white" />
|
|
79
|
+
</div>
|
|
80
|
+
<span className="font-medium">{consumer.id}</span>
|
|
81
|
+
<span className="text-gray-500 text-[11px]">v{consumer.version}</span>
|
|
82
|
+
</a>
|
|
83
|
+
))}
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
)}
|
|
87
|
+
</div>
|
|
88
|
+
)}
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import * as Dialog from '@radix-ui/react-dialog';
|
|
2
|
+
import { XMarkIcon, ArrowsPointingOutIcon, ClipboardDocumentIcon } from '@heroicons/react/24/outline';
|
|
3
|
+
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
|
4
|
+
import { oneLight as syntaxHighlighterStyle } from 'react-syntax-highlighter/dist/cjs/styles/prism';
|
|
5
|
+
import { getLanguageForHighlight } from './utils';
|
|
6
|
+
import type { SchemaItem } from './types';
|
|
7
|
+
|
|
8
|
+
interface SchemaCodeModalProps {
|
|
9
|
+
isOpen: boolean;
|
|
10
|
+
onOpenChange: (open: boolean) => void;
|
|
11
|
+
message: SchemaItem;
|
|
12
|
+
onCopy: () => void;
|
|
13
|
+
isCopied: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default function SchemaCodeModal({ isOpen, onOpenChange, message, onCopy, isCopied }: SchemaCodeModalProps) {
|
|
17
|
+
if (!message.schemaContent) return null;
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<Dialog.Root open={isOpen} onOpenChange={onOpenChange}>
|
|
21
|
+
<Dialog.Portal>
|
|
22
|
+
<Dialog.Overlay className="fixed inset-0 bg-black/50 data-[state=open]:animate-overlayShow z-50" />
|
|
23
|
+
<Dialog.Content className="fixed inset-4 md:inset-8 rounded-lg bg-white shadow-xl focus:outline-none data-[state=open]:animate-contentShow z-[100] flex flex-col">
|
|
24
|
+
{/* Header */}
|
|
25
|
+
<div className="flex items-center justify-between p-6 border-b border-gray-200 flex-shrink-0">
|
|
26
|
+
<div className="flex items-center gap-3">
|
|
27
|
+
<ArrowsPointingOutIcon className="h-6 w-6 text-gray-500" />
|
|
28
|
+
<div>
|
|
29
|
+
<Dialog.Title className="text-xl font-semibold text-gray-900">{message.data.name}</Dialog.Title>
|
|
30
|
+
<Dialog.Description className="text-sm text-gray-600 mt-1">
|
|
31
|
+
v{message.data.version} · {getLanguageForHighlight(message.schemaExtension).toUpperCase()}
|
|
32
|
+
</Dialog.Description>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
<div className="flex items-center gap-2">
|
|
36
|
+
<button
|
|
37
|
+
onClick={onCopy}
|
|
38
|
+
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-gray-700 bg-white border border-gray-300 hover:bg-gray-50 rounded-md transition-colors"
|
|
39
|
+
title="Copy code"
|
|
40
|
+
>
|
|
41
|
+
<ClipboardDocumentIcon className="h-4 w-4" />
|
|
42
|
+
{isCopied ? 'Copied!' : 'Copy'}
|
|
43
|
+
</button>
|
|
44
|
+
<Dialog.Close asChild>
|
|
45
|
+
<button
|
|
46
|
+
type="button"
|
|
47
|
+
className="p-2 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded-md transition-colors"
|
|
48
|
+
aria-label="Close"
|
|
49
|
+
>
|
|
50
|
+
<XMarkIcon className="h-6 w-6" />
|
|
51
|
+
</button>
|
|
52
|
+
</Dialog.Close>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
{/* Content */}
|
|
57
|
+
<div className="flex-1 overflow-auto p-6">
|
|
58
|
+
<SyntaxHighlighter
|
|
59
|
+
language={getLanguageForHighlight(message.schemaExtension)}
|
|
60
|
+
style={syntaxHighlighterStyle}
|
|
61
|
+
customStyle={{
|
|
62
|
+
margin: 0,
|
|
63
|
+
padding: '1rem',
|
|
64
|
+
borderRadius: '0.5rem',
|
|
65
|
+
fontSize: '0.875rem',
|
|
66
|
+
lineHeight: '1.6',
|
|
67
|
+
height: '100%',
|
|
68
|
+
}}
|
|
69
|
+
className="bg-white border border-gray-200 rounded-lg"
|
|
70
|
+
showLineNumbers={true}
|
|
71
|
+
wrapLines={true}
|
|
72
|
+
wrapLongLines={true}
|
|
73
|
+
>
|
|
74
|
+
{message.schemaContent}
|
|
75
|
+
</SyntaxHighlighter>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
{/* Footer */}
|
|
79
|
+
<div className="flex justify-end p-4 border-t border-gray-200 flex-shrink-0">
|
|
80
|
+
<Dialog.Close asChild>
|
|
81
|
+
<button
|
|
82
|
+
type="button"
|
|
83
|
+
className="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 transition-colors"
|
|
84
|
+
>
|
|
85
|
+
Close
|
|
86
|
+
</button>
|
|
87
|
+
</Dialog.Close>
|
|
88
|
+
</div>
|
|
89
|
+
</Dialog.Content>
|
|
90
|
+
</Dialog.Portal>
|
|
91
|
+
</Dialog.Root>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { ClipboardDocumentIcon, ArrowsPointingOutIcon } from '@heroicons/react/24/outline';
|
|
2
|
+
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
|
3
|
+
import { oneLight as syntaxHighlighterStyle } from 'react-syntax-highlighter/dist/cjs/styles/prism';
|
|
4
|
+
import { buildUrl } from '@utils/url-builder';
|
|
5
|
+
import JSONSchemaViewer from './JSONSchemaViewer';
|
|
6
|
+
import AvroSchemaViewer from './AvroSchemaViewer';
|
|
7
|
+
import { getLanguageForHighlight } from './utils';
|
|
8
|
+
import type { SchemaItem } from './types';
|
|
9
|
+
|
|
10
|
+
interface SchemaContentViewerProps {
|
|
11
|
+
message: SchemaItem;
|
|
12
|
+
onCopy: () => void;
|
|
13
|
+
isCopied: boolean;
|
|
14
|
+
viewMode: 'code' | 'schema' | 'diff';
|
|
15
|
+
parsedSchema: any;
|
|
16
|
+
parsedAvroSchema?: any;
|
|
17
|
+
onOpenFullscreen?: () => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default function SchemaContentViewer({
|
|
21
|
+
message,
|
|
22
|
+
onCopy,
|
|
23
|
+
isCopied,
|
|
24
|
+
viewMode,
|
|
25
|
+
parsedSchema,
|
|
26
|
+
parsedAvroSchema,
|
|
27
|
+
onOpenFullscreen,
|
|
28
|
+
}: SchemaContentViewerProps) {
|
|
29
|
+
if (!message.schemaContent) {
|
|
30
|
+
return (
|
|
31
|
+
<div className="flex items-center justify-center h-full text-gray-500">
|
|
32
|
+
<p className="text-sm">No schema content available</p>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Render schema viewer based on schema type
|
|
38
|
+
if (viewMode === 'schema') {
|
|
39
|
+
if (parsedAvroSchema) {
|
|
40
|
+
return <AvroSchemaViewer schema={parsedAvroSchema} onOpenFullscreen={onOpenFullscreen} />;
|
|
41
|
+
}
|
|
42
|
+
if (parsedSchema) {
|
|
43
|
+
return <JSONSchemaViewer schema={parsedSchema} onOpenFullscreen={onOpenFullscreen} />;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div className="h-full overflow-auto p-3 relative bg-white border border-gray-200 rounded-lg">
|
|
49
|
+
<div className="absolute top-5 right-5 z-10 flex items-center gap-2">
|
|
50
|
+
{message.collection === 'services' &&
|
|
51
|
+
(() => {
|
|
52
|
+
const specType = message.specType || 'openapi';
|
|
53
|
+
const specFilename = message.specFilenameWithoutExtension || 'schema';
|
|
54
|
+
|
|
55
|
+
// Determine the URL path segment based on spec type
|
|
56
|
+
let urlSegment = 'spec';
|
|
57
|
+
if (specType === 'asyncapi') {
|
|
58
|
+
urlSegment = 'asyncapi';
|
|
59
|
+
} else if (specType === 'graphql') {
|
|
60
|
+
urlSegment = 'graphql';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const specUrl = buildUrl(`/docs/services/${message.data.id}/${message.data.version}/${urlSegment}/${specFilename}`);
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<a
|
|
67
|
+
href={specUrl}
|
|
68
|
+
className="inline-flex items-center gap-1.5 px-2.5 py-1.5 text-xs font-medium text-gray-700 bg-white border border-gray-300 hover:bg-gray-50 rounded-md transition-colors shadow-sm"
|
|
69
|
+
title="View full specification"
|
|
70
|
+
>
|
|
71
|
+
<svg
|
|
72
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
73
|
+
className="h-3.5 w-3.5"
|
|
74
|
+
fill="none"
|
|
75
|
+
viewBox="0 0 24 24"
|
|
76
|
+
stroke="currentColor"
|
|
77
|
+
>
|
|
78
|
+
<path
|
|
79
|
+
strokeLinecap="round"
|
|
80
|
+
strokeLinejoin="round"
|
|
81
|
+
strokeWidth={2}
|
|
82
|
+
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
|
|
83
|
+
/>
|
|
84
|
+
</svg>
|
|
85
|
+
View Spec
|
|
86
|
+
</a>
|
|
87
|
+
);
|
|
88
|
+
})()}
|
|
89
|
+
{onOpenFullscreen && (
|
|
90
|
+
<button
|
|
91
|
+
onClick={onOpenFullscreen}
|
|
92
|
+
className="inline-flex items-center gap-1.5 px-2.5 py-1.5 text-xs font-medium text-gray-700 bg-white border border-gray-300 hover:bg-gray-50 rounded-md transition-colors shadow-sm"
|
|
93
|
+
title="Open in fullscreen"
|
|
94
|
+
>
|
|
95
|
+
<ArrowsPointingOutIcon className="h-3.5 w-3.5" />
|
|
96
|
+
Fullscreen
|
|
97
|
+
</button>
|
|
98
|
+
)}
|
|
99
|
+
<button
|
|
100
|
+
onClick={onCopy}
|
|
101
|
+
className="inline-flex items-center gap-1.5 px-2.5 py-1.5 text-xs font-medium text-gray-700 bg-white border border-gray-300 hover:bg-gray-50 rounded-md transition-colors shadow-sm"
|
|
102
|
+
title="Copy code"
|
|
103
|
+
>
|
|
104
|
+
<ClipboardDocumentIcon className="h-3.5 w-3.5" />
|
|
105
|
+
{isCopied ? 'Copied!' : 'Copy'}
|
|
106
|
+
</button>
|
|
107
|
+
</div>
|
|
108
|
+
<SyntaxHighlighter
|
|
109
|
+
language={getLanguageForHighlight(message.schemaExtension)}
|
|
110
|
+
style={syntaxHighlighterStyle}
|
|
111
|
+
customStyle={{
|
|
112
|
+
margin: 0,
|
|
113
|
+
padding: '0.75rem',
|
|
114
|
+
paddingTop: '2.5rem',
|
|
115
|
+
borderRadius: '0.5rem',
|
|
116
|
+
fontSize: '0.875rem',
|
|
117
|
+
lineHeight: '1.6',
|
|
118
|
+
height: '100%',
|
|
119
|
+
overflow: 'auto',
|
|
120
|
+
}}
|
|
121
|
+
className="bg-white border border-gray-200 rounded-lg"
|
|
122
|
+
showLineNumbers={true}
|
|
123
|
+
wrapLines={true}
|
|
124
|
+
wrapLongLines={true}
|
|
125
|
+
>
|
|
126
|
+
{message.schemaContent}
|
|
127
|
+
</SyntaxHighlighter>
|
|
128
|
+
</div>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { ClipboardDocumentIcon, ArrowDownTrayIcon, CodeBracketIcon, TableCellsIcon } from '@heroicons/react/24/outline';
|
|
2
|
+
import { buildUrl } from '@utils/url-builder';
|
|
3
|
+
import { getCollectionStyles } from '@components/Grids/utils';
|
|
4
|
+
import { getSchemaTypeLabel } from './utils';
|
|
5
|
+
import type { SchemaItem } from './types';
|
|
6
|
+
|
|
7
|
+
interface SchemaDetailsHeaderProps {
|
|
8
|
+
message: SchemaItem;
|
|
9
|
+
availableVersions: SchemaItem[];
|
|
10
|
+
selectedVersion: string | null;
|
|
11
|
+
onVersionChange: (version: string) => void;
|
|
12
|
+
onCopy: () => void;
|
|
13
|
+
onDownload: () => void;
|
|
14
|
+
isCopied: boolean;
|
|
15
|
+
schemaViewMode: 'code' | 'schema' | 'diff';
|
|
16
|
+
onViewModeChange: (mode: 'code' | 'schema' | 'diff') => void;
|
|
17
|
+
hasParsedSchema: boolean;
|
|
18
|
+
hasDiffs: boolean;
|
|
19
|
+
diffCount: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default function SchemaDetailsHeader({
|
|
23
|
+
message,
|
|
24
|
+
availableVersions,
|
|
25
|
+
selectedVersion,
|
|
26
|
+
onVersionChange,
|
|
27
|
+
onCopy,
|
|
28
|
+
onDownload,
|
|
29
|
+
isCopied,
|
|
30
|
+
schemaViewMode,
|
|
31
|
+
onViewModeChange,
|
|
32
|
+
hasParsedSchema,
|
|
33
|
+
hasDiffs,
|
|
34
|
+
diffCount,
|
|
35
|
+
}: SchemaDetailsHeaderProps) {
|
|
36
|
+
const { color, Icon } = getCollectionStyles(message.collection);
|
|
37
|
+
const hasMultipleVersions = availableVersions.length > 1;
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<div className="flex-shrink-0 border-b border-gray-200 p-3">
|
|
41
|
+
<div className="flex items-start justify-between mb-3">
|
|
42
|
+
<div className="flex-1 min-w-0">
|
|
43
|
+
<div className="flex items-center gap-2 mb-1.5">
|
|
44
|
+
<Icon className={`h-5 w-5 text-${color}-500 flex-shrink-0`} />
|
|
45
|
+
<a
|
|
46
|
+
href={buildUrl(`/docs/${message.collection}/${message.data.id}/${message.data.version}`)}
|
|
47
|
+
className={`text-lg font-semibold text-gray-900 hover:text-${color}-600 hover:underline truncate`}
|
|
48
|
+
>
|
|
49
|
+
{message.data.name}
|
|
50
|
+
</a>
|
|
51
|
+
{hasMultipleVersions ? (
|
|
52
|
+
<select
|
|
53
|
+
value={selectedVersion || message.data.version}
|
|
54
|
+
onChange={(e) => onVersionChange(e.target.value)}
|
|
55
|
+
className="text-xs text-gray-700 bg-white border border-gray-300 rounded px-2 py-1 focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent"
|
|
56
|
+
>
|
|
57
|
+
{availableVersions.map((v) => (
|
|
58
|
+
<option key={v.data.version} value={v.data.version}>
|
|
59
|
+
v{v.data.version}
|
|
60
|
+
</option>
|
|
61
|
+
))}
|
|
62
|
+
</select>
|
|
63
|
+
) : (
|
|
64
|
+
<span className="text-xs text-gray-500 flex-shrink-0">v{message.data.version}</span>
|
|
65
|
+
)}
|
|
66
|
+
</div>
|
|
67
|
+
<div className="flex items-center gap-1 mb-2">
|
|
68
|
+
<span
|
|
69
|
+
className={`inline-flex items-center rounded-full bg-${color}-100 px-1.5 py-0.5 text-xs font-medium text-${color}-800`}
|
|
70
|
+
>
|
|
71
|
+
{message.collection}
|
|
72
|
+
</span>
|
|
73
|
+
<span className="inline-flex items-center gap-1 rounded-full bg-gray-100 px-1.5 py-0.5 text-xs font-medium text-gray-800">
|
|
74
|
+
{(() => {
|
|
75
|
+
const ext = message.schemaExtension?.toLowerCase();
|
|
76
|
+
if (
|
|
77
|
+
ext === 'openapi' ||
|
|
78
|
+
ext === 'asyncapi' ||
|
|
79
|
+
ext === 'graphql' ||
|
|
80
|
+
ext === 'avro' ||
|
|
81
|
+
ext === 'json' ||
|
|
82
|
+
ext === 'proto'
|
|
83
|
+
) {
|
|
84
|
+
// Map json extension to json-schema icon
|
|
85
|
+
const iconName = ext === 'json' ? 'json-schema' : ext;
|
|
86
|
+
const iconPath = buildUrl(`/icons/${iconName}.svg`, true);
|
|
87
|
+
return (
|
|
88
|
+
<>
|
|
89
|
+
<img src={iconPath} alt={`${ext} icon`} className="h-3 w-3" />
|
|
90
|
+
{getSchemaTypeLabel(message.schemaExtension)}
|
|
91
|
+
</>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
return getSchemaTypeLabel(message.schemaExtension);
|
|
95
|
+
})()}
|
|
96
|
+
</span>
|
|
97
|
+
</div>
|
|
98
|
+
{message.data.summary && <p className="text-xs text-gray-600 line-clamp-2">{message.data.summary}</p>}
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
{/* Action Buttons */}
|
|
103
|
+
<div className="flex items-center gap-2">
|
|
104
|
+
{/* View Mode Toggle */}
|
|
105
|
+
<div className="flex items-center gap-1 mr-2 border-r border-gray-300 pr-2">
|
|
106
|
+
<button
|
|
107
|
+
onClick={() => onViewModeChange('code')}
|
|
108
|
+
className={`inline-flex items-center gap-1 px-2 py-1 text-xs font-medium rounded transition-colors ${
|
|
109
|
+
schemaViewMode === 'code' ? 'bg-primary text-white' : 'text-gray-600 hover:bg-gray-100'
|
|
110
|
+
}`}
|
|
111
|
+
title="Code view"
|
|
112
|
+
>
|
|
113
|
+
<CodeBracketIcon className="h-3.5 w-3.5" />
|
|
114
|
+
Code
|
|
115
|
+
</button>
|
|
116
|
+
{hasParsedSchema && (
|
|
117
|
+
<button
|
|
118
|
+
onClick={() => onViewModeChange('schema')}
|
|
119
|
+
className={`inline-flex items-center gap-1 px-2 py-1 text-xs font-medium rounded transition-colors ${
|
|
120
|
+
schemaViewMode === 'schema' ? 'bg-primary text-white' : 'text-gray-600 hover:bg-gray-100'
|
|
121
|
+
}`}
|
|
122
|
+
title="Schema view"
|
|
123
|
+
>
|
|
124
|
+
<TableCellsIcon className="h-3.5 w-3.5" />
|
|
125
|
+
Schema
|
|
126
|
+
</button>
|
|
127
|
+
)}
|
|
128
|
+
{hasDiffs && (
|
|
129
|
+
<button
|
|
130
|
+
onClick={() => onViewModeChange('diff')}
|
|
131
|
+
className={`inline-flex items-center gap-1 px-2 py-1 text-xs font-medium rounded transition-colors ${
|
|
132
|
+
schemaViewMode === 'diff' ? 'bg-primary text-white' : 'text-gray-600 hover:bg-gray-100'
|
|
133
|
+
}`}
|
|
134
|
+
title="View version history diffs"
|
|
135
|
+
>
|
|
136
|
+
<svg
|
|
137
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
138
|
+
className="h-3.5 w-3.5"
|
|
139
|
+
fill="none"
|
|
140
|
+
viewBox="0 0 24 24"
|
|
141
|
+
stroke="currentColor"
|
|
142
|
+
>
|
|
143
|
+
<path
|
|
144
|
+
strokeLinecap="round"
|
|
145
|
+
strokeLinejoin="round"
|
|
146
|
+
strokeWidth={2}
|
|
147
|
+
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
|
148
|
+
/>
|
|
149
|
+
</svg>
|
|
150
|
+
Diff ({diffCount})
|
|
151
|
+
</button>
|
|
152
|
+
)}
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
<button
|
|
156
|
+
onClick={onCopy}
|
|
157
|
+
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 transition-colors"
|
|
158
|
+
title="Copy schema to clipboard"
|
|
159
|
+
>
|
|
160
|
+
<ClipboardDocumentIcon className="h-4 w-4" />
|
|
161
|
+
{isCopied ? 'Copied!' : 'Copy'}
|
|
162
|
+
</button>
|
|
163
|
+
<button
|
|
164
|
+
onClick={onDownload}
|
|
165
|
+
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 transition-colors"
|
|
166
|
+
title="Download schema file"
|
|
167
|
+
>
|
|
168
|
+
<ArrowDownTrayIcon className="h-4 w-4" />
|
|
169
|
+
Download
|
|
170
|
+
</button>
|
|
171
|
+
<a
|
|
172
|
+
href={buildUrl(`/docs/${message.collection}/${message.data.id}/${message.data.version}`)}
|
|
173
|
+
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 transition-colors ml-auto"
|
|
174
|
+
title="View full documentation"
|
|
175
|
+
>
|
|
176
|
+
View Docs →
|
|
177
|
+
</a>
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
);
|
|
181
|
+
}
|