@eventcatalog/core 2.50.3 → 2.51.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.
- package/README.md +2 -1
- 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-RGWI2XAA.js → chunk-GWDFHYDT.js} +1 -1
- package/dist/{chunk-JCCZIMIQ.js → chunk-IAZKU3ZK.js} +1 -1
- package/dist/{chunk-ZTETQ7PD.js → chunk-JH4IFYVJ.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/EntityMap/EntityMap.astro +2 -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/eventcatalog/src/utils/node-graphs/domain-entity-map.ts +13 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
<h4>Features: Documentation for Event Driven Architectures, Integration with any broker, Generator from your OpenAPI and AsyncAPI documents, Docs and Code, Markdown driven, Document Domains/Services/Messages/Schemas and more, Content versioning, Assign Owners, Schemas, OpenAPI, MDX Components and more...</h4>
|
|
35
35
|
|
|
36
36
|
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
|
37
|
-
[](#contributors-)
|
|
38
38
|
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
|
39
39
|
|
|
40
40
|
[Read the Docs](https://www.eventcatalog.dev/docs/development/getting-started/introduction) | [View Demo](https://demo.eventcatalog.dev)
|
|
@@ -265,6 +265,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|
|
265
265
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/VitaliiBalash"><img src="https://avatars.githubusercontent.com/u/4520809?v=4?s=100" width="100px;" alt="Vitalii Balash"/><br /><sub><b>Vitalii Balash</b></sub></a><br /><a href="https://github.com/event-catalog/eventcatalog/commits?author=VitaliiBalash" title="Code">💻</a></td>
|
|
266
266
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ababilone"><img src="https://avatars.githubusercontent.com/u/925013?v=4?s=100" width="100px;" alt="Arnaud Babilone"/><br /><sub><b>Arnaud Babilone</b></sub></a><br /><a href="https://github.com/event-catalog/eventcatalog/commits?author=ababilone" title="Code">💻</a></td>
|
|
267
267
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/alexanderhorner"><img src="https://avatars.githubusercontent.com/u/18349361?v=4?s=100" width="100px;" alt="Alexander Horner"/><br /><sub><b>Alexander Horner</b></sub></a><br /><a href="https://github.com/event-catalog/eventcatalog/commits?author=alexanderhorner" title="Code">💻</a></td>
|
|
268
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/simonwfarrow"><img src="https://avatars.githubusercontent.com/u/3245908?v=4?s=100" width="100px;" alt="simonwfarrow"/><br /><sub><b>simonwfarrow</b></sub></a><br /><a href="https://github.com/event-catalog/eventcatalog/commits?author=simonwfarrow" title="Code">💻</a></td>
|
|
268
269
|
</tr>
|
|
269
270
|
</tbody>
|
|
270
271
|
</table>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
log_build_default
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
5
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-JH4IFYVJ.js";
|
|
4
|
+
import "../chunk-IAZKU3ZK.js";
|
|
5
|
+
import "../chunk-GWDFHYDT.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-JH4IFYVJ.js";
|
|
10
|
+
import "./chunk-IAZKU3ZK.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-GWDFHYDT.js";
|
|
19
19
|
import {
|
|
20
20
|
isAuthEnabled,
|
|
21
21
|
isBackstagePluginEnabled,
|
|
@@ -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, entities } = Astro.props;
|
|
9
9
|
|
|
10
10
|
// Find the flow for the given id and version
|
|
11
11
|
const domains = await getDomains();
|
|
@@ -15,6 +15,7 @@ const domain = domainCollection[0];
|
|
|
15
15
|
const { nodes, edges } = await getNodesAndEdges({
|
|
16
16
|
id: id,
|
|
17
17
|
version: domain?.data?.version,
|
|
18
|
+
...(entities ? { entities } : {}), // Pass entities if provided
|
|
18
19
|
});
|
|
19
20
|
---
|
|
20
21
|
|
|
@@ -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) => {
|
|
@@ -12,17 +12,25 @@ const elk = new ELK();
|
|
|
12
12
|
interface Props {
|
|
13
13
|
id: string;
|
|
14
14
|
version: string;
|
|
15
|
+
entities?: string[]; // Optional: array of entity IDs/names to include
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
export const getNodesAndEdges = async ({ id, version }: Props) => {
|
|
18
|
+
export const getNodesAndEdges = async ({ id, version, entities }: Props) => {
|
|
18
19
|
let nodes = [] as any,
|
|
19
20
|
edges = [] as any;
|
|
20
21
|
|
|
21
22
|
const allDomains = await getDomains();
|
|
22
|
-
const
|
|
23
|
+
const allEntities = await getEntities();
|
|
23
24
|
|
|
24
25
|
const domain = getVersionFromCollection(allDomains, id, version)[0] as Domain;
|
|
25
|
-
|
|
26
|
+
let domainEntities = (domain?.data?.entities ?? []) as any;
|
|
27
|
+
|
|
28
|
+
// If entities filter is provided, filter domainEntities to only those specified
|
|
29
|
+
if (entities && Array.isArray(entities) && entities.length > 0) {
|
|
30
|
+
domainEntities = domainEntities.filter(
|
|
31
|
+
(entity: Entity) => entities.includes(entity.data.id) || entities.includes(entity.data.name)
|
|
32
|
+
);
|
|
33
|
+
}
|
|
26
34
|
|
|
27
35
|
const entitiesWithReferences = domainEntities.filter((entity: Entity) =>
|
|
28
36
|
entity.data.properties?.some((property: any) => property.references)
|
|
@@ -44,7 +52,7 @@ export const getNodesAndEdges = async ({ id, version }: Props) => {
|
|
|
44
52
|
.flat()
|
|
45
53
|
.filter((ref: any) => ref !== undefined);
|
|
46
54
|
|
|
47
|
-
const externalToDomain =
|
|
55
|
+
const externalToDomain = Array.from(new Set<string>(listOfReferencedEntities as string[])) // Remove duplicates
|
|
48
56
|
.filter((entityId: any) => !domainEntities.some((domainEntity: any) => domainEntity.id === entityId));
|
|
49
57
|
|
|
50
58
|
// Helper function to find which domain an entity belongs to
|
|
@@ -54,7 +62,7 @@ export const getNodesAndEdges = async ({ id, version }: Props) => {
|
|
|
54
62
|
|
|
55
63
|
const addedExternalEntities = [];
|
|
56
64
|
for (const entityId of externalToDomain) {
|
|
57
|
-
const externalEntity = getItemsFromCollectionByIdAndSemverOrLatest(
|
|
65
|
+
const externalEntity = getItemsFromCollectionByIdAndSemverOrLatest(allEntities, entityId as string, 'latest')[0] as Entity;
|
|
58
66
|
|
|
59
67
|
if (externalEntity) {
|
|
60
68
|
const nodeId = generateIdForNode(externalEntity);
|