@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.
- package/dist/analytics/analytics.cjs +1 -1
- package/dist/analytics/analytics.js +2 -2
- package/dist/analytics/log-build.cjs +1 -1
- package/dist/analytics/log-build.js +3 -3
- package/dist/{chunk-PRZAFM4L.js → chunk-2J3EYRYN.js} +1 -1
- package/dist/{chunk-L3RINLLN.js → chunk-CXGJYRWV.js} +1 -1
- package/dist/{chunk-6DT6JYDR.js → chunk-PRFUGFES.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +1 -1
- package/dist/eventcatalog.js +3 -3
- package/eventcatalog/src/components/MDX/Accordion/Accordion.tsx +40 -17
- package/eventcatalog/src/components/MDX/Flow/Flow.astro +4 -1
- package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +16 -1
- package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.tsx +12 -2
- package/eventcatalog/src/enterprise/custom-documentation/pages/docs/custom/index.astro +7 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +17 -1
- package/eventcatalog/src/pages/docs/[type]/[id]/language/[dictionaryId]/index.astro +30 -22
- package/eventcatalog/src/pages/docs/[type]/[id]/language/index.astro +235 -99
- package/eventcatalog/src/utils/collections/domains.ts +61 -0
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
log_build_default
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
5
|
-
import "../chunk-
|
|
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
|
package/dist/constants.cjs
CHANGED
package/dist/constants.js
CHANGED
package/dist/eventcatalog.cjs
CHANGED
package/dist/eventcatalog.js
CHANGED
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
} from "./chunk-XE6PFSH5.js";
|
|
7
7
|
import {
|
|
8
8
|
log_build_default
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
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-
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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 {
|
|
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=
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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 {
|
|
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
|
|
19
|
-
const ubiquitousLanguage =
|
|
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-
|
|
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
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
</
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
<
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
</
|
|
116
|
-
|
|
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
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
|
145
|
-
const
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
|
|
157
|
-
|
|
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
|
-
//
|
|
161
|
-
if (
|
|
162
|
-
|
|
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) => {
|