@eventcatalog/core 0.0.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/CHANGELOG.md +1 -0
- package/README.md +11 -0
- package/bin/eventcatalog.js +125 -0
- package/components/BreadCrumbs.tsx +50 -0
- package/components/ContentView.tsx +127 -0
- package/components/Footer.tsx +38 -0
- package/components/Grids/EventGrid.tsx +89 -0
- package/components/Grids/ServiceGrid.tsx +70 -0
- package/components/Header.tsx +59 -0
- package/components/Mdx/Admonition.tsx +33 -0
- package/components/Mdx/Examples.tsx +77 -0
- package/components/Mermaid/index.tsx +47 -0
- package/components/NotFound/index.tsx +44 -0
- package/components/Sidebars/EventSidebar.tsx +202 -0
- package/components/Sidebars/ServiceSidebar.tsx +198 -0
- package/components/SyntaxHighlighter.tsx +34 -0
- package/hooks/EventCatalog.tsx +35 -0
- package/lib/__tests__/assets/events/AddedItemToCart/index.md +19 -0
- package/lib/__tests__/assets/events/EmailSent/index.md +15 -0
- package/lib/__tests__/assets/events/EventWithSchemaAndExamples/examples/Basic.cs +31 -0
- package/lib/__tests__/assets/events/EventWithSchemaAndExamples/examples/Basic.js +1 -0
- package/lib/__tests__/assets/events/EventWithSchemaAndExamples/index.md +8 -0
- package/lib/__tests__/assets/events/EventWithSchemaAndExamples/schema.json +4 -0
- package/lib/__tests__/assets/events/EventWithVersions/index.md +10 -0
- package/lib/__tests__/assets/events/EventWithVersions/versioned/0.0.1/index.md +10 -0
- package/lib/__tests__/assets/services/Email Platform/index.md +17 -0
- package/lib/__tests__/events.spec.ts +294 -0
- package/lib/__tests__/file-reader.spec.ts +57 -0
- package/lib/__tests__/graphs.spec.ts +62 -0
- package/lib/__tests__/services.spec.ts +144 -0
- package/lib/events.ts +221 -0
- package/lib/file-reader.ts +52 -0
- package/lib/graphs.ts +33 -0
- package/lib/services.ts +72 -0
- package/next-env.d.ts +5 -0
- package/next.config.js +3 -0
- package/package.json +52 -0
- package/pages/_app.tsx +49 -0
- package/pages/api/event/[name]/download.js +25 -0
- package/pages/events/[name]/logs.tsx +170 -0
- package/pages/events/[name]/v/[version].tsx +19 -0
- package/pages/events/[name].tsx +139 -0
- package/pages/events.tsx +227 -0
- package/pages/index.tsx +47 -0
- package/pages/overview.tsx +80 -0
- package/pages/services/[name].tsx +102 -0
- package/pages/services.tsx +123 -0
- package/pages/users/[id].tsx +83 -0
- package/postcss.config.js +6 -0
- package/public/favicon.ico +0 -0
- package/public/logo-random.svg +114 -0
- package/public/logo.svg +44 -0
- package/public/opengraph.png +0 -0
- package/scripts/__tests__/assets/eventcatalog.config.js +33 -0
- package/scripts/__tests__/generate.spec.ts +39 -0
- package/scripts/generate.js +28 -0
- package/styles/Home.module.css +116 -0
- package/styles/globals.css +48 -0
- package/tailwind.config.js +16 -0
- package/tsconfig.json +38 -0
- package/types/index.ts +7 -0
- package/utils/random-bg.ts +13 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { getEventByName } from '@/lib/events';
|
|
2
|
+
import EventPage, { EventsPageProps } from '../../[name]';
|
|
3
|
+
|
|
4
|
+
export default function Events(props: EventsPageProps) {
|
|
5
|
+
return <EventPage {...props} />;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function getServerSideProps({ params }) {
|
|
9
|
+
const { name: eventName, version } = params;
|
|
10
|
+
const { event, markdown } = await getEventByName(eventName, version);
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
props: {
|
|
14
|
+
event,
|
|
15
|
+
markdown,
|
|
16
|
+
loadedVersion: version,
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import Head from 'next/head';
|
|
2
|
+
import { MDXRemote } from 'next-mdx-remote';
|
|
3
|
+
|
|
4
|
+
import { Event } from '@eventcatalog/types';
|
|
5
|
+
import { editUrl } from '../../eventcatalog.config';
|
|
6
|
+
import Admonition from '@/components/Mdx/Admonition';
|
|
7
|
+
import Examples from '@/components/Mdx/Examples';
|
|
8
|
+
|
|
9
|
+
import getBackgroundColor from '@/utils/random-bg';
|
|
10
|
+
|
|
11
|
+
import ContentView from '@/components/ContentView';
|
|
12
|
+
import Mermaid from '@/components/Mermaid';
|
|
13
|
+
import EventSideBar from '@/components/Sidebars/EventSidebar';
|
|
14
|
+
import NotFound from '@/components/NotFound';
|
|
15
|
+
import BreadCrumbs from '@/components/BreadCrumbs';
|
|
16
|
+
import SyntaxHighlighter from '@/components/SyntaxHighlighter';
|
|
17
|
+
|
|
18
|
+
import { getAllEvents, getEventByName } from '@/lib/events';
|
|
19
|
+
import { useUrl } from '@/hooks/EventCatalog';
|
|
20
|
+
|
|
21
|
+
import { MarkdownFile } from '@/types/index';
|
|
22
|
+
|
|
23
|
+
export interface EventsPageProps {
|
|
24
|
+
event: Event;
|
|
25
|
+
markdown: MarkdownFile;
|
|
26
|
+
notFound?: boolean;
|
|
27
|
+
loadedVersion?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const getComponents = ({ event, schema, examples }: any) => ({
|
|
31
|
+
code: ({ className, ...props }) => {
|
|
32
|
+
const match = /language-(\w+)/.exec(className || '');
|
|
33
|
+
|
|
34
|
+
return match ? (
|
|
35
|
+
<SyntaxHighlighter language={match[1]} {...props} />
|
|
36
|
+
) : (
|
|
37
|
+
<code className={className} {...props} />
|
|
38
|
+
);
|
|
39
|
+
},
|
|
40
|
+
Schema: ({ title = 'Event Schema' }: { title: string }) => {
|
|
41
|
+
if (!schema) return null;
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<section className="mt-8 xl:mt-10">
|
|
45
|
+
<h2 id="activity-title" className="text-lg font-medium text-gray-900 underline">
|
|
46
|
+
{title}
|
|
47
|
+
</h2>
|
|
48
|
+
<SyntaxHighlighter
|
|
49
|
+
language={schema.language}
|
|
50
|
+
showLineNumbers={false}
|
|
51
|
+
name={`${event.name} Schema (${schema.language})`}
|
|
52
|
+
>
|
|
53
|
+
{schema.snippet}
|
|
54
|
+
</SyntaxHighlighter>
|
|
55
|
+
</section>
|
|
56
|
+
);
|
|
57
|
+
},
|
|
58
|
+
Admonition,
|
|
59
|
+
EventExamples: (props) => <Examples {...props} examples={examples} showLineNumbers />,
|
|
60
|
+
Mermaid: ({ title }: { title: string }) => (
|
|
61
|
+
<div className="mx-auto w-full py-10">
|
|
62
|
+
{title && <h2 className="text-lg font-medium text-gray-900 underline">{title}</h2>}
|
|
63
|
+
<Mermaid source="event" data={event} rootNodeColor={getBackgroundColor(event.name)} />
|
|
64
|
+
</div>
|
|
65
|
+
),
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
export default function Events(props: EventsPageProps) {
|
|
69
|
+
const { event, markdown, loadedVersion, notFound } = props;
|
|
70
|
+
const { getEditUrl } = useUrl();
|
|
71
|
+
|
|
72
|
+
if (notFound) return <NotFound type="event" name={event.name} editUrl={editUrl} />;
|
|
73
|
+
|
|
74
|
+
const { name, summary, draft, schema, examples, version } = event;
|
|
75
|
+
const { lastModifiedDate } = markdown;
|
|
76
|
+
|
|
77
|
+
const pages = [
|
|
78
|
+
{ name: 'Events', href: '/events', current: false },
|
|
79
|
+
{ name, href: `/services/${name}`, current: true },
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
const mdxComponents = getComponents({ event, schema, examples });
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<>
|
|
86
|
+
<Head>
|
|
87
|
+
<title>
|
|
88
|
+
{name} v{version}
|
|
89
|
+
</title>
|
|
90
|
+
</Head>
|
|
91
|
+
<ContentView
|
|
92
|
+
// {...props}
|
|
93
|
+
title={name}
|
|
94
|
+
editUrl={getEditUrl(`/events/${name}/index.md`)}
|
|
95
|
+
subtitle={summary}
|
|
96
|
+
draft={draft}
|
|
97
|
+
lastModifiedDate={lastModifiedDate}
|
|
98
|
+
tags={[{ label: `v${version}` }]}
|
|
99
|
+
breadCrumbs={<BreadCrumbs pages={pages} />}
|
|
100
|
+
isOldVersion={loadedVersion !== 'latest'}
|
|
101
|
+
latestVersionUrl={`/events/${name}`}
|
|
102
|
+
version={loadedVersion}
|
|
103
|
+
sidebar={<EventSideBar event={event} loadedVersion={loadedVersion} />}
|
|
104
|
+
>
|
|
105
|
+
<MDXRemote {...markdown.source} components={mdxComponents} />
|
|
106
|
+
</ContentView>
|
|
107
|
+
</>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function getStaticProps({ params }) {
|
|
112
|
+
try {
|
|
113
|
+
const { event, markdown } = await getEventByName(params.name);
|
|
114
|
+
return {
|
|
115
|
+
props: {
|
|
116
|
+
event,
|
|
117
|
+
markdown,
|
|
118
|
+
loadedVersion: 'latest',
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
} catch (error) {
|
|
122
|
+
return {
|
|
123
|
+
props: {
|
|
124
|
+
notFound: true,
|
|
125
|
+
event: { name: params.name },
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export async function getStaticPaths() {
|
|
132
|
+
const events = getAllEvents();
|
|
133
|
+
const paths = events.map((event) => ({ params: { name: event.name } }));
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
paths,
|
|
137
|
+
fallback: 'blocking',
|
|
138
|
+
};
|
|
139
|
+
}
|
package/pages/events.tsx
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { Fragment, useState } from 'react';
|
|
2
|
+
import Head from 'next/head';
|
|
3
|
+
import type { Event, Service } from '@eventcatalog/types';
|
|
4
|
+
|
|
5
|
+
import Link from 'next/link';
|
|
6
|
+
|
|
7
|
+
import { Menu, Transition } from '@headlessui/react';
|
|
8
|
+
import { ChevronDownIcon } from '@heroicons/react/solid';
|
|
9
|
+
import EventGrid from '@/components/Grids/EventGrid';
|
|
10
|
+
import { getAllEvents, getUniqueServicesNamesFromEvents } from '@/lib/events';
|
|
11
|
+
|
|
12
|
+
function classNames(...classes) {
|
|
13
|
+
return classes.filter(Boolean).join(' ');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const sortOptions = [
|
|
17
|
+
{ name: 'Name', href: '#', current: true },
|
|
18
|
+
{ name: 'Version', href: '#', current: false },
|
|
19
|
+
{ name: 'Domains', href: '#', current: false },
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
export interface PageProps {
|
|
23
|
+
events: [Event];
|
|
24
|
+
services: [Service];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default function Page({ events, services }: PageProps) {
|
|
28
|
+
const filters = [
|
|
29
|
+
{
|
|
30
|
+
id: 'services',
|
|
31
|
+
name: 'Services',
|
|
32
|
+
options: services.map((service) => ({
|
|
33
|
+
value: service,
|
|
34
|
+
label: service,
|
|
35
|
+
checked: false,
|
|
36
|
+
})),
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
const [selectedFilters, setSelectedFilters] = useState({ services: [] });
|
|
41
|
+
const [showMermaidDiagrams, setShowMermaidDiagrams] = useState(false);
|
|
42
|
+
|
|
43
|
+
const handleFilterSelection = (option, type, event) => {
|
|
44
|
+
if (event.target.checked) {
|
|
45
|
+
const newFilters = selectedFilters[type].concat([option.value]);
|
|
46
|
+
setSelectedFilters({ ...selectedFilters, [type]: newFilters });
|
|
47
|
+
} else {
|
|
48
|
+
const newFilters = selectedFilters[type].filter((value) => value !== option.value);
|
|
49
|
+
setSelectedFilters({ ...selectedFilters, [type]: newFilters });
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
let eventsToRender = events;
|
|
54
|
+
|
|
55
|
+
if (selectedFilters.services.length > 0) {
|
|
56
|
+
// @ts-ignore
|
|
57
|
+
eventsToRender = eventsToRender.filter((event) => {
|
|
58
|
+
const { services: serviceFilters } = selectedFilters;
|
|
59
|
+
|
|
60
|
+
const hasConsumersFromFilters = event.consumers.some(
|
|
61
|
+
(consumerId) => serviceFilters.indexOf(consumerId) > -1
|
|
62
|
+
);
|
|
63
|
+
const hasProducersFromFilters = event.producers.some(
|
|
64
|
+
(producerId) => serviceFilters.indexOf(producerId) > -1
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
return hasConsumersFromFilters || hasProducersFromFilters;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<>
|
|
73
|
+
<Head>
|
|
74
|
+
<title>EventCatalog - All Events</title>
|
|
75
|
+
</Head>
|
|
76
|
+
<main className="max-w-7xl mx-auto min-h-screen px-4 md:px-0">
|
|
77
|
+
<div className="relative z-10 flex items-baseline justify-between pt-8 pb-6 border-b border-gray-200">
|
|
78
|
+
<h1 className="text-2xl font-extrabold tracking-tight text-gray-900">
|
|
79
|
+
Events ({events.length})
|
|
80
|
+
</h1>
|
|
81
|
+
|
|
82
|
+
<div className="flex items-center">
|
|
83
|
+
<Menu as="div" className="hidden relative text-left">
|
|
84
|
+
<div>
|
|
85
|
+
<Menu.Button className="group inline-flex justify-center text-sm font-medium text-gray-700 hover:text-gray-900">
|
|
86
|
+
Sort
|
|
87
|
+
<ChevronDownIcon
|
|
88
|
+
className="flex-shrink-0 -mr-1 ml-1 h-5 w-5 text-gray-400 group-hover:text-gray-500"
|
|
89
|
+
aria-hidden="true"
|
|
90
|
+
/>
|
|
91
|
+
</Menu.Button>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<Transition
|
|
95
|
+
as={Fragment}
|
|
96
|
+
enter="transition ease-out duration-100"
|
|
97
|
+
enterFrom="transform opacity-0 scale-95"
|
|
98
|
+
enterTo="transform opacity-100 scale-100"
|
|
99
|
+
leave="transition ease-in duration-75"
|
|
100
|
+
leaveFrom="transform opacity-100 scale-100"
|
|
101
|
+
leaveTo="transform opacity-0 scale-95"
|
|
102
|
+
>
|
|
103
|
+
<Menu.Items className="origin-top-right absolute right-0 mt-2 w-40 rounded-md shadow-2xl bg-white ring-1 ring-black ring-opacity-5 focus:outline-none">
|
|
104
|
+
<div className="py-1">
|
|
105
|
+
{sortOptions.map((option) => (
|
|
106
|
+
<Menu.Item key={option.name}>
|
|
107
|
+
{({ active }) => (
|
|
108
|
+
<a
|
|
109
|
+
href={option.href}
|
|
110
|
+
className={classNames(
|
|
111
|
+
option.current ? 'font-medium text-gray-900' : 'text-gray-500',
|
|
112
|
+
active ? 'bg-gray-100' : '',
|
|
113
|
+
'block px-4 py-2 text-sm'
|
|
114
|
+
)}
|
|
115
|
+
>
|
|
116
|
+
{option.name}
|
|
117
|
+
</a>
|
|
118
|
+
)}
|
|
119
|
+
</Menu.Item>
|
|
120
|
+
))}
|
|
121
|
+
</div>
|
|
122
|
+
</Menu.Items>
|
|
123
|
+
</Transition>
|
|
124
|
+
</Menu>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<section className="pt-6 pb-24">
|
|
129
|
+
<div className="grid grid-cols-4 gap-x-8 gap-y-10">
|
|
130
|
+
{/* Filters */}
|
|
131
|
+
<form className="hidden lg:block">
|
|
132
|
+
<span className="text-sm font-bold text-gray-900 mb-4 block">Events</span>
|
|
133
|
+
<ul className=" text-sm text-gray-600 space-y-4 pb-6 border-b border-gray-200 items-stretch">
|
|
134
|
+
{events.map((event) => (
|
|
135
|
+
<li key={event.name}>
|
|
136
|
+
<Link href={`/events/${event.name}`}>
|
|
137
|
+
<a>{event.name}</a>
|
|
138
|
+
</Link>
|
|
139
|
+
</li>
|
|
140
|
+
))}
|
|
141
|
+
</ul>
|
|
142
|
+
|
|
143
|
+
{filters.map((section: any) => (
|
|
144
|
+
<div key={section.id} className="border-b border-gray-200 py-6">
|
|
145
|
+
<h3 className="-my-3 flow-root">
|
|
146
|
+
<div className="py-3 bg-white w-full flex items-center justify-between text-sm text-gray-400 hover:text-gray-500">
|
|
147
|
+
<span className="font-medium text-gray-900">{section.name}</span>
|
|
148
|
+
</div>
|
|
149
|
+
</h3>
|
|
150
|
+
<div className="pt-6">
|
|
151
|
+
<div className="space-y-4">
|
|
152
|
+
{section.options.map((option, optionIdx) => (
|
|
153
|
+
<div key={option.value} className="flex items-center">
|
|
154
|
+
<input
|
|
155
|
+
id={`filter-${section.id}-${optionIdx}`}
|
|
156
|
+
name={`${section.id}[]`}
|
|
157
|
+
defaultValue={option.value}
|
|
158
|
+
type="checkbox"
|
|
159
|
+
onChange={(event) => handleFilterSelection(option, section.id, event)}
|
|
160
|
+
defaultChecked={option.checked}
|
|
161
|
+
className="h-4 w-4 border-gray-300 rounded text-gray-600 focus:ring-gray-500"
|
|
162
|
+
/>
|
|
163
|
+
<label
|
|
164
|
+
htmlFor={`filter-${section.id}-${optionIdx}`}
|
|
165
|
+
className="ml-3 text-sm text-gray-600"
|
|
166
|
+
>
|
|
167
|
+
{option.label}
|
|
168
|
+
</label>
|
|
169
|
+
</div>
|
|
170
|
+
))}
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
))}
|
|
175
|
+
|
|
176
|
+
<div className="border-b border-gray-200 py-6">
|
|
177
|
+
<h3 className="-my-3 flow-root">
|
|
178
|
+
<div className="py-3 bg-white w-full flex items-center justify-between text-sm text-gray-400 hover:text-gray-500">
|
|
179
|
+
<span className="font-medium text-gray-900">Features</span>
|
|
180
|
+
</div>
|
|
181
|
+
</h3>
|
|
182
|
+
<div className="pt-6">
|
|
183
|
+
<div className="space-y-4">
|
|
184
|
+
<div className="flex items-center">
|
|
185
|
+
<input
|
|
186
|
+
id="show-mermaid"
|
|
187
|
+
type="checkbox"
|
|
188
|
+
onChange={(e) => setShowMermaidDiagrams(e.target.checked)}
|
|
189
|
+
defaultChecked={showMermaidDiagrams}
|
|
190
|
+
className="h-4 w-4 border-gray-300 rounded text-gray-600 focus:ring-gray-500"
|
|
191
|
+
/>
|
|
192
|
+
<label htmlFor="show-mermaid" className="ml-3 text-sm text-gray-600">
|
|
193
|
+
Show Mermaid Diagrams
|
|
194
|
+
</label>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
</form>
|
|
200
|
+
|
|
201
|
+
<div className="col-span-4 lg:col-span-3">
|
|
202
|
+
<div>
|
|
203
|
+
<h2 className="text-gray-500 text-xs font-medium uppercase tracking-wide">
|
|
204
|
+
Events ({eventsToRender.length})
|
|
205
|
+
</h2>
|
|
206
|
+
<EventGrid events={eventsToRender} showMermaidDiagrams={showMermaidDiagrams} />
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
</section>
|
|
211
|
+
</main>
|
|
212
|
+
</>
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export const getServerSideProps = () => {
|
|
217
|
+
const events = getAllEvents();
|
|
218
|
+
const services = getUniqueServicesNamesFromEvents(events);
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
props: {
|
|
222
|
+
events,
|
|
223
|
+
// @ts-ignore
|
|
224
|
+
services: [...new Set(services)],
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
};
|
package/pages/index.tsx
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/* This example requires Tailwind CSS v2.0+ */
|
|
2
|
+
|
|
3
|
+
import Link from 'next/link';
|
|
4
|
+
import { useConfig } from '@/hooks/EventCatalog';
|
|
5
|
+
|
|
6
|
+
export default function Example() {
|
|
7
|
+
const { title, tagline, logo } = useConfig();
|
|
8
|
+
|
|
9
|
+
const logoToLoad = logo || { alt: 'EventCatalog Logo', src: '/logo.svg' };
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<main className="sm:bg-top md:min-h-screen bg-gradient-to-t from-blue-700 to-gray-800">
|
|
13
|
+
<div className="max-w-7xl mx-auto px-4 py-16 text-center sm:px-6 sm:py-24 lg:px-8 lg:py-48">
|
|
14
|
+
<img
|
|
15
|
+
src={logoToLoad.src}
|
|
16
|
+
alt={logoToLoad.alt}
|
|
17
|
+
style={{ height: '85px' }}
|
|
18
|
+
className="mx-auto"
|
|
19
|
+
/>
|
|
20
|
+
<h1 className="mt-2 text-4xl font-extrabold text-white tracking-tight sm:text-5xl">
|
|
21
|
+
{title}
|
|
22
|
+
</h1>
|
|
23
|
+
{tagline && <p className="mt-2 text-lg font-medium text-white">{tagline}</p>}
|
|
24
|
+
<div className="mt-5 max-w-md mx-auto sm:flex sm:justify-center md:mt-8">
|
|
25
|
+
<div className="rounded-md shadow">
|
|
26
|
+
<Link href="/events">
|
|
27
|
+
<a className="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 md:py-4 md:text-lg md:px-10">
|
|
28
|
+
Explore Events
|
|
29
|
+
</a>
|
|
30
|
+
</Link>
|
|
31
|
+
</div>
|
|
32
|
+
<div className="mt-3 rounded-md shadow sm:mt-0 sm:ml-3">
|
|
33
|
+
<a
|
|
34
|
+
href="https://eventcatalog.dev"
|
|
35
|
+
target="_blank"
|
|
36
|
+
className="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-indigo-600 bg-white hover:bg-gray-50 md:py-4 md:text-lg md:px-10"
|
|
37
|
+
rel="noreferrer"
|
|
38
|
+
>
|
|
39
|
+
Getting Started
|
|
40
|
+
</a>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
<div className="mt-6 space-x-5" />
|
|
44
|
+
</div>
|
|
45
|
+
</main>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import ReactDOMServer from 'react-dom/server';
|
|
3
|
+
import Head from 'next/head';
|
|
4
|
+
|
|
5
|
+
import dynamic from 'next/dynamic';
|
|
6
|
+
|
|
7
|
+
import { getAllEvents, getUniqueServicesNamesFromEvents } from '@/lib/events';
|
|
8
|
+
|
|
9
|
+
const ForceGraph3D = dynamic(
|
|
10
|
+
() => import('react-force-graph-3d').then((module) => module.default),
|
|
11
|
+
{ ssr: false }
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
function NodeElement({ node: { id } }: { node: { id: string } }) {
|
|
15
|
+
return <div className={`text-sm text-center p-1 rounded-md `}>{id}</div>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const graph = ({ events, services }) => {
|
|
19
|
+
const eventNodes = events.map(({ name: event }) => ({ id: event, group: 1, type: 'event' }));
|
|
20
|
+
const serviceNodes = services.map((service) => ({ id: service, group: 2, type: 'service' }));
|
|
21
|
+
|
|
22
|
+
// Create all links
|
|
23
|
+
const links = events.reduce((nodes, event) => {
|
|
24
|
+
const { consumers = [], producers = [], name } = event;
|
|
25
|
+
const consumerNodes = consumers.map((consumer) => ({ source: name, target: consumer }));
|
|
26
|
+
const producerNodes = producers.map((producer) => ({ source: producer, target: name }));
|
|
27
|
+
return nodes.concat(consumerNodes).concat(producerNodes);
|
|
28
|
+
}, []);
|
|
29
|
+
|
|
30
|
+
const data = { nodes: eventNodes.concat(serviceNodes), links };
|
|
31
|
+
|
|
32
|
+
if (typeof window === 'undefined') {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// @ts-ignore
|
|
37
|
+
const extraRenderers = [new window.THREE.CSS2DRenderer()];
|
|
38
|
+
return (
|
|
39
|
+
<div className="min-h-screen ">
|
|
40
|
+
<Head>
|
|
41
|
+
<title>EventCatalog - 3D Node Graph</title>
|
|
42
|
+
</Head>
|
|
43
|
+
<ForceGraph3D
|
|
44
|
+
extraRenderers={extraRenderers}
|
|
45
|
+
graphData={data}
|
|
46
|
+
nodeAutoColorBy="group"
|
|
47
|
+
nodeRelSize={9}
|
|
48
|
+
nodeThreeObject={(node) => {
|
|
49
|
+
const nodeEl = document.createElement('div');
|
|
50
|
+
nodeEl.innerHTML = ReactDOMServer.renderToString(<NodeElement node={node} />);
|
|
51
|
+
node.height = '100px';
|
|
52
|
+
nodeEl.style.color = node.color;
|
|
53
|
+
// @ts-ignore
|
|
54
|
+
return new THREE.CSS2DObject(nodeEl);
|
|
55
|
+
}}
|
|
56
|
+
nodeThreeObjectExtend
|
|
57
|
+
enableNodeDrag={false}
|
|
58
|
+
nodeOpacity={0.2}
|
|
59
|
+
linkDirectionalParticles={2}
|
|
60
|
+
linkDirectionalParticleWidth={2}
|
|
61
|
+
linkDirectionalParticleColor={() => 'rgba(236, 72, 153, 1)'}
|
|
62
|
+
/>
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export default graph;
|
|
68
|
+
|
|
69
|
+
export const getServerSideProps = () => {
|
|
70
|
+
const events = getAllEvents();
|
|
71
|
+
const services = getUniqueServicesNamesFromEvents(events);
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
props: {
|
|
75
|
+
events,
|
|
76
|
+
// @ts-ignore
|
|
77
|
+
services: [...new Set(services)],
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import Head from 'next/head';
|
|
2
|
+
import { MDXRemote } from 'next-mdx-remote';
|
|
3
|
+
import { Service } from '@eventcatalog/types';
|
|
4
|
+
import { editUrl } from '../../eventcatalog.config.js';
|
|
5
|
+
import ContentView from '@/components/ContentView';
|
|
6
|
+
import { getAllServices, getServiceByName } from '@/lib/services';
|
|
7
|
+
|
|
8
|
+
import Admonition from '@/components/Mdx/Admonition';
|
|
9
|
+
import Mermaid from '@/components/Mermaid';
|
|
10
|
+
import ServiceSidebar from '@/components/Sidebars/ServiceSidebar';
|
|
11
|
+
import BreadCrumbs from '@/components/BreadCrumbs';
|
|
12
|
+
import NotFound from '@/components/NotFound';
|
|
13
|
+
import getBackgroundColor from '@/utils/random-bg';
|
|
14
|
+
import { useUrl } from '@/hooks/EventCatalog';
|
|
15
|
+
|
|
16
|
+
import { MarkdownFile } from '@/types/index';
|
|
17
|
+
|
|
18
|
+
interface ServicesPageProps {
|
|
19
|
+
service: Service;
|
|
20
|
+
markdown: MarkdownFile;
|
|
21
|
+
notFound?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function MermaidComponent({ title, service }: { title: string; service: Service }) {
|
|
25
|
+
return (
|
|
26
|
+
<div className="mx-auto w-full py-10">
|
|
27
|
+
{title && <h2 className="text-lg font-medium text-gray-900 underline">{title}</h2>}
|
|
28
|
+
<Mermaid source="service" data={service} rootNodeColor={getBackgroundColor(service.name)} />
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const getComponents = (service) => ({
|
|
34
|
+
Admonition,
|
|
35
|
+
Mermaid: ({ title }: { title: string }) => <MermaidComponent service={service} title={title} />,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export default function Services(props: ServicesPageProps) {
|
|
39
|
+
const { service, markdown, notFound } = props;
|
|
40
|
+
const { getEditUrl } = useUrl();
|
|
41
|
+
|
|
42
|
+
if (notFound) return <NotFound type="service" name={service.name} editUrl={editUrl} />;
|
|
43
|
+
|
|
44
|
+
const { name, summary, draft } = service;
|
|
45
|
+
const { lastModifiedDate } = markdown;
|
|
46
|
+
|
|
47
|
+
const mdxComponents = getComponents(service);
|
|
48
|
+
|
|
49
|
+
const pages = [
|
|
50
|
+
{ name: 'Services', href: '/services', current: false },
|
|
51
|
+
{ name, href: `/services/${name}`, current: true },
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<>
|
|
56
|
+
<Head>
|
|
57
|
+
<title>{name}</title>
|
|
58
|
+
</Head>
|
|
59
|
+
<ContentView
|
|
60
|
+
title={name}
|
|
61
|
+
editUrl={getEditUrl(`/services/${name}/index.md`)}
|
|
62
|
+
subtitle={summary}
|
|
63
|
+
draft={draft}
|
|
64
|
+
lastModifiedDate={lastModifiedDate}
|
|
65
|
+
breadCrumbs={<BreadCrumbs pages={pages} homePath="/services" />}
|
|
66
|
+
sidebar={<ServiceSidebar service={service} />}
|
|
67
|
+
>
|
|
68
|
+
{/* @ts-ignore */}
|
|
69
|
+
<MDXRemote {...markdown.source} components={mdxComponents} />
|
|
70
|
+
</ContentView>
|
|
71
|
+
</>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function getStaticProps({ params }) {
|
|
76
|
+
try {
|
|
77
|
+
const { service, markdown } = await getServiceByName(params.name);
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
props: {
|
|
81
|
+
service,
|
|
82
|
+
markdown,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
} catch (error) {
|
|
86
|
+
return {
|
|
87
|
+
props: {
|
|
88
|
+
notFound: true,
|
|
89
|
+
service: { name: params.name },
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export async function getStaticPaths() {
|
|
96
|
+
const services = getAllServices();
|
|
97
|
+
const paths = services.map((service) => ({ params: { name: service.name } }));
|
|
98
|
+
return {
|
|
99
|
+
paths,
|
|
100
|
+
fallback: 'blocking',
|
|
101
|
+
};
|
|
102
|
+
}
|