@truedat/bg 7.1.3 → 7.1.5

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.
@@ -0,0 +1,185 @@
1
+ import { getConceptVersionPath } from "../getConceptVersionPath";
2
+
3
+ describe("getConceptVersionPath selector (no mocks)", () => {
4
+ it("returns only the top breadcrumb if no subscope and status is published", () => {
5
+ // No template matches the concept.type => getConceptSubscope returns null
6
+ // or we can omit 'type' entirely or mismatch the template name.
7
+ // Must still have concept.id for getConceptSubscope to even check further.
8
+ const state = {
9
+ concept: {
10
+ id: 1,
11
+ status: "published",
12
+ },
13
+ // No matching template => the sub-scope will be null
14
+ templates: [{ name: "SomeOtherType", subscope: "someScope" }],
15
+ sidemenuGlossarySubscopes: undefined,
16
+ };
17
+
18
+ const result = getConceptVersionPath(state);
19
+ expect(result).toEqual([
20
+ {
21
+ id: "concepts.crumbs.top",
22
+ path: "/concepts",
23
+ },
24
+ ]);
25
+ });
26
+
27
+ it("returns only top breadcrumb if getConceptSubscope returns undefined (e.g., concept is missing id)", () => {
28
+ // Because concept.id is missing, getConceptSubscope returns undefined
29
+ const state = {
30
+ concept: {
31
+ status: "published",
32
+ // no 'id' property
33
+ },
34
+ templates: [{ name: "Foo", subscope: "myScope" }],
35
+ sidemenuGlossarySubscopes: ["myScope"],
36
+ };
37
+
38
+ const result = getConceptVersionPath(state);
39
+ expect(result).toEqual([
40
+ {
41
+ id: "concepts.crumbs.top",
42
+ path: "/concepts",
43
+ },
44
+ ]);
45
+ });
46
+
47
+ it("includes second breadcrumb (pending) if status is draft and not in side menu, but has subscope", () => {
48
+ // concept.type matches template => subScope = 'myScope'
49
+ // 'myScope' is NOT in sidemenuGlossarySubscopes => not root side menu
50
+ const state = {
51
+ concept: {
52
+ id: 2,
53
+ status: "draft",
54
+ type: "Foo",
55
+ },
56
+ templates: [{ name: "Foo", subscope: "myScope" }],
57
+ sidemenuGlossarySubscopes: ["otherScope"],
58
+ };
59
+
60
+ const result = getConceptVersionPath(state);
61
+ expect(result).toEqual([
62
+ {
63
+ id: "concepts.crumbs.top",
64
+ path: "/concepts",
65
+ },
66
+ {
67
+ id: "concepts.crumbs.pending",
68
+ path: "/concepts/pending",
69
+ },
70
+ ]);
71
+ });
72
+
73
+ it("includes second breadcrumb (deprecated) if status is 'deprecated' and not side menu, but has subscope", () => {
74
+ // match => subScope = 'myScope'
75
+ const state = {
76
+ concept: {
77
+ id: 3,
78
+ status: "deprecated",
79
+ type: "Foo",
80
+ },
81
+ templates: [{ name: "Foo", subscope: "myScope" }],
82
+ sidemenuGlossarySubscopes: ["anotherScope"],
83
+ };
84
+
85
+ const result = getConceptVersionPath(state);
86
+ expect(result).toEqual([
87
+ {
88
+ id: "concepts.crumbs.top",
89
+ path: "/concepts",
90
+ },
91
+ {
92
+ id: "concepts.crumbs.deprecated",
93
+ path: "/concepts/deprecated",
94
+ },
95
+ ]);
96
+ });
97
+
98
+ it("top breadcrumb uses side menu route if subscope is in side menu", () => {
99
+ // subScope = "someSubscope", which is in sideMenuGlossarySubscopes => root side menu
100
+ const state = {
101
+ concept: {
102
+ id: 4,
103
+ status: "published",
104
+ type: "SideMenuScope",
105
+ },
106
+ templates: [{ name: "SideMenuScope", subscope: "someSubscope" }],
107
+ sidemenuGlossarySubscopes: ["someSubscope"],
108
+ };
109
+
110
+ const result = getConceptVersionPath(state);
111
+
112
+ // Because it's root side menu, top crumb => /glossarySubscope/someSubscope/published
113
+ expect(result).toEqual([
114
+ {
115
+ id: "concepts.someSubscope.crumbs",
116
+ path: "/glossarySubscope/someSubscope/published",
117
+ },
118
+ ]);
119
+ });
120
+
121
+ it("returns second crumb as pending if status is draft and subscope is in side menu", () => {
122
+ const state = {
123
+ concept: {
124
+ id: 5,
125
+ status: "draft",
126
+ type: "SideMenuScope",
127
+ },
128
+ templates: [{ name: "SideMenuScope", subscope: "subscopeA" }],
129
+ sidemenuGlossarySubscopes: ["subscopeA"],
130
+ };
131
+
132
+ const result = getConceptVersionPath(state);
133
+ expect(result).toEqual([
134
+ {
135
+ id: "concepts.subscopeA.crumbs",
136
+ path: "/glossarySubscope/subscopeA/published",
137
+ },
138
+ {
139
+ id: "concepts.crumbs.pending",
140
+ path: "/glossarySubscope/subscopeA/pending",
141
+ },
142
+ ]);
143
+ });
144
+
145
+ it("returns no second breadcrumb if status is published and subscope is in side menu", () => {
146
+ const state = {
147
+ concept: {
148
+ id: 6,
149
+ status: "published",
150
+ type: "SideMenuScope",
151
+ },
152
+ templates: [{ name: "SideMenuScope", subscope: "subscopeA" }],
153
+ sidemenuGlossarySubscopes: ["subscopeA"],
154
+ };
155
+
156
+ const result = getConceptVersionPath(state);
157
+ expect(result).toEqual([
158
+ {
159
+ id: "concepts.subscopeA.crumbs",
160
+ path: "/glossarySubscope/subscopeA/published",
161
+ },
162
+ ]);
163
+ });
164
+
165
+ it("returns only top breadcrumb if subscope is null (concept with null subscope in the matching template)", () => {
166
+ // subScope found is null => hasSubscope = false => top crumb only
167
+ const state = {
168
+ concept: {
169
+ id: 7,
170
+ status: "published",
171
+ type: "NoSubscope",
172
+ },
173
+ templates: [{ name: "NoSubscope", subscope: null }],
174
+ sidemenuGlossarySubscopes: ["subscopeA"],
175
+ };
176
+
177
+ const result = getConceptVersionPath(state);
178
+ expect(result).toEqual([
179
+ {
180
+ id: "concepts.crumbs.top",
181
+ path: "/concepts",
182
+ },
183
+ ]);
184
+ });
185
+ });
@@ -0,0 +1,17 @@
1
+ import { getSidemenuGlossarySubscopes } from "../getSidemenuGlossarySubscopes";
2
+
3
+ describe("getSidemenuGlossarySubscopes", () => {
4
+ it("returns the sidemenuGlossarySubscopes array if present", () => {
5
+ const state = {
6
+ sidemenuGlossarySubscopes: ["scopeA", "scopeB"],
7
+ };
8
+ const result = getSidemenuGlossarySubscopes(state);
9
+ expect(result).toEqual(["scopeA", "scopeB"]);
10
+ });
11
+
12
+ it("returns an empty array if sidemenuGlossarySubscopes is undefined", () => {
13
+ const state = {};
14
+ const result = getSidemenuGlossarySubscopes(state);
15
+ expect(result).toEqual([]);
16
+ });
17
+ });
@@ -0,0 +1 @@
1
+ export const getConceptStatus = (state) => state.concept?.status ?? null;
@@ -0,0 +1,19 @@
1
+ import { createSelector } from "reselect";
2
+ import _ from "lodash/fp";
3
+
4
+ export const getConcept = _.prop("concept");
5
+ export const getTemplates = _.prop("templates");
6
+
7
+ export const getConceptSubscope = createSelector(
8
+ [getConcept, getTemplates],
9
+ (concept, templates) => {
10
+ if (!concept?.id) {
11
+ return undefined;
12
+ }
13
+
14
+ const type = _.get("type", concept);
15
+ const subscopeTemplate = _.find({ name: type }, templates);
16
+
17
+ return subscopeTemplate?.subscope || null;
18
+ }
19
+ );
@@ -0,0 +1,99 @@
1
+ import { createSelector } from "reselect";
2
+ import _ from "lodash/fp";
3
+ import {
4
+ CONCEPTS,
5
+ CONCEPTS_DEPRECATED,
6
+ CONCEPTS_PENDING,
7
+ } from "@truedat/core/routes";
8
+ import { linkTo } from "@truedat/core/routes";
9
+ import { getConceptSubscope } from "./getConceptSubscope";
10
+ import { getSidemenuGlossarySubscopes } from "./getSidemenuGlossarySubscopes";
11
+
12
+ function isPending(status) {
13
+ return (
14
+ status === "draft" || status === "pending_approval" || status === "rejected"
15
+ );
16
+ }
17
+
18
+ function buildTopBreadcrumb(isRootSideMenu, subscope) {
19
+ if (isRootSideMenu) {
20
+ return {
21
+ id: `concepts.${subscope}.crumbs`,
22
+ path: linkTo.CONCEPTS_SIDEMENU_SUBSCOPE({ subscope: subscope }),
23
+ };
24
+ }
25
+ return {
26
+ id: "concepts.crumbs.top",
27
+ path: CONCEPTS,
28
+ };
29
+ }
30
+
31
+ function buildSecondBreadcrumb({
32
+ hasSubscope,
33
+ isRootSideMenu,
34
+ subscope,
35
+ status,
36
+ }) {
37
+ if (!isRootSideMenu) {
38
+ if (isPending(status)) {
39
+ return {
40
+ id: "concepts.crumbs.pending",
41
+ path: CONCEPTS_PENDING,
42
+ };
43
+ }
44
+ if (status === "deprecated") {
45
+ return {
46
+ id: "concepts.crumbs.deprecated",
47
+ path: CONCEPTS_DEPRECATED,
48
+ };
49
+ }
50
+ if (hasSubscope) {
51
+ return {
52
+ id: `concepts.${subscope}.crumbs`,
53
+ path: linkTo.CONCEPTS_SUBSCOPE({ subscope: subscope }),
54
+ };
55
+ }
56
+ return null;
57
+ }
58
+
59
+ if (isPending(status)) {
60
+ return {
61
+ id: `concepts.crumbs.pending`,
62
+ path: linkTo.CONCEPTS_SIDEMENU_SUBSCOPE_PENDING({ subscope: subscope }),
63
+ };
64
+ }
65
+
66
+ if (status === "deprecated") {
67
+ return {
68
+ id: `concepts.crumbs.deprecated`,
69
+ path: linkTo.CONCEPTS_SIDEMENU_SUBSCOPE_DEPRECATED({
70
+ subscope: subscope,
71
+ }),
72
+ };
73
+ }
74
+
75
+ return null;
76
+ }
77
+
78
+ export const getConceptVersionPath = createSelector(
79
+ [_.prop("concept.status"), getConceptSubscope, getSidemenuGlossarySubscopes],
80
+ (status, subscope, sideMenuGlossarySubscopes) => {
81
+ const hasSubscope = subscope !== undefined && subscope !== null;
82
+
83
+ const isRootSideMenu =
84
+ hasSubscope &&
85
+ Array.isArray(sideMenuGlossarySubscopes) &&
86
+ sideMenuGlossarySubscopes.includes(subscope);
87
+
88
+ const topBreadcrumb = buildTopBreadcrumb(isRootSideMenu, subscope);
89
+
90
+ const secondBreadcrumb = buildSecondBreadcrumb({
91
+ hasSubscope,
92
+ isRootSideMenu,
93
+ subscope,
94
+ status,
95
+ });
96
+
97
+ return [topBreadcrumb, ...(secondBreadcrumb ? [secondBreadcrumb] : [])];
98
+ }
99
+ );
@@ -0,0 +1,8 @@
1
+ import _ from "lodash/fp";
2
+
3
+ export const defaultSidemenuGlossarySubscopes = [];
4
+
5
+ export const getSidemenuGlossarySubscopes = _.getOr(
6
+ defaultSidemenuGlossarySubscopes,
7
+ "sidemenuGlossarySubscopes"
8
+ );
@@ -118,6 +118,7 @@ export default {
118
118
  "concepts.actions.publish": "Publish",
119
119
  "concepts.actions.restore": "Restore",
120
120
  "concepts.actions.reject": "Reject",
121
+ "concepts.actions.new": "New",
121
122
  "concepts.actions.send_for_approval": "Send for Approval",
122
123
  "concepts.actions.undo_rejection": "Rectify",
123
124
  "concepts.actions.upload": "Upload file",
@@ -129,6 +130,8 @@ export default {
129
130
  "Updated {count} concepts successfully",
130
131
  "concepts.bulkUpdate.success.header": "Succeded Bulk Update",
131
132
  "concepts.crumbs.top": "Business Glossary",
133
+ "concepts.crumbs.deprecated": "Deprecated",
134
+ "concepts.crumbs.pending": "Draft",
132
135
  "concepts.edit.bulk.confirmation.content":
133
136
  "{concepts_count} concepts will be updated. Are you sure?",
134
137
  "concepts.edit.bulk.confirmation.header": "Bulk update",
@@ -180,6 +183,7 @@ export default {
180
183
  "concepts.subheader.linksManager": "Query links business concepts",
181
184
  "concepts.subheader.manage": "Concept Versions in Progress",
182
185
  "concepts.subheader.view": "Query business concepts",
186
+ "concepts.someSubscope.crumbs": "Some Subscope",
183
187
  "concepts.summary": "Summary",
184
188
  "concepts.taxonomy": "Taxonomy",
185
189
  "concepts.upload.header": "Upload concepts events",
@@ -126,6 +126,10 @@ export default {
126
126
  "concepts.bulkUpdate.success.content": "Se han actualizado {count} términos",
127
127
  "concepts.bulkUpdate.success.header": "Actualización terminada con éxito",
128
128
  "concepts.crumbs.top": "Glosario de conceptos",
129
+ "concepts.crumbs.deprecated": "Archivados",
130
+ "concepts.actions.new": "Nuevo",
131
+ "concepts.crumbs.pending": "Borrador",
132
+ "concepts.someSubscope.crumbs": "Algún Subscope",
129
133
  "concepts.edit.bulk.confirmation.content":
130
134
  "Se van a actualizar {concepts_count} términos. ¿Estás seguro?",
131
135
  "concepts.edit.bulk.confirmation.header": "Actualización masiva",