@eventcatalog/core 2.50.2 → 2.51.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.
@@ -37,7 +37,7 @@ var import_axios = __toESM(require("axios"), 1);
37
37
  var import_os = __toESM(require("os"), 1);
38
38
 
39
39
  // package.json
40
- var version = "2.50.2";
40
+ var version = "2.51.0";
41
41
 
42
42
  // src/constants.ts
43
43
  var VERSION = version;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  raiseEvent
3
- } from "../chunk-PRZAFM4L.js";
4
- import "../chunk-L3RINLLN.js";
3
+ } from "../chunk-2J3EYRYN.js";
4
+ import "../chunk-CXGJYRWV.js";
5
5
  export {
6
6
  raiseEvent
7
7
  };
@@ -106,7 +106,7 @@ var import_axios = __toESM(require("axios"), 1);
106
106
  var import_os = __toESM(require("os"), 1);
107
107
 
108
108
  // package.json
109
- var version = "2.50.2";
109
+ var version = "2.51.0";
110
110
 
111
111
  // src/constants.ts
112
112
  var VERSION = version;
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  log_build_default
3
- } from "../chunk-6DT6JYDR.js";
4
- import "../chunk-PRZAFM4L.js";
5
- import "../chunk-L3RINLLN.js";
3
+ } from "../chunk-PRFUGFES.js";
4
+ import "../chunk-2J3EYRYN.js";
5
+ import "../chunk-CXGJYRWV.js";
6
6
  import "../chunk-E7TXTI7G.js";
7
7
  export {
8
8
  log_build_default as default
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-L3RINLLN.js";
3
+ } from "./chunk-CXGJYRWV.js";
4
4
 
5
5
  // src/analytics/analytics.js
6
6
  import axios from "axios";
@@ -1,5 +1,5 @@
1
1
  // package.json
2
- var version = "2.50.2";
2
+ var version = "2.51.0";
3
3
 
4
4
  // src/constants.ts
5
5
  var VERSION = version;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  raiseEvent
3
- } from "./chunk-PRZAFM4L.js";
3
+ } from "./chunk-2J3EYRYN.js";
4
4
  import {
5
5
  getEventCatalogConfigFile,
6
6
  verifyRequiredFieldsAreInCatalogConfigFile
@@ -25,7 +25,7 @@ __export(constants_exports, {
25
25
  module.exports = __toCommonJS(constants_exports);
26
26
 
27
27
  // package.json
28
- var version = "2.50.2";
28
+ var version = "2.51.0";
29
29
 
30
30
  // src/constants.ts
31
31
  var VERSION = version;
package/dist/constants.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-L3RINLLN.js";
3
+ } from "./chunk-CXGJYRWV.js";
4
4
  export {
5
5
  VERSION
6
6
  };
@@ -157,7 +157,7 @@ var import_axios = __toESM(require("axios"), 1);
157
157
  var import_os = __toESM(require("os"), 1);
158
158
 
159
159
  // package.json
160
- var version = "2.50.2";
160
+ var version = "2.51.0";
161
161
 
162
162
  // src/constants.ts
163
163
  var VERSION = version;
@@ -6,8 +6,8 @@ import {
6
6
  } from "./chunk-XE6PFSH5.js";
7
7
  import {
8
8
  log_build_default
9
- } from "./chunk-6DT6JYDR.js";
10
- import "./chunk-PRZAFM4L.js";
9
+ } from "./chunk-PRFUGFES.js";
10
+ import "./chunk-2J3EYRYN.js";
11
11
  import {
12
12
  catalogToAstro,
13
13
  checkAndConvertMdToMdx
@@ -15,7 +15,7 @@ import {
15
15
  import "./chunk-LDBRNJIL.js";
16
16
  import {
17
17
  VERSION
18
- } from "./chunk-L3RINLLN.js";
18
+ } from "./chunk-CXGJYRWV.js";
19
19
  import {
20
20
  isAuthEnabled,
21
21
  isBackstagePluginEnabled,
@@ -1,28 +1,51 @@
1
1
  import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/react';
2
2
  import { MinusIcon } from '@heroicons/react/16/solid';
3
3
  import { PlusIcon } from '@heroicons/react/24/outline';
4
+ import { useEffect } from 'react';
5
+
6
+ declare global {
7
+ interface Window {
8
+ renderDiagrams?: (graphs: HTMLCollectionOf<Element>) => void;
9
+ renderPlantUML?: (graphs: HTMLCollectionOf<Element>) => void;
10
+ }
11
+ }
4
12
 
5
13
  export default function Example({ title, children }: any) {
6
14
  return (
7
15
  <div className="border border-gray-200 rounded-md px-4 shadow-sm py-2 accordion">
8
16
  <Disclosure as="div" key={title} className="">
9
- {({ open }) => (
10
- <div>
11
- <DisclosureButton className="flex w-full items-start justify-between text-left text-gray-900">
12
- <span className="text-base font-semibold leading-7">{title}</span>
13
- <span className="ml-6 flex h-7 items-center">
14
- {open ? (
15
- <MinusIcon className="h-6 w-6" aria-hidden="true" />
16
- ) : (
17
- <PlusIcon className="h-6 w-6" aria-hidden="true" />
18
- )}
19
- </span>
20
- </DisclosureButton>
21
- <DisclosurePanel as="dd" className="pr-12 not-prose py-4">
22
- <p className="text-base leading-7 text-gray-600">{children}</p>
23
- </DisclosurePanel>
24
- </div>
25
- )}
17
+ {({ open }) => {
18
+ useEffect(() => {
19
+ if (open) {
20
+ const graphs = document.getElementsByClassName('mermaid');
21
+ const plantUML = document.getElementsByClassName('plantuml');
22
+ if (graphs.length > 0 && window.renderDiagrams) {
23
+ window.renderDiagrams(graphs);
24
+ }
25
+ if (plantUML.length > 0 && window.renderPlantUML) {
26
+ window.renderPlantUML(plantUML);
27
+ }
28
+ }
29
+ }, [open]);
30
+
31
+ return (
32
+ <div>
33
+ <DisclosureButton className="flex w-full items-start justify-between text-left text-gray-900">
34
+ <span className="text-base font-semibold leading-7">{title}</span>
35
+ <span className="ml-6 flex h-7 items-center">
36
+ {open ? (
37
+ <MinusIcon className="h-6 w-6" aria-hidden="true" />
38
+ ) : (
39
+ <PlusIcon className="h-6 w-6" aria-hidden="true" />
40
+ )}
41
+ </span>
42
+ </DisclosureButton>
43
+ <DisclosurePanel as="dd" className="pr-12 py-4 prose prose-sm max-w-none">
44
+ <div className="text-base leading-7 text-gray-600">{children}</div>
45
+ </DisclosurePanel>
46
+ </div>
47
+ );
48
+ }}
26
49
  </Disclosure>
27
50
  </div>
28
51
  );
@@ -5,7 +5,7 @@ import Admonition from '@components/MDX/Admonition';
5
5
  import NodeGraph from '../NodeGraph/NodeGraph';
6
6
  import { getVersionFromCollection } from '@utils/collections/versions';
7
7
 
8
- const { id, version = 'latest', maxHeight, includeKey = true } = Astro.props;
8
+ const { id, version = 'latest', maxHeight, includeKey = true, mode = 'simple', walkthrough = true, search = true } = Astro.props;
9
9
 
10
10
  // Find the flow for the given id and version
11
11
  const flows = await getFlows();
@@ -17,6 +17,7 @@ const flow = flowCollection[0];
17
17
  const { nodes, edges } = await getNodesAndEdges({
18
18
  id: id,
19
19
  version: flow.data.version,
20
+ mode: mode,
20
21
  });
21
22
  ---
22
23
 
@@ -53,6 +54,8 @@ const { nodes, edges } = await getNodesAndEdges({
53
54
  includeKey={includeKey}
54
55
  footerLabel=`Flow diagram - ${flow.data.name} - v(${flow.data.version})`
55
56
  client:only="react"
57
+ showFlowWalkthrough={walkthrough}
58
+ showSearch={search}
56
59
  />
57
60
  </div>
58
61
 
@@ -30,9 +30,22 @@ interface Props {
30
30
  url: string;
31
31
  };
32
32
  linksToVisualiser?: boolean;
33
+ showSearch?: boolean;
34
+ showLegend?: boolean;
33
35
  }
34
36
 
35
- const { id, collection, title, mode = 'simple', linkTo = 'docs', version, href, linksToVisualiser } = Astro.props;
37
+ const {
38
+ id,
39
+ collection,
40
+ title,
41
+ mode = 'simple',
42
+ linkTo = 'docs',
43
+ version,
44
+ href,
45
+ linksToVisualiser,
46
+ showSearch = true,
47
+ showLegend = true,
48
+ } = Astro.props;
36
49
 
37
50
  let nodes = [],
38
51
  edges = [];
@@ -121,6 +134,8 @@ if (collection === 'domains-entities') {
121
134
  linksToVisualiser={linksToVisualiser}
122
135
  links={links}
123
136
  mode={mode}
137
+ showSearch={showSearch}
138
+ includeKey={showLegend}
124
139
  />
125
140
  </div>
126
141
 
@@ -55,6 +55,8 @@ interface Props {
55
55
  linksToVisualiser?: boolean;
56
56
  links?: { label: string; url: string }[];
57
57
  mode?: 'full' | 'simple';
58
+ showFlowWalkthrough?: boolean;
59
+ showSearch?: boolean;
58
60
  }
59
61
 
60
62
  const getVisualiserUrlForCollection = (collectionItem: CollectionEntry<CollectionTypes>) => {
@@ -71,6 +73,8 @@ const NodeGraphBuilder = ({
71
73
  linksToVisualiser = false,
72
74
  links = [],
73
75
  mode = 'full',
76
+ showFlowWalkthrough = true,
77
+ showSearch = true,
74
78
  }: Props) => {
75
79
  const nodeTypes = useMemo(
76
80
  () => ({
@@ -492,7 +496,7 @@ const NodeGraphBuilder = ({
492
496
  </span>
493
497
  )}
494
498
  </div>
495
- {mode === 'full' && (
499
+ {mode === 'full' && showSearch && (
496
500
  <div className="flex justify-end space-x-2 w-96">
497
501
  <VisualiserSearch ref={searchRef} nodes={nodes} onNodeSelect={handleNodeSelect} onClear={handleSearchClear} />
498
502
  </div>
@@ -588,7 +592,7 @@ const NodeGraphBuilder = ({
588
592
  )}
589
593
  {includeBackground && <Background color="#bbb" gap={16} />}
590
594
  {includeBackground && <Controls />}
591
- {isFlowVisualization && (
595
+ {isFlowVisualization && showFlowWalkthrough && (
592
596
  <Panel position="bottom-left">
593
597
  <StepWalkthrough
594
598
  nodes={nodes}
@@ -637,6 +641,8 @@ interface NodeGraphProps {
637
641
  links?: { label: string; url: string }[];
638
642
  mode?: 'full' | 'simple';
639
643
  portalId?: string;
644
+ showFlowWalkthrough?: boolean;
645
+ showSearch?: boolean;
640
646
  }
641
647
 
642
648
  const NodeGraph = ({
@@ -653,6 +659,8 @@ const NodeGraph = ({
653
659
  links = [],
654
660
  mode = 'full',
655
661
  portalId,
662
+ showFlowWalkthrough = true,
663
+ showSearch = true,
656
664
  }: NodeGraphProps) => {
657
665
  const [elem, setElem] = useState(null);
658
666
  const [showFooter, setShowFooter] = useState(true);
@@ -687,6 +695,8 @@ const NodeGraph = ({
687
695
  linksToVisualiser={linksToVisualiser}
688
696
  links={links}
689
697
  mode={mode}
698
+ showFlowWalkthrough={showFlowWalkthrough}
699
+ showSearch={showSearch}
690
700
  />
691
701
 
692
702
  {showFooter && (
@@ -410,6 +410,9 @@ const badges = doc?.badges || [];
410
410
  renderDiagrams(graphs);
411
411
  }
412
412
 
413
+ // Make renderDiagrams available globally for accordion component
414
+ window.renderDiagrams = renderDiagrams;
415
+
413
416
  document.addEventListener('astro:page-load', setupObserver);
414
417
  </script>
415
418
 
@@ -467,5 +470,9 @@ const badges = doc?.badges || [];
467
470
  if (document.getElementsByClassName('plantuml').length > 0) {
468
471
  renderPlantUML(graphs, deflate);
469
472
  }
473
+
474
+ window.renderPlantUML = (graphs: any) => {
475
+ renderPlantUML(graphs, deflate);
476
+ };
470
477
  });
471
478
  </script>
@@ -191,10 +191,17 @@ const generatePromptForResource = (props: any) => {
191
191
  // Handle node graphs in the markdown
192
192
  let nodeGraphs = getMDXComponentsByName(props.body || '', 'NodeGraph') || [];
193
193
 
194
+ // Get props for the node graph (when no id is passed, we assume its the current page)
195
+ const nodeGraphPropsForPage = nodeGraphs.find((nodeGraph: any) => nodeGraph.id === undefined) || ({} as any);
196
+
197
+ // This will render the graph for this page
194
198
  nodeGraphs.push({
195
199
  id: props.data.id,
196
200
  version: props.data.version,
197
201
  type: collectionToResourceMap[props.collection as keyof typeof collectionToResourceMap],
202
+ ...nodeGraphPropsForPage,
203
+ search: nodeGraphPropsForPage?.search ? nodeGraphPropsForPage.search === 'true' : true,
204
+ legend: nodeGraphPropsForPage?.legend ? nodeGraphPropsForPage.legend === 'true' : true,
198
205
  });
199
206
  ---
200
207
 
@@ -350,8 +357,10 @@ nodeGraphs.push({
350
357
  version={nodeGraph.version}
351
358
  collection={collection}
352
359
  title={nodeGraph.title}
353
- mode="simple"
360
+ mode={nodeGraph.mode || 'simple'}
354
361
  linksToVisualiser={true}
362
+ showSearch={nodeGraph.search ?? true}
363
+ showLegend={nodeGraph.legend ?? true}
355
364
  href={{
356
365
  label: 'Open in Visualiser',
357
366
  url: buildUrl(`/visualiser/${collection}/${nodeGraph.id}/${nodeGraph.version}`),
@@ -485,6 +494,9 @@ nodeGraphs.push({
485
494
  if (document.getElementsByClassName('mermaid').length > 0) {
486
495
  renderDiagrams(graphs);
487
496
  }
497
+
498
+ // Make renderDiagrams available globally for accordion component
499
+ window.renderDiagrams = renderDiagrams;
488
500
  </script>
489
501
 
490
502
  <script>
@@ -542,6 +554,10 @@ nodeGraphs.push({
542
554
  if (document.getElementsByClassName('plantuml').length > 0) {
543
555
  renderPlantUML(graphs, deflate);
544
556
  }
557
+
558
+ window.renderPlantUML = (graphs: any) => {
559
+ renderPlantUML(graphs, deflate);
560
+ };
545
561
  });
546
562
  </script>
547
563
  </VerticalSideBarLayout>
@@ -5,6 +5,7 @@ import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
5
5
  import { buildUrl } from '@utils/url-builder';
6
6
  import { ClientRouter } from 'astro:transitions';
7
7
  import { marked } from 'marked';
8
+ import * as LucideIcons from 'lucide-react';
8
9
 
9
10
  import { Page } from './_index.data';
10
11
 
@@ -37,28 +38,35 @@ const badges = [
37
38
  <main class="flex sm:px-8 docs-layout h-full max-w-7xl" data-pagefind-body data-pagefind-meta={`title:${pageTitle}`}>
38
39
  <div class="flex docs-layout w-full">
39
40
  <div class="w-full lg:mr-2 pr-8 overflow-y-auto py-8 min-h-[50em]">
40
- <nav class="flex mb-4" aria-label="Breadcrumb">
41
- <ol class="inline-flex items-center space-x-1 md:space-x-3">
42
- <li class="inline-flex items-center">
43
- <a
44
- href={buildUrl(`/docs/${props.type}/${props.domainId}/language`)}
45
- class="inline-flex items-center text-sm font-medium text-gray-500 hover:text-primary"
46
- >
47
- <svg
48
- class="w-3 h-3 mr-2.5"
49
- aria-hidden="true"
50
- xmlns="http://www.w3.org/2000/svg"
51
- fill="none"
52
- viewBox="0 0 24 24"
53
- stroke-width="2"
54
- stroke="currentColor"
55
- >
56
- <path stroke-linecap="round" stroke-linejoin="round" d="M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18"></path>
57
- </svg>
58
- Back to Ubiquitous Language List
59
- </a>
60
- </li>
61
- </ol>
41
+ {/* Breadcrumb */}
42
+ <nav class="mb-4 flex items-center space-x-2 text-sm text-gray-500" aria-label="Breadcrumb">
43
+ <a
44
+ href={buildUrl(`/docs/${props.type}/${props.domainId}/${props.domain.latestVersion}`)}
45
+ class="hover:text-gray-700 hover:underline flex items-center gap-2"
46
+ >
47
+ <RectangleGroupIcon className="h-4 w-4" />
48
+ {props.domain.name}
49
+ </a>
50
+ <svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
51
+ <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"></path>
52
+ </svg>
53
+ <a
54
+ href={buildUrl(`/docs/${props.type}/${props.domainId}/language`)}
55
+ class="hover:text-gray-700 hover:underline flex items-center gap-2"
56
+ >
57
+ {
58
+ (() => {
59
+ const BookOpen = LucideIcons.BookOpen;
60
+ //@ts-ignore
61
+ return <BookOpen className="h-4 w-4" />;
62
+ })()
63
+ }
64
+ Ubiquitous Language Explorer
65
+ </a>
66
+ <svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
67
+ <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"></path>
68
+ </svg>
69
+ <span class="text-gray-900">{ubiquitousLanguage.name}</span>
62
70
  </nav>
63
71
 
64
72
  <div class="border-b border-gray-200 flex justify-between items-start md:pb-2">
@@ -1,10 +1,11 @@
1
1
  ---
2
2
  import Footer from '@layouts/Footer.astro';
3
3
  import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
4
- import { getUbiquitousLanguage } from '@utils/collections/domains';
4
+ import { getUbiquitousLanguageWithSubdomains } from '@utils/collections/domains';
5
5
  import { buildUrl } from '@utils/url-builder';
6
6
  import { ClientRouter } from 'astro:transitions';
7
7
  import * as LucideIcons from 'lucide-react';
8
+ import { RectangleGroupIcon } from '@heroicons/react/24/outline';
8
9
 
9
10
  import { Page } from './_index.data';
10
11
 
@@ -15,47 +16,82 @@ export const getStaticPaths = Page.getStaticPaths;
15
16
  const props = await Page.getData(Astro);
16
17
 
17
18
  const pageTitle = `${props.collection} | ${props.data.name}`.replace(/^\w/, (c) => c.toUpperCase());
18
- const ubiquitousLanguages = await getUbiquitousLanguage(props);
19
- const ubiquitousLanguage = ubiquitousLanguages[0];
19
+ const ubiquitousLanguageData = await getUbiquitousLanguageWithSubdomains(props);
20
+ const ubiquitousLanguage = ubiquitousLanguageData.domain;
21
+ const { subdomains, duplicateTerms } = ubiquitousLanguageData;
20
22
  ---
21
23
 
22
24
  <VerticalSideBarLayout title={pageTitle} description={props.data.summary}>
23
- <main class="flex sm:px-8 docs-layout h-full" data-pagefind-body data-pagefind-meta={`title:${pageTitle}`}>
25
+ <main class="flex sm:px-6 docs-layout h-full" data-pagefind-body data-pagefind-meta={`title:${pageTitle}`}>
24
26
  <div class="flex docs-layout w-full">
25
- <div class="w-full lg:mr-2 pr-8 overflow-y-auto py-8 min-h-[50em]">
26
- <nav class="flex mb-4" aria-label="Breadcrumb">
27
- <ol class="inline-flex items-center space-x-1 md:space-x-3">
28
- <li class="inline-flex items-center">
29
- <a
30
- href={buildUrl(`/docs/${props.type}/${props.data.id}/${props.data.latestVersion}`)}
31
- class="inline-flex items-center text-sm font-medium text-gray-500 hover:text-primary"
32
- >
33
- <svg
34
- class="w-3 h-3 mr-2.5"
35
- aria-hidden="true"
36
- xmlns="http://www.w3.org/2000/svg"
37
- fill="none"
38
- viewBox="0 0 24 24"
39
- stroke-width="2"
40
- stroke="currentColor"
41
- >
42
- <path stroke-linecap="round" stroke-linejoin="round" d="M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18"></path>
43
- </svg>
44
- Back to Documentation
45
- </a>
46
- </li>
47
- </ol>
27
+ <div class="w-full lg:mr-2 pr-8 overflow-y-auto pt-6 pb-8 min-h-[50em]">
28
+ {/* Breadcrumb */}
29
+ <nav class="mb-4 flex items-center space-x-2 text-sm text-gray-500" aria-label="Breadcrumb">
30
+ <a
31
+ href={buildUrl(`/docs/${props.type}/${props.data.id}/${props.data.latestVersion}`)}
32
+ class="hover:text-gray-700 hover:underline flex items-center gap-2"
33
+ >
34
+ <RectangleGroupIcon className="h-4 w-4" />
35
+ {props.data.name}
36
+ </a>
37
+ <svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
38
+ <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"></path>
39
+ </svg>
40
+ <span class="text-gray-900 flex items-center gap-2">
41
+ {
42
+ (() => {
43
+ const BookOpen = LucideIcons.BookOpen;
44
+ //@ts-ignore
45
+ return <BookOpen className="h-4 w-4" />;
46
+ })()
47
+ }
48
+ Ubiquitous Language Explorer
49
+ </span>
48
50
  </nav>
49
51
 
50
- <div class="border-b border-gray-200 flex justify-between items-start md:pb-2">
51
- <div>
52
- <h2 id="doc-page-header" class="text-2xl md:text-4xl font-bold text-black">Domain Language Explorer</h2>
53
- <h2 class="text-2xl pt-2 text-gray-500 font-light">{props.data.name} domain</h2>
52
+ {/* Title Section */}
53
+ <div class="relative border-b border-gray-200 mb-4 pb-4">
54
+ <div class="xl:flex xl:items-start xl:justify-between">
55
+ <div class="min-w-0 flex-1">
56
+ <div class="flex items-center gap-2">
57
+ <h1 class="text-xl font-bold leading-7 text-gray-900 sm:text-2xl xl:text-3xl">Ubiquitous Language Explorer</h1>
58
+ </div>
59
+ <p class="mt-2 text-sm text-gray-500">
60
+ Browse and discover ubiquitous language terms in the {props.data.name} domain{
61
+ subdomains.length > 0 ? ' and its subdomains' : ''
62
+ }
63
+ </p>
64
+ </div>
65
+
66
+ <div class="mt-4 xl:mt-0 xl:ml-4 xl:flex-shrink-0">
67
+ <div class="relative w-full xl:w-96">
68
+ <input
69
+ type="text"
70
+ id="searchInput"
71
+ placeholder="Search terms across domains..."
72
+ class="w-full px-4 py-3 pl-10 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm xl:px-5 xl:py-3.5 xl:pl-12"
73
+ />
74
+ <div class="absolute inset-y-0 left-0 pl-3 xl:pl-4 flex items-center pointer-events-none">
75
+ <svg class="h-5 w-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
76
+ <path
77
+ stroke-linecap="round"
78
+ stroke-linejoin="round"
79
+ stroke-width="2"
80
+ d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
81
+ </svg>
82
+ </div>
83
+ </div>
84
+ <div class="mt-2 text-right">
85
+ <div class="text-sm text-gray-500" id="resultsCount">
86
+ {/* This will be updated by JavaScript */}
87
+ </div>
88
+ </div>
89
+ </div>
54
90
  </div>
55
91
  </div>
56
92
 
57
93
  {
58
- !ubiquitousLanguage && (
94
+ !ubiquitousLanguage && subdomains.length === 0 && (
59
95
  <div class="bg-yellow-50 border-l-4 border-yellow-400 p-4 my-4">
60
96
  <p class="text-yellow-700">
61
97
  This domain does not have any defined ubiquitous language terms yet. Consider adding some terms to help establish
@@ -67,66 +103,138 @@ const ubiquitousLanguage = ubiquitousLanguages[0];
67
103
 
68
104
  <div class="py-4 w-full min-h-[calc(100vh-10em)]">
69
105
  {
70
- ubiquitousLanguage && (
106
+ (ubiquitousLanguage || subdomains.some((s) => s.ubiquitousLanguage)) && (
71
107
  <div>
72
- <div class="mb-6">
73
- <input
74
- type="text"
75
- id="searchInput"
76
- placeholder="Search terms..."
77
- class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
78
- />
79
- </div>
80
-
81
- {!ubiquitousLanguage ? (
82
- <div class="text-center py-12 bg-gray-50 rounded-lg">
83
- <h3 class="text-lg font-medium text-gray-900">No domain language terms</h3>
84
- <p class="mt-2 text-sm text-gray-500">There are no language terms defined for this domain yet.</p>
85
- </div>
86
- ) : (
87
- <div id="termsGrid" class="grid grid-cols-1 md:grid-cols-3 gap-4">
88
- {ubiquitousLanguage?.data?.dictionary?.map((term) => (
89
- <div class="term-card block bg-white border border-gray-200 rounded-lg p-6 transition-all duration-300 ease-in-out hover:shadow-md hover:border-primary focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-primary focus:ring-white min-h-[12em]">
90
- <div class="flex flex-col h-full space-y-8">
91
- {term.icon && (
108
+ {/* Domain Language Section */}
109
+ {ubiquitousLanguage && (
110
+ <div class="mb-8" data-domain-section="main">
111
+ <h3 class="text-xl font-semibold text-gray-900 mb-4 border-b border-gray-200 pb-2">
112
+ {props.data.name} Domain Language
113
+ </h3>
114
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-4" data-terms-grid="main">
115
+ {ubiquitousLanguage?.data?.dictionary?.map((term) => (
116
+ <div
117
+ class={`term-card block bg-white border rounded-lg p-6 transition-all duration-300 ease-in-out hover:shadow-md hover:border-primary focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-primary focus:ring-white min-h-[12em] ${duplicateTerms.has(term.name.toLowerCase()) ? 'border-orange-300 bg-orange-50' : 'border-gray-200'}`}
118
+ data-domain="main"
119
+ >
120
+ <div class="flex flex-col h-full space-y-8">
121
+ {term.icon && (
122
+ <div>
123
+ {(() => {
124
+ const Icon = LucideIcons[term.icon as keyof typeof LucideIcons];
125
+ //@ts-ignore
126
+ return Icon ? <Icon className="w-6 h-6 text-primary" /> : null;
127
+ })()}
128
+ </div>
129
+ )}
92
130
  <div>
93
- {(() => {
94
- const Icon = LucideIcons[term.icon as keyof typeof LucideIcons];
95
- //@ts-ignore
96
- return Icon ? <Icon className="w-6 h-6 text-primary" /> : null;
97
- })()}
98
- </div>
99
- )}
100
- <div>
101
- <h3 class="text-gray-800 text-lg font-semibold transition-colors duration-300 ease-in-out group-hover:text-gray-300">
102
- {term.name}
103
- </h3>
104
- <div class="term-content">
105
- <p class="summary-text text-gray-600 transition-colors duration-300 ease-in-out group-hover:text-gray-200 m-0 font-light text-sm mb-4">
106
- {term.summary}
107
- </p>
108
- {term.description && (
109
- <div class="prose prose-sm prose-p:my-3">
110
- <a
111
- href={buildUrl(`/docs/${props.type}/${props.data.id}/language/${term.id}`)}
112
- class="show-more-text text-sm text-primary font-medium hover:underline"
113
- >
114
- Read more
115
- </a>
116
- </div>
117
- )}
131
+ <h4
132
+ class={`text-lg font-semibold transition-colors duration-300 ease-in-out group-hover:text-gray-300 ${duplicateTerms.has(term.name.toLowerCase()) ? 'text-orange-800' : 'text-gray-800'}`}
133
+ >
134
+ {term.name}
135
+ {duplicateTerms.has(term.name.toLowerCase()) && (
136
+ <span class="ml-2 inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-orange-100 text-orange-800">
137
+ Duplicate
138
+ </span>
139
+ )}
140
+ </h4>
141
+ <div class="term-content">
142
+ <p class="summary-text text-gray-600 transition-colors duration-300 ease-in-out group-hover:text-gray-200 m-0 font-light text-sm mb-4">
143
+ {term.summary}
144
+ </p>
145
+ {term.description && (
146
+ <div class="prose prose-sm prose-p:my-3">
147
+ <a
148
+ href={buildUrl(`/docs/${props.type}/${props.data.id}/language/${term.id}`)}
149
+ class="show-more-text text-sm text-primary font-medium hover:underline"
150
+ >
151
+ Read more
152
+ </a>
153
+ </div>
154
+ )}
155
+ </div>
118
156
  </div>
119
157
  </div>
120
158
  </div>
121
- </div>
122
- ))}
159
+ ))}
160
+ </div>
161
+ <div class="hidden domain-no-results text-center py-8 bg-gray-50 rounded-lg" data-domain-no-results="main">
162
+ <h4 class="text-md font-medium text-gray-900">No matching terms in {props.data.name} domain</h4>
163
+ <p class="mt-1 text-sm text-gray-500">Try adjusting your search terms.</p>
164
+ </div>
123
165
  </div>
124
166
  )}
125
167
 
126
- <div id="noSearchResults" class="hidden text-center py-12 bg-gray-50 rounded-lg">
127
- <h3 class="text-lg font-medium text-gray-900">No matching terms</h3>
128
- <p class="mt-2 text-sm text-gray-500">Try adjusting your search terms.</p>
129
- </div>
168
+ {/* Subdomain Language Sections */}
169
+ {subdomains
170
+ .filter((s) => s.ubiquitousLanguage)
171
+ .map(({ subdomain, ubiquitousLanguage: subdomainUL }) => (
172
+ <div class="mb-8" data-domain-section={subdomain.data.id}>
173
+ <div class="flex justify-between items-center mb-4 border-b border-gray-200 pb-2">
174
+ <h3 class="text-xl font-semibold text-gray-900">{subdomain.data.name} Subdomain Language</h3>
175
+ <a
176
+ href={buildUrl(`/docs/domains/${subdomain.data.id}/${subdomain.data.version}`)}
177
+ class="text-sm text-primary hover:underline font-medium"
178
+ >
179
+ View Domain →
180
+ </a>
181
+ </div>
182
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-4" data-terms-grid={subdomain.data.id}>
183
+ {subdomainUL?.data?.dictionary?.map((term) => (
184
+ <div
185
+ class={`term-card block bg-white border rounded-lg p-6 transition-all duration-300 ease-in-out hover:shadow-md hover:border-primary focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-primary focus:ring-white min-h-[12em] ${duplicateTerms.has(term.name.toLowerCase()) ? 'border-orange-300 bg-orange-50' : 'border-gray-200'}`}
186
+ data-domain={subdomain.data.id}
187
+ >
188
+ <div class="flex flex-col h-full space-y-8">
189
+ {term.icon && (
190
+ <div>
191
+ {(() => {
192
+ const Icon = LucideIcons[term.icon as keyof typeof LucideIcons];
193
+ //@ts-ignore
194
+ return Icon ? <Icon className="w-6 h-6 text-primary" /> : null;
195
+ })()}
196
+ </div>
197
+ )}
198
+ <div>
199
+ <h4
200
+ class={`text-lg font-semibold transition-colors duration-300 ease-in-out group-hover:text-gray-300 ${duplicateTerms.has(term.name.toLowerCase()) ? 'text-orange-800' : 'text-gray-800'}`}
201
+ >
202
+ {term.name}
203
+ {duplicateTerms.has(term.name.toLowerCase()) && (
204
+ <span class="ml-2 inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-orange-100 text-orange-800">
205
+ Duplicate
206
+ </span>
207
+ )}
208
+ </h4>
209
+ <div class="term-content">
210
+ <p class="summary-text text-gray-600 transition-colors duration-300 ease-in-out group-hover:text-gray-200 m-0 font-light text-sm mb-4">
211
+ {term.summary}
212
+ </p>
213
+ {term.description && (
214
+ <div class="prose prose-sm prose-p:my-3">
215
+ <a
216
+ href={buildUrl(`/docs/${props.type}/${subdomain.data.id}/language/${term.id}`)}
217
+ class="show-more-text text-sm text-primary font-medium hover:underline"
218
+ >
219
+ Read more
220
+ </a>
221
+ </div>
222
+ )}
223
+ </div>
224
+ </div>
225
+ </div>
226
+ </div>
227
+ ))}
228
+ </div>
229
+ <div
230
+ class="hidden domain-no-results text-center py-8 bg-gray-50 rounded-lg"
231
+ data-domain-no-results={subdomain.data.id}
232
+ >
233
+ <h4 class="text-md font-medium text-gray-900">No matching terms in {subdomain.data.name} subdomain</h4>
234
+ <p class="mt-1 text-sm text-gray-500">Try adjusting your search terms.</p>
235
+ </div>
236
+ </div>
237
+ ))}
130
238
  </div>
131
239
  )
132
240
  }
@@ -141,27 +249,55 @@ const ubiquitousLanguage = ubiquitousLanguages[0];
141
249
  <script>
142
250
  function initializeSearch() {
143
251
  const searchInput = document.getElementById('searchInput');
144
- const termCards = document.querySelectorAll('.term-card');
145
- const noSearchResults = document.getElementById('noSearchResults');
252
+ const domainSections = document.querySelectorAll('[data-domain-section]');
253
+ const resultsCount = document.getElementById('resultsCount');
254
+
255
+ function updateResults() {
256
+ //@ts-ignore
257
+ const searchTerm = searchInput?.value.toLowerCase() || '';
258
+ let totalVisibleTerms = 0;
259
+ let totalTerms = 0;
146
260
 
147
- searchInput?.addEventListener('input', (e) => {
148
- const searchTerm = (e.target as HTMLInputElement).value.toLowerCase();
149
- let visibleCount = 0;
261
+ // Handle search for each domain section
262
+ domainSections.forEach((section) => {
263
+ const domainId = section.getAttribute('data-domain-section');
264
+ const domainCards = section.querySelectorAll('.term-card');
265
+ const domainNoResults = section.querySelector(`[data-domain-no-results="${domainId}"]`);
266
+ let domainVisibleCount = 0;
150
267
 
151
- termCards.forEach((card) => {
152
- const title = card.querySelector('h3')?.textContent?.toLowerCase() || '';
153
- const description = card.querySelector('p')?.textContent?.toLowerCase() || '';
154
- const matches = title.includes(searchTerm) || description.includes(searchTerm);
268
+ domainCards.forEach((card) => {
269
+ totalTerms++;
270
+ const title = card.querySelector('h4')?.textContent?.toLowerCase() || '';
271
+ const description = card.querySelector('p')?.textContent?.toLowerCase() || '';
272
+ const matches = searchTerm === '' || title.includes(searchTerm) || description.includes(searchTerm);
155
273
 
156
- card.classList.toggle('hidden', !matches);
157
- if (matches) visibleCount++;
274
+ card.classList.toggle('hidden', !matches);
275
+ if (matches) {
276
+ domainVisibleCount++;
277
+ totalVisibleTerms++;
278
+ }
279
+ });
280
+
281
+ // Show/hide domain-specific no results message
282
+ if (domainNoResults) {
283
+ if (searchTerm.trim() === '') {
284
+ domainNoResults.classList.add('hidden');
285
+ } else {
286
+ domainNoResults.classList.toggle('hidden', domainVisibleCount > 0);
287
+ }
288
+ }
158
289
  });
159
290
 
160
- // Show/hide no results message
161
- if (noSearchResults) {
162
- noSearchResults.classList.toggle('hidden', visibleCount > 0);
291
+ // Update results count
292
+ if (resultsCount) {
293
+ resultsCount.textContent = `Showing ${totalVisibleTerms} terms`;
163
294
  }
164
- });
295
+ }
296
+
297
+ searchInput?.addEventListener('input', updateResults);
298
+
299
+ // Initialize results count
300
+ updateResults();
165
301
  }
166
302
 
167
303
  function initializeShowMore() {
@@ -132,6 +132,67 @@ export const getUbiquitousLanguage = async (domain: Domain): Promise<UbiquitousL
132
132
  return ubiquitousLanguages;
133
133
  };
134
134
 
135
+ export const getUbiquitousLanguageWithSubdomains = async (
136
+ domain: Domain
137
+ ): Promise<{
138
+ domain: UbiquitousLanguage | null;
139
+ subdomains: Array<{ subdomain: Domain; ubiquitousLanguage: UbiquitousLanguage | null }>;
140
+ duplicateTerms: Set<string>;
141
+ }> => {
142
+ // Get domain's own ubiquitous language
143
+ const domainUbiquitousLanguage = await getUbiquitousLanguage(domain);
144
+ const domainUL = domainUbiquitousLanguage[0] || null;
145
+
146
+ // Get all subdomains
147
+ const subdomains = (domain.data.domains as unknown as Domain[]) || [];
148
+
149
+ // Get ubiquitous language for each subdomain
150
+ const subdomainULs = await Promise.all(
151
+ subdomains.map(async (subdomain) => {
152
+ const subdomainUL = await getUbiquitousLanguage(subdomain);
153
+ return {
154
+ subdomain,
155
+ ubiquitousLanguage: subdomainUL[0] || null,
156
+ };
157
+ })
158
+ );
159
+
160
+ // Find duplicate terms across domain and subdomains
161
+ const duplicateTerms = new Set<string>();
162
+ const termCounts = new Map<string, number>();
163
+
164
+ // Count terms from domain
165
+ if (domainUL?.data?.dictionary) {
166
+ domainUL.data.dictionary.forEach((term) => {
167
+ const termName = term.name.toLowerCase();
168
+ termCounts.set(termName, (termCounts.get(termName) || 0) + 1);
169
+ });
170
+ }
171
+
172
+ // Count terms from subdomains
173
+ subdomainULs.forEach(({ ubiquitousLanguage }) => {
174
+ if (ubiquitousLanguage?.data?.dictionary) {
175
+ ubiquitousLanguage.data.dictionary.forEach((term) => {
176
+ const termName = term.name.toLowerCase();
177
+ termCounts.set(termName, (termCounts.get(termName) || 0) + 1);
178
+ });
179
+ }
180
+ });
181
+
182
+ // Identify duplicates
183
+ termCounts.forEach((count, termName) => {
184
+ if (count > 1) {
185
+ duplicateTerms.add(termName);
186
+ }
187
+ });
188
+
189
+ return {
190
+ domain: domainUL,
191
+ subdomains: subdomainULs,
192
+ duplicateTerms,
193
+ };
194
+ };
195
+
135
196
  export const getParentDomains = async (domain: Domain): Promise<Domain[]> => {
136
197
  const domains = await getDomains({ getAllVersions: false });
137
198
  return domains.filter((d) => {
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "url": "https://github.com/event-catalog/eventcatalog.git"
7
7
  },
8
8
  "type": "module",
9
- "version": "2.50.2",
9
+ "version": "2.51.0",
10
10
  "publishConfig": {
11
11
  "access": "public"
12
12
  },