@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.
Files changed (62) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/README.md +11 -0
  3. package/bin/eventcatalog.js +125 -0
  4. package/components/BreadCrumbs.tsx +50 -0
  5. package/components/ContentView.tsx +127 -0
  6. package/components/Footer.tsx +38 -0
  7. package/components/Grids/EventGrid.tsx +89 -0
  8. package/components/Grids/ServiceGrid.tsx +70 -0
  9. package/components/Header.tsx +59 -0
  10. package/components/Mdx/Admonition.tsx +33 -0
  11. package/components/Mdx/Examples.tsx +77 -0
  12. package/components/Mermaid/index.tsx +47 -0
  13. package/components/NotFound/index.tsx +44 -0
  14. package/components/Sidebars/EventSidebar.tsx +202 -0
  15. package/components/Sidebars/ServiceSidebar.tsx +198 -0
  16. package/components/SyntaxHighlighter.tsx +34 -0
  17. package/hooks/EventCatalog.tsx +35 -0
  18. package/lib/__tests__/assets/events/AddedItemToCart/index.md +19 -0
  19. package/lib/__tests__/assets/events/EmailSent/index.md +15 -0
  20. package/lib/__tests__/assets/events/EventWithSchemaAndExamples/examples/Basic.cs +31 -0
  21. package/lib/__tests__/assets/events/EventWithSchemaAndExamples/examples/Basic.js +1 -0
  22. package/lib/__tests__/assets/events/EventWithSchemaAndExamples/index.md +8 -0
  23. package/lib/__tests__/assets/events/EventWithSchemaAndExamples/schema.json +4 -0
  24. package/lib/__tests__/assets/events/EventWithVersions/index.md +10 -0
  25. package/lib/__tests__/assets/events/EventWithVersions/versioned/0.0.1/index.md +10 -0
  26. package/lib/__tests__/assets/services/Email Platform/index.md +17 -0
  27. package/lib/__tests__/events.spec.ts +294 -0
  28. package/lib/__tests__/file-reader.spec.ts +57 -0
  29. package/lib/__tests__/graphs.spec.ts +62 -0
  30. package/lib/__tests__/services.spec.ts +144 -0
  31. package/lib/events.ts +221 -0
  32. package/lib/file-reader.ts +52 -0
  33. package/lib/graphs.ts +33 -0
  34. package/lib/services.ts +72 -0
  35. package/next-env.d.ts +5 -0
  36. package/next.config.js +3 -0
  37. package/package.json +52 -0
  38. package/pages/_app.tsx +49 -0
  39. package/pages/api/event/[name]/download.js +25 -0
  40. package/pages/events/[name]/logs.tsx +170 -0
  41. package/pages/events/[name]/v/[version].tsx +19 -0
  42. package/pages/events/[name].tsx +139 -0
  43. package/pages/events.tsx +227 -0
  44. package/pages/index.tsx +47 -0
  45. package/pages/overview.tsx +80 -0
  46. package/pages/services/[name].tsx +102 -0
  47. package/pages/services.tsx +123 -0
  48. package/pages/users/[id].tsx +83 -0
  49. package/postcss.config.js +6 -0
  50. package/public/favicon.ico +0 -0
  51. package/public/logo-random.svg +114 -0
  52. package/public/logo.svg +44 -0
  53. package/public/opengraph.png +0 -0
  54. package/scripts/__tests__/assets/eventcatalog.config.js +33 -0
  55. package/scripts/__tests__/generate.spec.ts +39 -0
  56. package/scripts/generate.js +28 -0
  57. package/styles/Home.module.css +116 -0
  58. package/styles/globals.css +48 -0
  59. package/tailwind.config.js +16 -0
  60. package/tsconfig.json +38 -0
  61. package/types/index.ts +7 -0
  62. package/utils/random-bg.ts +13 -0
@@ -0,0 +1,77 @@
1
+ import React, { useState } from 'react';
2
+ import SyntaxHighlighter from '@/components/SyntaxHighlighter';
3
+
4
+ function classNames(...classes) {
5
+ return classes.filter(Boolean).join(' ');
6
+ }
7
+
8
+ interface ExampleProps {
9
+ title?: string;
10
+ description?: string;
11
+ examples: any;
12
+ showLineNumbers?: boolean;
13
+ }
14
+
15
+ function Examples({
16
+ title = 'Examples',
17
+ description,
18
+ examples = [],
19
+ showLineNumbers,
20
+ }: ExampleProps) {
21
+ const tabs = examples.map((example, index) => ({
22
+ name: example.name || `Example ${index + 1}`,
23
+ content: example.snippet,
24
+ description: example.description,
25
+ langugage: example.langugage,
26
+ }));
27
+
28
+ const [selectedTab, setSelectedTab] = useState(tabs[0]);
29
+
30
+ const handleTabSelection = (tab: string) => {
31
+ setSelectedTab(tab);
32
+ };
33
+
34
+ return (
35
+ <div className="my-5 examples">
36
+ <div className="">
37
+ <h2 className="text-lg font-medium text-gray-700 underline">{title}</h2>
38
+ {description && <p className="text-md font-medium text-gray-700">{description}</p>}
39
+
40
+ <div>
41
+ <nav className="-mb-px flex" aria-label="Tabs">
42
+ {tabs.map((tab) => {
43
+ const isSelected = tab.name === selectedTab.name;
44
+ return (
45
+ <button
46
+ type="button"
47
+ key={tab.name}
48
+ onClick={() => handleTabSelection(tab)}
49
+ className={classNames(
50
+ isSelected
51
+ ? 'border-green-500 text-green-600 selected'
52
+ : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300',
53
+ 'whitespace-nowrap py-4 border-b-2 font-medium text-sm important:no-underline px-8 hover:bg-gray-50 transition'
54
+ )}
55
+ aria-current={isSelected ? 'page' : undefined}
56
+ >
57
+ {tab.name}
58
+ <span className="block text-xs mt-">({tab.langugage})</span>
59
+ </button>
60
+ );
61
+ })}
62
+ </nav>
63
+ <div className="my-4">
64
+ <SyntaxHighlighter language={selectedTab.langugage} showLineNumbers={showLineNumbers}>
65
+ {selectedTab.content}
66
+ </SyntaxHighlighter>
67
+ {selectedTab.langugage && (
68
+ <span className="-mb-2 block text-xs text-right font-bold">{selectedTab.name}</span>
69
+ )}
70
+ </div>
71
+ </div>
72
+ </div>
73
+ </div>
74
+ );
75
+ }
76
+
77
+ export default Examples;
@@ -0,0 +1,47 @@
1
+ import React, { useEffect } from 'react';
2
+ import mermaid from 'mermaid';
3
+ import { Service, Event } from '@eventcatalog/types';
4
+ import { buildMermaidFlowChartForEvent, buildMermaidFlowChartForService } from '@/lib/graphs';
5
+
6
+ mermaid.initialize({
7
+ startOnLoad: true,
8
+ theme: 'forest',
9
+ securityLevel: 'loose',
10
+ flowchart: {
11
+ useMaxWidth: false,
12
+ width: '1000px',
13
+ },
14
+ themeCSS: `
15
+ .node {
16
+ filter: drop-shadow( 3px 3px 2px rgba(0, 0, 0, .2))
17
+ }
18
+ .mermaid svg {
19
+ width: 10000px
20
+ }
21
+ .node rect {
22
+ fill: white
23
+ }
24
+ `,
25
+ fontFamily: 'Fira Code',
26
+ width: '100%',
27
+ });
28
+
29
+ interface MermaidProps {
30
+ data: Event | Service;
31
+ source: 'event' | 'service';
32
+ rootNodeColor?: string;
33
+ }
34
+
35
+ function Mermaid({ data, source = 'event', rootNodeColor }: MermaidProps) {
36
+ const mermaidChart =
37
+ source === 'event'
38
+ ? buildMermaidFlowChartForEvent(data as Event, rootNodeColor)
39
+ : buildMermaidFlowChartForService(data as Service, rootNodeColor);
40
+
41
+ useEffect(() => {
42
+ mermaid.contentLoaded();
43
+ }, []);
44
+ return <div className="mermaid">{mermaidChart}</div>;
45
+ }
46
+
47
+ export default Mermaid;
@@ -0,0 +1,44 @@
1
+ import { DocumentAddIcon } from '@heroicons/react/solid';
2
+ import url from 'url';
3
+
4
+ interface NotFoundProps {
5
+ type: 'service' | 'event';
6
+ name: string;
7
+ editUrl: string;
8
+ }
9
+
10
+ export default function Example(props: NotFoundProps) {
11
+ const { type, name, editUrl } = props;
12
+
13
+ const urlToAddPage = url.resolve(editUrl, `${type}/${name}`);
14
+
15
+ return (
16
+ <main className="min-h-full bg-cover bg-top sm:bg-top h-screen">
17
+ <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">
18
+ <p className="text-sm font-semibold text-gray-700 text-opacity-50 uppercase tracking-wide blur-xl">
19
+ Failed to find {type}
20
+ </p>
21
+ <h1 className="mt-2 text-4xl font-extrabold text-gray-900 tracking-tight sm:text-5xl">
22
+ Missing Documentation
23
+ </h1>
24
+ <p className="mt-2 text-lg font-medium text-gray-700 text-opacity-50">
25
+ Documentation for {type} <span className="underline">{name}</span> is missing!
26
+ </p>
27
+ <p className="mt-4 text-xs text-gray-400">
28
+ Help the eco-system and add the documentation for others ❤️{' '}
29
+ </p>
30
+ <div className="mt-12">
31
+ <a
32
+ href={urlToAddPage}
33
+ target="_blank"
34
+ className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-black bg-opacity-75 "
35
+ rel="noreferrer"
36
+ >
37
+ <DocumentAddIcon className="-ml-1 mr-2 h-5 w-5" aria-hidden="true" />
38
+ Add <span className="underline px-1">{name}</span> documentation
39
+ </a>
40
+ </div>
41
+ </div>
42
+ </main>
43
+ );
44
+ }
@@ -0,0 +1,202 @@
1
+ import React from 'react';
2
+ import Link from 'next/link';
3
+ import { CubeIcon, DownloadIcon } from '@heroicons/react/outline';
4
+ import type { Event } from '@eventcatalog/types';
5
+ import fileDownload from 'js-file-download';
6
+ import { useUser } from '@/hooks/EventCatalog';
7
+
8
+ interface EventSideBarProps {
9
+ event: Event;
10
+ loadedVersion?: string;
11
+ }
12
+
13
+ function EventSideBar({ event, loadedVersion }: EventSideBarProps) {
14
+ const { getUserById } = useUser();
15
+
16
+ const { name: eventName, owners, producers, consumers, historicVersions } = event;
17
+
18
+ const handleDownload = async () => {
19
+ try {
20
+ const res = await fetch(`/api/event/${event.name}/download`);
21
+ if (res.status === 404) throw new Error('Failed to find file');
22
+ const data = await res.text();
23
+ fileDownload(data, event.name);
24
+ } catch (error) {
25
+ // TODO: Maybe better error experince
26
+ console.error(error);
27
+ }
28
+ };
29
+
30
+ return (
31
+ <aside className="hidden xl:block xl:pl-8">
32
+ <h2 className="sr-only">Details</h2>
33
+
34
+ <div className="pt-6 py-6 space-y-8">
35
+ <div>
36
+ <h2 className="text-sm font-medium text-gray-500">
37
+ <CubeIcon className="h-5 w-5 text-green-400 inline-block mr-2" aria-hidden="true" />
38
+ Producers
39
+ </h2>
40
+ <ul className="mt-2 leading-8">
41
+ {producers.map((producer) => (
42
+ <li className="inline mr-1" key={producer}>
43
+ <Link href={`/services/${producer}`}>
44
+ <a className="relative inline-flex items-center rounded-full border border-gray-300 px-3 py-0.5">
45
+ <div className="absolute flex-shrink-0 flex items-center justify-center">
46
+ <span
47
+ className="h-1.5 w-1.5 rounded-full bg-green-500 animate animate-pulse"
48
+ aria-hidden="true"
49
+ />
50
+ </div>
51
+ <div className="ml-3.5 text-sm font-medium text-gray-900">{producer}</div>
52
+ </a>
53
+ </Link>
54
+ </li>
55
+ ))}
56
+ </ul>
57
+ </div>
58
+ </div>
59
+ <div className="border-t border-gray-200 py-6 space-y-8">
60
+ <div>
61
+ <h2 className="text-sm font-medium text-gray-500">
62
+ <CubeIcon className="h-5 w-5 text-indigo-400 inline-block mr-2" aria-hidden="true" />
63
+ Consumers
64
+ </h2>
65
+ <ul className="mt-2 leading-8">
66
+ {consumers.map((consumer) => (
67
+ <li className="inline" key={consumer}>
68
+ <Link href={`/services/${consumer}`}>
69
+ <a
70
+ href="#"
71
+ className="relative inline-flex items-center rounded-full border border-gray-300 px-3 py-0.5"
72
+ >
73
+ <div className="absolute flex-shrink-0 flex items-center justify-center">
74
+ <span
75
+ className="h-1.5 w-1.5 rounded-full bg-indigo-500 animate animate-pulse"
76
+ aria-hidden="true"
77
+ />
78
+ </div>
79
+ <div className="ml-3.5 text-sm font-medium text-gray-900">{consumer}</div>
80
+ </a>
81
+ </Link>
82
+ </li>
83
+ ))}
84
+ </ul>
85
+ </div>
86
+ </div>
87
+ {historicVersions.length > 0 && (
88
+ <div className="border-t border-gray-200 py-6">
89
+ <div>
90
+ <h2 className="text-sm font-medium text-gray-500">Event Versions</h2>
91
+ <ul className="mt-2 leading-8 text-left text-blue-500">
92
+ <li className="text-sm inline ">
93
+ <Link href={`/events/${eventName}`}>
94
+ <a>
95
+ <span
96
+ className={`inline-flex mr-2 items-center px-2.5 py-0.5 rounded-full text-xs font-medium -top-0.5 relative ${
97
+ loadedVersion === 'latest'
98
+ ? 'bg-blue-400 text-white shadow-md font-bold underline'
99
+ : 'bg-blue-100 text-blue-800'
100
+ }`}
101
+ >
102
+ Latest
103
+ </span>
104
+ </a>
105
+ </Link>
106
+ </li>
107
+
108
+ {historicVersions.map((version) => {
109
+ const isLoadedVersion = loadedVersion === version;
110
+ const styles = isLoadedVersion
111
+ ? 'bg-blue-400 text-white shadow-md font-bold underline'
112
+ : 'bg-blue-100 text-blue-800';
113
+ return (
114
+ <li className="text-sm inline ">
115
+ <Link href={`/events/${eventName}/v/${version}`}>
116
+ <a>
117
+ <span
118
+ className={`inline-flex mr-2 items-center px-2.5 py-0.5 rounded-full text-xs font-medium -top-0.5 relative ${styles}`}
119
+ >
120
+ v{version}
121
+ </span>
122
+ </a>
123
+ </Link>
124
+ </li>
125
+ );
126
+ })}
127
+ </ul>
128
+ </div>
129
+ </div>
130
+ )}
131
+ {/* <div className="border-t border-gray-200 py-6 space-y-8">
132
+ <div>
133
+ <h2 className="text-sm font-medium text-gray-500">
134
+ <MapIcon className="h-5 w-5 text-red-400 inline-block mr-2" aria-hidden="true" />
135
+ Domains
136
+ </h2>
137
+ <ul role="list" className="mt-2 leading-8">
138
+ {domains.map((domain) => {
139
+ return (
140
+ <li className="inline" key={domain}>
141
+ <a
142
+ href="#"
143
+ className="relative inline-flex items-center rounded-full border border-gray-300 px-3 py-0.5"
144
+ >
145
+ <div className="absolute flex-shrink-0 flex items-center justify-center">
146
+ <span className="h-1.5 w-1.5 rounded-full bg-red-500" aria-hidden="true" />
147
+ </div>
148
+ <div className="ml-3.5 text-sm font-medium text-gray-900">{domain}</div>
149
+ </a>{' '}
150
+ </li>
151
+ )
152
+ })}
153
+ </ul>
154
+ </div>
155
+ </div> */}
156
+ <div className="border-t border-gray-200 py-6 space-y-8">
157
+ <div>
158
+ <h2 className="text-sm font-medium text-gray-500">Event Owners</h2>
159
+ <ul className="mt-4 leading-8 space-y-2">
160
+ {owners.map((id) => {
161
+ const user = getUserById(id);
162
+
163
+ if (!user) return null;
164
+
165
+ return (
166
+ <li className="flex justify-start" key={id}>
167
+ <Link href={`/users/${id}`}>
168
+ <a className="flex items-center space-x-3">
169
+ <div className="flex-shrink-0">
170
+ <img className="h-5 w-5 rounded-full" src={user.avatarUrl} alt="" />
171
+ </div>
172
+ <div className="text-sm font-medium text-gray-900">{user.name}</div>
173
+ </a>
174
+ </Link>
175
+ </li>
176
+ );
177
+ })}
178
+ </ul>
179
+ </div>
180
+ </div>
181
+ <div className="border-t border-gray-200 py-6 space-y-1">
182
+ <button
183
+ type="button"
184
+ className="hidden w-full md:inline-flex h-10 justify-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-200 bg-gray-800 hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-900"
185
+ onClick={() => handleDownload()}
186
+ >
187
+ <DownloadIcon className="-ml-1 mr-2 h-5 w-5 text-gray-200" aria-hidden="true" />
188
+ <span>Download Schema</span>
189
+ </button>
190
+ {historicVersions.length > 0 && (
191
+ <Link href={`/events/${eventName}/logs`}>
192
+ <a className="hidden w-full md:inline-flex h-10 justify-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-800 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-200">
193
+ <span>View Changes</span>
194
+ </a>
195
+ </Link>
196
+ )}
197
+ </div>
198
+ </aside>
199
+ );
200
+ }
201
+
202
+ export default EventSideBar;
@@ -0,0 +1,198 @@
1
+ import React from 'react';
2
+ import Link from 'next/link';
3
+ import type { Service } from '@eventcatalog/types';
4
+
5
+ import { CubeIcon, TagIcon } from '@heroicons/react/outline';
6
+ import { useUser } from '@/hooks/EventCatalog';
7
+ import getBackgroundColor from '@/utils/random-bg';
8
+
9
+ const tailwindBgs = ['purple', 'pink', 'green', 'yellow', 'blue', 'indigo'];
10
+
11
+ interface ServiceSideBarProps {
12
+ service: Service;
13
+ }
14
+
15
+ function ServiceSidebar({ service }: ServiceSideBarProps) {
16
+ const { getUserById } = useUser();
17
+
18
+ const { owners, subscribes, publishes, repository, tags = [] } = service;
19
+ const { language, url: repositoryUrl } = repository;
20
+
21
+ let languages = [];
22
+
23
+ if (language) {
24
+ languages = Array.isArray(language) ? language : [language];
25
+ }
26
+
27
+ let trimmedUrl = '';
28
+
29
+ if (repositoryUrl) {
30
+ trimmedUrl = repositoryUrl.replace(/(^\w+:|^)\/\//, '');
31
+ }
32
+
33
+ return (
34
+ <aside className="hidden xl:block xl:pl-8">
35
+ <h2 className="sr-only">Details</h2>
36
+
37
+ <div className="pt-6 py-6 space-y-8">
38
+ <div>
39
+ <h2 className="text-sm font-medium text-gray-500">
40
+ <CubeIcon className="h-5 w-5 text-indigo-400 inline-block mr-2" aria-hidden="true" />
41
+ Publishes Events ({publishes.length})
42
+ </h2>
43
+ <ul className="mt-2 leading-8">
44
+ {publishes.map((event) => (
45
+ <li className="inline" key={event.name}>
46
+ <Link href={`/events/${event.name}`}>
47
+ <a
48
+ href="#"
49
+ className="relative inline-flex items-center rounded-full border border-gray-300 px-3 py-0.5"
50
+ >
51
+ <div className="absolute flex-shrink-0 flex items-center justify-center">
52
+ <span
53
+ className="h-1.5 w-1.5 rounded-full bg-indigo-500 animate animate-pulse"
54
+ aria-hidden="true"
55
+ />
56
+ </div>
57
+ <div className="ml-3.5 text-sm font-medium text-gray-900">{event.name}</div>
58
+ </a>
59
+ </Link>
60
+ </li>
61
+ ))}
62
+ </ul>
63
+ </div>
64
+ </div>
65
+ <div className="border-t border-gray-200 py-6 space-y-8">
66
+ <div>
67
+ <h2 className="text-sm font-medium text-gray-500">
68
+ <CubeIcon className="h-5 w-5 text-green-400 inline-block mr-2" aria-hidden="true" />
69
+ Subscribes to Events ({subscribes.length})
70
+ </h2>
71
+ <ul className="mt-2 leading-8">
72
+ {subscribes.map((event) => (
73
+ <li className="inline" key={event.name}>
74
+ <Link href={`/events/${event.name}`}>
75
+ <a className="relative inline-flex items-center rounded-full border border-gray-300 px-3 py-0.5">
76
+ <div className="absolute flex-shrink-0 flex items-center justify-center">
77
+ <span
78
+ className="h-1.5 w-1.5 rounded-full bg-green-500 animate animate-pulse"
79
+ aria-hidden="true"
80
+ />
81
+ </div>
82
+ <div className="ml-3.5 text-sm font-medium text-gray-900">{event.name}</div>
83
+ </a>
84
+ </Link>
85
+ </li>
86
+ ))}
87
+ </ul>
88
+ </div>
89
+ </div>
90
+
91
+ <div className="border-t border-gray-200 py-6 space-y-8">
92
+ <div>
93
+ <h2 className="text-sm font-medium text-gray-500">Service Owners ({owners.length})</h2>
94
+ <ul className="mt-4 leading-8 space-y-2">
95
+ {owners.map((owner) => {
96
+ const user = getUserById(owner);
97
+
98
+ if (!user) return null;
99
+
100
+ return (
101
+ <li className="flex justify-start" key={user.id}>
102
+ <Link href={`/users/${user.id}`}>
103
+ <a className="flex items-center space-x-3">
104
+ <div className="flex-shrink-0">
105
+ <img className="h-5 w-5 rounded-full" src={user.avatarUrl} alt="" />
106
+ </div>
107
+ <div className="text-sm font-medium text-gray-900">{user.name}</div>
108
+ </a>
109
+ </Link>
110
+ </li>
111
+ );
112
+ })}
113
+ </ul>
114
+ </div>
115
+ </div>
116
+ {repository?.url && (
117
+ <div className="border-t border-gray-200 py-6 space-y-8">
118
+ <div className="space-y-3">
119
+ <h2 className="text-sm font-medium text-gray-500">Repository</h2>
120
+ <ul className=" leading-8 space-y-2">
121
+ <li className="flex justify-start">
122
+ <a
123
+ href={repository?.url}
124
+ target="_blank"
125
+ className="flex items-center space-x-3 text-blue-600 underline text-sm"
126
+ rel="noreferrer"
127
+ >
128
+ {trimmedUrl}
129
+ </a>
130
+ </li>
131
+ </ul>
132
+ </div>
133
+ </div>
134
+ )}
135
+ {languages.length > 0 && (
136
+ <div className="border-t border-gray-200 py-6 space-y-8">
137
+ <div className="space-y-3">
138
+ <h2 className="text-sm font-medium text-gray-500">Language</h2>
139
+ {languages.map((value) => (
140
+ <div className="relative flex items-center mt-2">
141
+ <div className="absolute flex-shrink-0 flex items-center justify-center">
142
+ <span
143
+ className="h-2 w-2 rounded-full"
144
+ aria-hidden="true"
145
+ style={{ background: getBackgroundColor(value) }}
146
+ />
147
+ </div>
148
+ <div className="ml-3.5 text-sm font-medium text-gray-900">{value}</div>
149
+ </div>
150
+ ))}
151
+ </div>
152
+ </div>
153
+ )}
154
+ {tags.length > 0 && (
155
+ <div className="border-t border-gray-200 py-6 space-y-8">
156
+ <div>
157
+ <h2 className="text-sm font-medium text-gray-500">
158
+ <TagIcon className="h-5 w-5 text-gray-400 inline-block mr-2" aria-hidden="true" />
159
+ Tags
160
+ </h2>
161
+ <div className="mt-3 space-y-2">
162
+ {tags.map(({ label, url }, index) => {
163
+ const color = tailwindBgs[index % tailwindBgs.length];
164
+
165
+ if (url) {
166
+ return (
167
+ <a
168
+ href={url}
169
+ className="inline-block underline"
170
+ target="_blank"
171
+ rel="noreferrer"
172
+ >
173
+ <span
174
+ className={`underline inline-block mr-2 items-center px-2.5 py-0.5 rounded-full text-xs font-medium -top-0.5 relative bg-${color}-100 text-${color}-800`}
175
+ >
176
+ {label}
177
+ </span>
178
+ </a>
179
+ );
180
+ }
181
+
182
+ return (
183
+ <span
184
+ className={`inline-block mr-2 items-center px-2.5 py-0.5 rounded-full text-xs font-medium -top-0.5 relative bg-${color}-100 text-${color}-800`}
185
+ >
186
+ {label}
187
+ </span>
188
+ );
189
+ })}
190
+ </div>
191
+ </div>
192
+ </div>
193
+ )}
194
+ </aside>
195
+ );
196
+ }
197
+
198
+ export default ServiceSidebar;
@@ -0,0 +1,34 @@
1
+ import React, { useState } from 'react';
2
+
3
+ import codeStyle from 'react-syntax-highlighter/dist/cjs/styles/prism/dracula';
4
+ import { Prism as PrismSyntaxHighlighter } from 'react-syntax-highlighter';
5
+ import copy from 'copy-text-to-clipboard';
6
+
7
+ function SyntaxHighlighter({ language, name = '', ...props }: any) {
8
+ const [showCopied, setShowCopied] = useState(false);
9
+
10
+ const handleCopyCode = () => {
11
+ copy(props.children);
12
+ setShowCopied(true);
13
+ setTimeout(() => setShowCopied(false), 2000);
14
+ };
15
+
16
+ const regex = /\\n/g;
17
+ return (
18
+ <div className="relative group">
19
+ <button
20
+ type="button"
21
+ onClick={handleCopyCode}
22
+ className="absolute top-2 right-5 text-sm bg-gray-700 text-white rounded-md py-1 px-4 transform transition opacity-0 group-hover:opacity-100"
23
+ >
24
+ {showCopied ? 'Copied' : 'Copy'}
25
+ </button>
26
+ <PrismSyntaxHighlighter style={codeStyle} language={language} {...props} wrapLines>
27
+ {props.children.replace(regex, '\n')}
28
+ </PrismSyntaxHighlighter>
29
+ {name && <span className="-mb-2 block text-xs text-right font-bold">{name}</span>}
30
+ </div>
31
+ );
32
+ }
33
+
34
+ export default SyntaxHighlighter;
@@ -0,0 +1,35 @@
1
+ import React, { useContext, ReactNode } from 'react';
2
+ import type { EventCataLogConfig, User } from '@eventcatalog/types';
3
+ import path from 'path';
4
+ import defaultConfig from '../eventcatalog.config';
5
+
6
+ export const Context = React.createContext<EventCataLogConfig | null>(defaultConfig);
7
+
8
+ export function EventCatalogContextProvider({ children }: { children: ReactNode }): JSX.Element {
9
+ return <Context.Provider value={defaultConfig}>{children}</Context.Provider>;
10
+ }
11
+
12
+ export const useConfig = () => useContext<EventCataLogConfig>(Context);
13
+
14
+ export const useUser = () => {
15
+ const config = useConfig();
16
+
17
+ const getUserById = (id): User => {
18
+ const users = config.users || [];
19
+ return users.find((user) => user.id === id);
20
+ };
21
+
22
+ return {
23
+ getUserById,
24
+ };
25
+ };
26
+
27
+ export const useUrl = () => {
28
+ const config = useConfig();
29
+
30
+ const getEditUrl = (url: string) => path.join(config.editUrl, url);
31
+
32
+ return {
33
+ getEditUrl,
34
+ };
35
+ };
@@ -0,0 +1,19 @@
1
+ ---
2
+ name: AddedItemToCart
3
+ version: 0.0.1
4
+ summary: |
5
+ Holds information about the cusomer and product when they add an item to the cart.
6
+ producers:
7
+ - Shopping API
8
+ - Application API
9
+ consumers:
10
+ - Customer Portal
11
+ domains:
12
+ - Shopping
13
+ owners:
14
+ - dboyne
15
+ - mSmith
16
+ draft: true
17
+ ---
18
+
19
+ # Testing
@@ -0,0 +1,15 @@
1
+ ---
2
+ name: EmailSent
3
+ version: 0.0.1
4
+ summary: |
5
+ Tells us when an email has been sent
6
+ producers:
7
+ - Email Platform
8
+ owners:
9
+ - dboyne
10
+ - mSmith
11
+ ---
12
+
13
+ Duis mollis quam enim, feugiat porta mi porta non. In lacus nulla, gravida nec sagittis vel, sagittis id tellus. Vestibulum maximus velit eget massa pulvinar ornare. In vel libero nulla. Aliquam a leo risus. Donec bibendum velit non nulla sollicitudin lacinia. Vestibulum imperdiet nunc eget neque sagittis, eget volutpat purus ornare. Mauris malesuada finibus pretium. Vestibulum suscipit tortor sit amet dolor tempor cursus. Nunc ac felis accumsan.
14
+
15
+ <Mermaid />