@eventcatalog/core 2.4.0 → 2.5.1

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 (44) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +8 -1
  3. package/bin/dist/eventcatalog.cjs +5 -4
  4. package/bin/dist/eventcatalog.js +5 -4
  5. package/package.json +8 -6
  6. package/scripts/catalog-to-astro-content-directory.js +32 -1
  7. package/scripts/default-files-for-collections/flows.md +11 -0
  8. package/scripts/default-files-for-collections/pages.md +1 -0
  9. package/scripts/watcher.js +1 -2
  10. package/src/components/DocsNavigation.astro +5 -1
  11. package/src/components/MDX/Flow/Flow.astro +63 -0
  12. package/src/components/MDX/NodeGraph/NodeGraph.astro +12 -0
  13. package/src/components/MDX/NodeGraph/NodeGraph.tsx +75 -33
  14. package/src/components/MDX/NodeGraph/Nodes/ExternalSystem.tsx +79 -0
  15. package/src/components/MDX/NodeGraph/Nodes/Service.tsx +0 -12
  16. package/src/components/MDX/NodeGraph/Nodes/Step.tsx +69 -0
  17. package/src/components/MDX/NodeGraph/Nodes/User.tsx +79 -0
  18. package/src/components/MDX/components.tsx +2 -0
  19. package/src/components/SideBars/DomainSideBar.astro +1 -1
  20. package/src/components/Tables/columns/FlowTableColumns.tsx +82 -0
  21. package/src/components/Tables/columns/index.tsx +3 -0
  22. package/src/content/config.ts +65 -5
  23. package/src/layouts/DiscoverLayout.astro +11 -1
  24. package/src/layouts/VisualiserLayout.astro +33 -22
  25. package/src/pages/discover/[type]/index.astro +3 -0
  26. package/src/pages/docs/[type]/[id]/[version]/index.astro +12 -3
  27. package/src/pages/docs/teams/[id]/index.astro +14 -0
  28. package/src/pages/docs/users/[id]/index.astro +23 -4
  29. package/src/pages/visualiser/[type]/[id]/[version]/index.astro +9 -2
  30. package/src/types/index.ts +1 -1
  31. package/src/utils/collections/util.ts +22 -0
  32. package/src/utils/commands/node-graph.ts +13 -2
  33. package/src/utils/config/catalog.ts +1 -0
  34. package/src/utils/domains/domains.ts +3 -5
  35. package/src/utils/domains/node-graph.ts +28 -9
  36. package/src/utils/events/node-graph.ts +13 -2
  37. package/src/utils/flows/flows.ts +59 -0
  38. package/src/utils/flows/node-graph.ts +153 -0
  39. package/src/utils/services/node-graph.ts +16 -5
  40. package/src/utils/services/services.ts +3 -26
  41. package/src/utils/teams.ts +7 -1
  42. package/src/utils/users.ts +8 -0
  43. package/src/utils/versions/versions.ts +26 -0
  44. package/tailwind.config.mjs +1 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @eventcatalog/core
2
2
 
3
+ ## 2.5.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 2bbccf8: feat(core): added new MDX component for Flows
8
+
9
+ ## 2.5.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 067fd89: feat(core): added flows to eventcatalog
14
+ - c336807: feat(core): only latest versions are now shown in visualizer
15
+ - 20c2cba: fix(core): fixing search not working after builds
16
+ - 7476fa6: fix(core): nodegraphs for domains now work with semver versions
17
+ - 737380a: feat(core): added ability to see how many domains a team and users own
18
+ - a1490d3: chore(core): updated astro versions
19
+ feat(core): visuzlier arrows stroke width increased
20
+ - 58d6a2d: chore(core): fixing default for flows
21
+
3
22
  ## 2.4.0
4
23
 
5
24
  ### Minor Changes
package/README.md CHANGED
@@ -113,7 +113,7 @@ You can see the markdown files that generated the website in the GitHub repo und
113
113
 
114
114
  Interested in collaborating with us? Our offerings include dedicated support, priority assistance, feature development, custom integrations, and more.
115
115
 
116
- Find more details on our [enterprise plan](https://eventcatalog.dev/enterprise).
116
+ Find more details on our [services page](https://eventcatalog.dev/services).
117
117
 
118
118
  # Looking for v1?
119
119
 
@@ -129,6 +129,13 @@ If you have any questions, features or issues please raise any issue or pull req
129
129
 
130
130
  You can find the [contributing guidelines here](https://eventcatalog.dev/docs/contributing/overview).
131
131
 
132
+ ## Running the project locally
133
+
134
+ 1. Clone the repo
135
+ 1. Install required dependencies `npm run i`
136
+ 1. Run the command `npm run start:catalog`
137
+ - This will start the catalog found in `/examples` repo, locally on your machine
138
+
132
139
  [license-badge]: https://img.shields.io/github/license/event-catalog/eventcatalog.svg?color=yellow
133
140
  [license]: https://github.com/event-catalog/eventcatalog/blob/main/LICENSE
134
141
  [prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square
@@ -3095,9 +3095,6 @@ var ensureDir = (dir2) => {
3095
3095
  }
3096
3096
  };
3097
3097
  var copyCore = () => {
3098
- if (import_fs.default.existsSync(core)) {
3099
- import_fs.default.rmSync(core, { recursive: true });
3100
- }
3101
3098
  ensureDir(core);
3102
3099
  import_fs.default.cpSync(eventCatalogDir, core, {
3103
3100
  recursive: true,
@@ -3106,13 +3103,17 @@ var copyCore = () => {
3106
3103
  }
3107
3104
  });
3108
3105
  };
3109
- program2.command("dev").description("Run development server of EventCatalog").option("-d, --debug", "Output EventCatalog application information into your terminal").action((options) => {
3106
+ var clearCore = () => {
3107
+ if (import_fs.default.existsSync(core)) import_fs.default.rmSync(core, { recursive: true });
3108
+ };
3109
+ program2.command("dev").description("Run development server of EventCatalog").option("-d, --debug", "Output EventCatalog application information into your terminal").option("--force-recreate", "Recreate the eventcatalog-core directory", false).action((options) => {
3110
3110
  console.log("Setting up EventCatalog....");
3111
3111
  if (options.debug) {
3112
3112
  console.log("Debug mode enabled");
3113
3113
  console.log("PROJECT_DIR", dir);
3114
3114
  console.log("CATALOG_DIR", core);
3115
3115
  }
3116
+ if (options.forceRecreate) clearCore();
3116
3117
  copyCore();
3117
3118
  copyFolder((0, import_node_path.join)(dir, "public"), (0, import_node_path.join)(core, "public"));
3118
3119
  copyFile((0, import_node_path.join)(dir, "eventcatalog.config.js"), (0, import_node_path.join)(core, "eventcatalog.config.js"));
@@ -3062,9 +3062,6 @@ var ensureDir = (dir2) => {
3062
3062
  }
3063
3063
  };
3064
3064
  var copyCore = () => {
3065
- if (fs.existsSync(core)) {
3066
- fs.rmSync(core, { recursive: true });
3067
- }
3068
3065
  ensureDir(core);
3069
3066
  fs.cpSync(eventCatalogDir, core, {
3070
3067
  recursive: true,
@@ -3073,13 +3070,17 @@ var copyCore = () => {
3073
3070
  }
3074
3071
  });
3075
3072
  };
3076
- program2.command("dev").description("Run development server of EventCatalog").option("-d, --debug", "Output EventCatalog application information into your terminal").action((options) => {
3073
+ var clearCore = () => {
3074
+ if (fs.existsSync(core)) fs.rmSync(core, { recursive: true });
3075
+ };
3076
+ program2.command("dev").description("Run development server of EventCatalog").option("-d, --debug", "Output EventCatalog application information into your terminal").option("--force-recreate", "Recreate the eventcatalog-core directory", false).action((options) => {
3077
3077
  console.log("Setting up EventCatalog....");
3078
3078
  if (options.debug) {
3079
3079
  console.log("Debug mode enabled");
3080
3080
  console.log("PROJECT_DIR", dir);
3081
3081
  console.log("CATALOG_DIR", core);
3082
3082
  }
3083
+ if (options.forceRecreate) clearCore();
3083
3084
  copyCore();
3084
3085
  copyFolder(join(dir, "public"), join(core, "public"));
3085
3086
  copyFile(join(dir, "eventcatalog.config.js"), join(core, "eventcatalog.config.js"));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@eventcatalog/core",
3
3
  "type": "module",
4
- "version": "2.4.0",
4
+ "version": "2.5.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -30,10 +30,10 @@
30
30
  "format:diff": "prettier --config .prettierrc --list-different \"**/*.{js,jsx,ts,tsx,json,astro}\""
31
31
  },
32
32
  "dependencies": {
33
- "@astrojs/check": "^0.7.0",
34
- "@astrojs/markdown-remark": "^5.1.0",
35
- "@astrojs/mdx": "^3.1.0",
36
- "@astrojs/react": "^3.5.0",
33
+ "@astrojs/check": "^0.9.2",
34
+ "@astrojs/markdown-remark": "^5.2.0",
35
+ "@astrojs/mdx": "^3.1.3",
36
+ "@astrojs/react": "^3.6.2",
37
37
  "@astrojs/tailwind": "^5.1.0",
38
38
  "@changesets/cli": "^2.27.5",
39
39
  "@headlessui/react": "^2.0.3",
@@ -44,13 +44,14 @@
44
44
  "@tanstack/react-table": "^8.17.3",
45
45
  "@types/dagre": "^0.7.52",
46
46
  "@types/node": "^20.14.2",
47
- "astro": "^4.10.1",
47
+ "astro": "^4.14.2",
48
48
  "astro-expressive-code": "^0.35.2",
49
49
  "astro-pagefind": "^1.5.0",
50
50
  "astro-seo": "^0.8.4",
51
51
  "dagre": "^0.8.5",
52
52
  "glob": "^10.4.1",
53
53
  "html-to-image": "^1.11.11",
54
+ "lodash.merge": "4.6.2",
54
55
  "mermaid": "^10.9.1",
55
56
  "rapidoc": "^9.3.4",
56
57
  "react": "^18.3.1",
@@ -65,6 +66,7 @@
65
66
  },
66
67
  "devDependencies": {
67
68
  "@parcel/watcher": "^2.4.1",
69
+ "@types/lodash.merge": "4.6.9",
68
70
  "@types/react": "^18.3.3",
69
71
  "@types/react-dom": "^18.3.0",
70
72
  "commander": "^12.1.0",
@@ -22,11 +22,12 @@ const ensureDirSync = async (filePath) => {
22
22
  }
23
23
  };
24
24
 
25
- const copyFiles = async ({ source, target, catalogFilesDir, pathToMarkdownFiles, pathToAllFiles, type }) => {
25
+ const copyFiles = async ({ source, target, catalogFilesDir, pathToMarkdownFiles, pathToAllFiles, type, ignore = null }) => {
26
26
  // Find all the event files
27
27
  const markdownFiles = await glob(pathToMarkdownFiles, {
28
28
  nodir: true,
29
29
  windowsPathsNoEscape: os.platform() == 'win32',
30
+ ignore: ignore,
30
31
  });
31
32
  const files = await glob(pathToAllFiles, {
32
33
  ignore: {
@@ -186,6 +187,11 @@ export const catalogToAstro = async (source, astroContentDir, catalogFilesDir) =
186
187
  path.join(source, 'services/**/**/changelog.md'),
187
188
  ],
188
189
  pathToAllFiles: [path.join(source, 'services/**'), path.join(source, 'domains/**/services/**')],
190
+ ignore: [
191
+ path.join(source, 'services/**/events/**'),
192
+ path.join(source, 'services/**/commands/**'),
193
+ path.join(source, 'services/**/flows/**'),
194
+ ],
189
195
  type: 'services',
190
196
  });
191
197
 
@@ -196,9 +202,34 @@ export const catalogToAstro = async (source, astroContentDir, catalogFilesDir) =
196
202
  catalogFilesDir,
197
203
  pathToMarkdownFiles: [path.join(source, 'domains/**/**/index.md'), path.join(source, 'domains/**/**/changelog.md')],
198
204
  pathToAllFiles: [path.join(source, 'domains/**')],
205
+ ignore: [
206
+ path.join(source, 'domains/**/services/**'),
207
+ path.join(source, 'domains/**/commands/**'),
208
+ path.join(source, 'domains/**/events/**'),
209
+ path.join(source, 'domains/**/flows/**'),
210
+ ],
199
211
  type: 'domains',
200
212
  });
201
213
 
214
+ // // Copy all the flow files over
215
+ await copyFiles({
216
+ source,
217
+ target: astroContentDir,
218
+ catalogFilesDir,
219
+ pathToMarkdownFiles: [
220
+ path.join(source, 'flows/**/**/index.md'),
221
+ path.join(source, 'flows/**/**/changelog.md'),
222
+ path.join(source, 'services/**/flows/**/index.md'),
223
+ path.join(source, 'domains/**/flows/**/index.md'),
224
+ ],
225
+ pathToAllFiles: [
226
+ path.join(source, 'flows/**'),
227
+ path.join(source, 'services/**/flows/**'),
228
+ path.join(source, 'domains/**/flows/**'),
229
+ ],
230
+ type: 'flows',
231
+ });
232
+
202
233
  // // Copy all the users
203
234
  await copyFiles({
204
235
  source,
@@ -0,0 +1,11 @@
1
+ ---
2
+ id: empty
3
+ steps:
4
+ - id: "empty"
5
+ title: "empty"
6
+ name: empty
7
+ version: 0.0.1
8
+ hidden: true
9
+ ---
10
+
11
+ <!-- Do not delete this file, required for EC, you an ignore this file -->
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  id: index
3
3
  slug: index
4
+ hidden: true
4
5
  ---
5
6
 
6
7
  <!-- Do not delete this file, required for EC, you an ignore this file -->
@@ -11,7 +11,7 @@ const catalogDirectory = process.env.CATALOG_DIR;
11
11
 
12
12
  const contentPath = path.join(catalogDirectory, 'src', 'content');
13
13
 
14
- const watchList = ['domains', 'commands', 'events', 'services', 'teams', 'users', 'pages', 'components'];
14
+ const watchList = ['domains', 'commands', 'events', 'services', 'teams', 'users', 'pages', 'components', 'flows'];
15
15
  // const absoluteWatchList = watchList.map((item) => path.join(projectDirectory, item));
16
16
 
17
17
  // confirm folders exist before watching them
@@ -62,7 +62,6 @@ for (let item of [...verifiedWatchList]) {
62
62
 
63
63
  // IF directory remove it
64
64
  if (type === 'delete') {
65
- console.log('eventPath', eventPath);
66
65
  fs.rmSync(newPath);
67
66
  }
68
67
  }
@@ -3,6 +3,7 @@ import { getCommands } from '@utils/commands';
3
3
  import { getDomains } from '@utils/domains/domains';
4
4
  import { getEvents } from '@utils/events';
5
5
  import { getServices } from '@utils/services/services';
6
+ import { getFlows } from '@utils/flows/flows';
6
7
  import { getTeams } from '@utils/teams';
7
8
  import { getUsers } from '@utils/users';
8
9
  import config, { type CatalogConfig } from '@eventcatalog';
@@ -12,19 +13,21 @@ const events = await getEvents({ getAllVersions: false });
12
13
  const commands = await getCommands({ getAllVersions: false });
13
14
  const services = await getServices({ getAllVersions: false });
14
15
  const domains = await getDomains({ getAllVersions: false });
16
+ const flows = await getFlows({ getAllVersions: false });
15
17
  const teams = await getTeams();
16
18
  const users = await getUsers();
17
19
 
18
20
  const messages = [...events, ...commands];
19
21
 
20
22
  // @ts-ignore for large catalogs https://github.com/event-catalog/eventcatalog/issues/552
21
- const allData = [...domains, ...services, ...messages, ...teams, ...users];
23
+ const allData = [...domains, ...services, ...messages, ...flows, ...teams, ...users];
22
24
 
23
25
  const eventCatalogConfig = config as CatalogConfig;
24
26
  const {
25
27
  services: servicesConfig,
26
28
  domains: domainsConfig,
27
29
  messages: messagesConfig,
30
+ flows: flowsConfig,
28
31
  teams: teamsConfig,
29
32
  users: usersConfig,
30
33
  showPageHeadings = true,
@@ -38,6 +41,7 @@ const visibleCollections: { [key: string]: boolean } = {
38
41
  events: getConfigValue(messagesConfig, 'visible', true),
39
42
  commands: getConfigValue(messagesConfig, 'visible', true),
40
43
  domains: getConfigValue(domainsConfig, 'visible', true),
44
+ flows: getConfigValue(flowsConfig, 'visible', true),
41
45
  services: getConfigValue(servicesConfig, 'visible', true),
42
46
  teams: getConfigValue(teamsConfig, 'visible', true),
43
47
  users: getConfigValue(usersConfig, 'visible', true),
@@ -0,0 +1,63 @@
1
+ ---
2
+ import { getFlows } from '@utils/flows/flows';
3
+ import { getNodesAndEdges } from '@utils/flows/node-graph';
4
+ import Admonition from '@components/MDX/Admonition';
5
+ import NodeGraph from '../NodeGraph/NodeGraph';
6
+ import { getVersionFromCollection } from '@utils/versions/versions';
7
+
8
+ const { id, version = 'latest', maxHeight, includeKey = true } = Astro.props;
9
+
10
+ // Find the flow for the given id and version
11
+ const flows = await getFlows();
12
+ const flowCollection = getVersionFromCollection(flows, id, version) || [];
13
+ const flow = flowCollection[0];
14
+
15
+ // const flow = flows.find((flow) => flow.data.id === id && flow.data.version === version);
16
+
17
+ const { nodes, edges } = await getNodesAndEdges({
18
+ id: id,
19
+ version: flow.data.version,
20
+ });
21
+ ---
22
+
23
+ {
24
+ !flow && (
25
+ <Admonition type="warning">
26
+ <>
27
+ <span class="block font-bold">{`<Flow/>`} failed to load</span>
28
+ <span class="block">
29
+ Tried to load flow id: {id} with version {version}. Make sure you have this flow defined in your project.
30
+ </span>
31
+ </>
32
+ </Admonition>
33
+ )
34
+ }
35
+
36
+ <div
37
+ class="h-[30em] my-6 mb-12 w-full relative border border-gray-200 rounded-md"
38
+ id={`${id}-portal`}
39
+ style={{
40
+ maxHeight: maxHeight ? `${maxHeight}em` : `30em`,
41
+ }}
42
+ >
43
+ </div>
44
+
45
+ <div>
46
+ <NodeGraph
47
+ id={id}
48
+ nodes={nodes}
49
+ edges={edges}
50
+ hrefLabel={'View in visualizer'}
51
+ href={`/visualiser/flows/${id}/${version}`}
52
+ linkTo={'visualiser'}
53
+ includeKey={includeKey}
54
+ footerLabel=`Flow diagram - ${flow.data.name} - v(${flow.data.version})`
55
+ client:load
56
+ />
57
+ </div>
58
+
59
+ <style is:global>
60
+ .react-flow__attribution {
61
+ display: none;
62
+ }
63
+ </style>
@@ -4,6 +4,7 @@ import { getNodesAndEdges as getNodesAndEdgesForService } from '@utils/services/
4
4
  import { getNodesAndEdges as getNodesAndEdgesForEvent } from '@utils/events/node-graph';
5
5
  import { getNodesAndEdges as getNodesAndEdgesForCommand } from '@utils/commands/node-graph';
6
6
  import { getNodesAndEdges as getNodesAndEdgesForDomain } from '@utils/domains/node-graph';
7
+ import { getNodesAndEdges as getNodesAndEdgesForFlows } from '@utils/flows/node-graph';
7
8
 
8
9
  interface Props {
9
10
  id: string;
@@ -65,6 +66,17 @@ if (collection === 'domains') {
65
66
  nodes = eventNodes;
66
67
  edges = eventEdges;
67
68
  }
69
+
70
+ if (collection === 'flows') {
71
+ const { nodes: eventNodes, edges: eventEdges } = await getNodesAndEdgesForFlows({
72
+ id: id,
73
+ version,
74
+ mode,
75
+ });
76
+
77
+ nodes = eventNodes;
78
+ edges = eventEdges;
79
+ }
68
80
  ---
69
81
 
70
82
  <div>
@@ -13,7 +13,10 @@ import ReactFlow, {
13
13
  import 'reactflow/dist/style.css';
14
14
  import ServiceNode from './Nodes/Service';
15
15
  import EventNode from './Nodes/Event';
16
+ import UserNode from './Nodes/User';
17
+ import StepNode from './Nodes/Step';
16
18
  import CommandNode from './Nodes/Command';
19
+ import ExternalSystemNode from './Nodes/ExternalSystem';
17
20
  import type { CollectionEntry } from 'astro:content';
18
21
  import { navigate } from 'astro:transitions/client';
19
22
  import type { CollectionTypes } from '@types';
@@ -28,6 +31,7 @@ interface Props {
28
31
  includeBackground?: boolean;
29
32
  includeControls?: boolean;
30
33
  linkTo: 'docs' | 'visualiser';
34
+ includeKey?: boolean;
31
35
  }
32
36
 
33
37
  const getDocUrlForCollection = (collectionItem: CollectionEntry<CollectionTypes>) => {
@@ -38,10 +42,27 @@ const getVisualiserUrlForCollection = (collectionItem: CollectionEntry<Collectio
38
42
  };
39
43
 
40
44
  // const NodeGraphBuilder = ({ title, subtitle, includeBackground = true, includeControls = true }: Props) => {
41
- const NodeGraphBuilder = ({ nodes: initialNodes, edges, title, includeBackground = true, linkTo = 'docs' }: Props) => {
42
- const [nodes, _, onNodesChange] = useNodesState(initialNodes);
43
-
44
- const nodeTypes = useMemo(() => ({ services: ServiceNode, events: EventNode, commands: CommandNode }), []);
45
+ const NodeGraphBuilder = ({
46
+ nodes: initialNodes,
47
+ edges,
48
+ title,
49
+ includeBackground = true,
50
+ linkTo = 'docs',
51
+ includeKey = true,
52
+ }: Props) => {
53
+ const nodeTypes = useMemo(
54
+ () => ({
55
+ services: ServiceNode,
56
+ events: EventNode,
57
+ commands: CommandNode,
58
+ step: StepNode,
59
+ user: UserNode,
60
+ actor: UserNode,
61
+ externalSystem: ExternalSystemNode,
62
+ }),
63
+ []
64
+ );
65
+ const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
45
66
  const nodeOrigin = [0.5, 0.5];
46
67
 
47
68
  const handleNodeClick = (_: any, node: Node) => {
@@ -60,7 +81,6 @@ const NodeGraphBuilder = ({ nodes: initialNodes, edges, title, includeBackground
60
81
  nodes={nodes}
61
82
  edges={edges}
62
83
  fitView
63
- nodesDraggable
64
84
  onNodesChange={onNodesChange}
65
85
  connectionLineType={ConnectionLineType.SmoothStep}
66
86
  // @ts-ignore
@@ -77,25 +97,27 @@ const NodeGraphBuilder = ({ nodes: initialNodes, edges, title, includeBackground
77
97
  <DownloadButton filename={title} addPadding={!!title} />
78
98
  {includeBackground && <Background color="#bbb" gap={16} />}
79
99
  {includeBackground && <Controls />}
80
- <Panel position="bottom-right">
81
- <div className=" bg-white font-light px-4 text-[14px] shadow-md py-1 rounded-md">
82
- <span className="font-bold">Key</span>
83
- <ul>
84
- <li className="flex space-x-2 items-center text-[12px]">
85
- <span className="w-2 h-2 bg-orange-500 block" />
86
- <span className="block">Event</span>
87
- </li>
88
- <li className="flex space-x-2 items-center text-[12px]">
89
- <span className="w-2 h-2 bg-pink-500 block" />
90
- <span className="block">Service</span>
91
- </li>
92
- <li className="flex space-x-2 items-center text-[12px]">
93
- <span className="w-2 h-2 bg-blue-500 block" />
94
- <span className="block">Command</span>
95
- </li>
96
- </ul>
97
- </div>
98
- </Panel>
100
+ {includeKey && (
101
+ <Panel position="bottom-right">
102
+ <div className=" bg-white font-light px-4 text-[14px] shadow-md py-1 rounded-md">
103
+ <span className="font-bold">Key</span>
104
+ <ul>
105
+ <li className="flex space-x-2 items-center text-[12px]">
106
+ <span className="w-2 h-2 bg-orange-500 block" />
107
+ <span className="block">Event</span>
108
+ </li>
109
+ <li className="flex space-x-2 items-center text-[12px]">
110
+ <span className="w-2 h-2 bg-pink-500 block" />
111
+ <span className="block">Service</span>
112
+ </li>
113
+ <li className="flex space-x-2 items-center text-[12px]">
114
+ <span className="w-2 h-2 bg-blue-500 block" />
115
+ <span className="block">Command</span>
116
+ </li>
117
+ </ul>
118
+ </div>
119
+ </Panel>
120
+ )}
99
121
  </ReactFlow>
100
122
  );
101
123
  };
@@ -108,9 +130,21 @@ interface NodeGraphProps {
108
130
  nodes: Node[];
109
131
  edges: Edge[];
110
132
  linkTo: 'docs' | 'visualiser';
133
+ includeKey?: boolean;
134
+ footerLabel?: string;
111
135
  }
112
136
 
113
- const NodeGraph = ({ id, nodes, edges, title, href, linkTo = 'docs', hrefLabel = 'Open in visualiser' }: NodeGraphProps) => {
137
+ const NodeGraph = ({
138
+ id,
139
+ nodes,
140
+ edges,
141
+ title,
142
+ href,
143
+ linkTo = 'docs',
144
+ hrefLabel = 'Open in visualizer',
145
+ includeKey = true,
146
+ footerLabel,
147
+ }: NodeGraphProps) => {
114
148
  const [elem, setElem] = useState(null);
115
149
 
116
150
  useEffect(() => {
@@ -124,15 +158,23 @@ const NodeGraph = ({ id, nodes, edges, title, href, linkTo = 'docs', hrefLabel =
124
158
  <div>
125
159
  {createPortal(
126
160
  <ReactFlowProvider>
127
- <NodeGraphBuilder edges={edges} nodes={nodes} title={title} linkTo={linkTo} />
161
+ <NodeGraphBuilder edges={edges} nodes={nodes} title={title} linkTo={linkTo} includeKey={includeKey} />
162
+
163
+ <div className="flex justify-between">
164
+ {footerLabel && (
165
+ <div className="py-2 w-full text-left ">
166
+ <span className=" text-sm no-underline py-2 text-gray-300">{footerLabel}</span>
167
+ </div>
168
+ )}
128
169
 
129
- {href && (
130
- <div className="py-2 w-full text-right">
131
- <a className=" text-sm no-underline py-2 text-gray-800 hover:text-purple-500" href={href}>
132
- {hrefLabel} &rarr;
133
- </a>
134
- </div>
135
- )}
170
+ {href && (
171
+ <div className="py-2 w-full text-right">
172
+ <a className=" text-sm no-underline py-2 text-gray-800 hover:text-purple-500" href={href}>
173
+ {hrefLabel} &rarr;
174
+ </a>
175
+ </div>
176
+ )}
177
+ </div>
136
178
  </ReactFlowProvider>,
137
179
  elem
138
180
  )}
@@ -0,0 +1,79 @@
1
+ import { ServerIcon } from '@heroicons/react/16/solid';
2
+ import { GlobeAmericasIcon } from '@heroicons/react/20/solid';
3
+ import type { CollectionEntry } from 'astro:content';
4
+ import { Handle } from 'reactflow';
5
+
6
+ interface Data {
7
+ label: string;
8
+ bgColor: string;
9
+ color: string;
10
+ mode: 'simple' | 'full';
11
+ step: { id: string; title: string; summary: string; externalSystem: { name: string; summary?: string; url?: string } };
12
+ showTarget?: boolean;
13
+ showSource?: boolean;
14
+ }
15
+
16
+ function classNames(...classes: any) {
17
+ return classes.filter(Boolean).join(' ');
18
+ }
19
+
20
+ export default function ExternalSystemNode({ data, sourcePosition, targetPosition }: any) {
21
+ const { mode, step, showTarget = true, showSource = true } = data as Data;
22
+ const { externalSystem } = step;
23
+ const { name, summary, url } = externalSystem;
24
+
25
+ return (
26
+ <div
27
+ className={classNames(
28
+ `w-full rounded-md border flex justify-start bg-white text-black border-pink-500`,
29
+ mode === 'full' ? 'min-h-[7em]' : 'min-h-[2em]'
30
+ )}
31
+ >
32
+ <div
33
+ className={classNames(
34
+ 'bg-gradient-to-b from-pink-500 to-pink-700 relative flex items-center w-5 justify-center rounded-l-sm text-red-100-500',
35
+ `border-r-[1px] border-pink-500`
36
+ )}
37
+ >
38
+ <GlobeAmericasIcon className="w-4 h-4 opacity-90 text-white absolute top-1 " />
39
+ {mode === 'full' && (
40
+ <span className="rotate -rotate-90 w-1/2 text-center absolute bottom-1 text-[9px] text-white font-bold uppercase tracking-[3px] ">
41
+ External
42
+ </span>
43
+ )}
44
+ </div>
45
+ <div className="p-1 min-w-60 max-w-[min-content]">
46
+ {showTarget && <Handle type="target" position={targetPosition} />}
47
+ {showSource && <Handle type="source" position={sourcePosition} />}
48
+ <div className={classNames(mode === 'full' ? `border-b border-gray-200` : '')}>
49
+ <div className="h-full ">
50
+ <span className="text-sm font-bold pb-0.5 block w-full">{name}</span>
51
+ {mode === 'simple' && (
52
+ <div className="w-full text-right">
53
+ <span className=" w-full text-[10px] text-gray-500 font-light block pt-0.5 pb-0.5 ">External System</span>
54
+ </div>
55
+ )}
56
+ </div>
57
+ </div>
58
+ {mode === 'full' && (
59
+ <div className="divide-y divide-gray-200 ">
60
+ <div className="leading-3 py-1">
61
+ <span className="text-[8px] font-light">{summary}</span>
62
+ </div>
63
+
64
+ {url && (
65
+ <div className="grid grid-cols-2 gap-x-4 py-1">
66
+ <span className="text-xs" style={{ fontSize: '0.2em' }}>
67
+ URL:{' '}
68
+ <a href={url} target="_blank" className="text-purple-500 underline">
69
+ {url}
70
+ </a>
71
+ </span>
72
+ </div>
73
+ )}
74
+ </div>
75
+ )}
76
+ </div>
77
+ </div>
78
+ );
79
+ }
@@ -21,9 +21,6 @@ export default function ServiceNode({ data, sourcePosition, targetPosition }: an
21
21
 
22
22
  const { version, owners = [], sends = [], receives = [], name } = service.data;
23
23
 
24
- const renderTarget = showTarget;
25
- const renderSource = showSource;
26
-
27
24
  return (
28
25
  <div className={classNames('w-full rounded-md border flex justify-start bg-white text-black border-pink-500')}>
29
26
  <div
@@ -42,21 +39,12 @@ export default function ServiceNode({ data, sourcePosition, targetPosition }: an
42
39
  <div className="p-1 min-w-60 max-w-[min-content]">
43
40
  {showTarget && <Handle type="target" position={targetPosition} />}
44
41
  {showSource && <Handle type="source" position={sourcePosition} />}
45
- {/* {mode === 'full' && (
46
- <div className="flex justify-end font-thin space-x-1 border-b border-orange-200 pb-0.5 " style={{ fontSize: '0.2em' }}>
47
- <span>Service</span>
48
- <ServerIcon className="w-2 h-2 opacity-90" />
49
- </div>
50
- )} */}
51
42
  <div className={classNames(mode === 'full' ? `border-b border-gray-200` : '')}>
52
43
  <span className="text-xs font-bold block pt-0.5 pb-0.5">{name}</span>
53
44
  <div className="flex justify-between">
54
45
  <span className="text-[10px] font-light block pt-0.5 pb-0.5 ">v{version}</span>
55
46
  {mode === 'simple' && <span className="text-[10px] text-gray-500 font-light block pt-0.5 pb-0.5 ">Service</span>}
56
47
  </div>
57
- {/* <div className="flex justify-end">
58
- <span className="text-[8px] text-gray-500 font-light block pt-0.5 pb-0.5 ">View service</span>
59
- </div> */}
60
48
  </div>
61
49
  {mode === 'full' && (
62
50
  <div className="divide-y divide-gray-200 ">