@truedat/bg 5.13.1 → 5.13.3
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/package.json +5 -5
- package/src/concepts/relations/components/ConceptRelations.js +182 -101
- package/src/concepts/relations/components/__tests__/__snapshots__/ConceptRelations.spec.js.snap +54 -27
- package/src/messages/en.js +3 -0
- package/src/messages/es.js +3 -0
- package/src/taxonomy/api.js +1 -1
- package/src/taxonomy/sagas/__tests__/addDomainMember.spec.js +1 -1
- package/src/taxonomy/sagas/addDomainMember.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/bg",
|
|
3
|
-
"version": "5.13.
|
|
3
|
+
"version": "5.13.3",
|
|
4
4
|
"description": "Truedat Web Business Glossary",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"jsnext:main": "src/index.js",
|
|
@@ -86,9 +86,9 @@
|
|
|
86
86
|
]
|
|
87
87
|
},
|
|
88
88
|
"dependencies": {
|
|
89
|
-
"@truedat/core": "5.13.
|
|
90
|
-
"@truedat/df": "5.13.
|
|
91
|
-
"@truedat/lm": "5.13.
|
|
89
|
+
"@truedat/core": "5.13.3",
|
|
90
|
+
"@truedat/df": "5.13.3",
|
|
91
|
+
"@truedat/lm": "5.13.3",
|
|
92
92
|
"decode-uri-component": "^0.2.2",
|
|
93
93
|
"file-saver": "^2.0.5",
|
|
94
94
|
"moment": "^2.29.4",
|
|
@@ -111,5 +111,5 @@
|
|
|
111
111
|
"react-dom": ">= 16.8.6 < 17",
|
|
112
112
|
"semantic-ui-react": ">= 2.0.3 < 2.2"
|
|
113
113
|
},
|
|
114
|
-
"gitHead": "
|
|
114
|
+
"gitHead": "93c1576f82fedd1d9b607cd0c9053279763877bc"
|
|
115
115
|
}
|
|
@@ -2,16 +2,27 @@ import _ from "lodash/fp";
|
|
|
2
2
|
import React, { useEffect, useState } from "react";
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
4
|
import { connect } from "react-redux";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
Button,
|
|
7
|
+
Dropdown,
|
|
8
|
+
Segment,
|
|
9
|
+
Grid,
|
|
10
|
+
Icon,
|
|
11
|
+
Popup,
|
|
12
|
+
Table,
|
|
13
|
+
} from "semantic-ui-react";
|
|
6
14
|
import { FormattedMessage, useIntl } from "react-intl";
|
|
7
15
|
import { useHistory, useLocation } from "react-router-dom";
|
|
8
16
|
|
|
9
17
|
import { RelationGraph } from "@truedat/lm/components";
|
|
10
18
|
import { linkTo } from "@truedat/core/routes";
|
|
11
19
|
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
20
|
+
anyTraversalTags,
|
|
21
|
+
findMaxDepth,
|
|
22
|
+
pruneGraph,
|
|
23
|
+
graphDistinctTags,
|
|
24
|
+
EMPTY,
|
|
25
|
+
} from "@truedat/lm/services/relationGraphTraversal";
|
|
15
26
|
import { RelationGraphDepth } from "@truedat/lm/components";
|
|
16
27
|
import { getConceptToConceptRelations } from "../selectors/getConceptRelations";
|
|
17
28
|
|
|
@@ -41,34 +52,6 @@ export const ConceptRelations = ({
|
|
|
41
52
|
relationsGraph,
|
|
42
53
|
initialDepth,
|
|
43
54
|
}) => {
|
|
44
|
-
const [depth, setDepth] = useState(initialDepth || Infinity);
|
|
45
|
-
const [maxDepth, setMaxDepth] = useState();
|
|
46
|
-
const [currentId, setCurrentId] = useState();
|
|
47
|
-
const [limitedRelationGraph, setLimitedRelationGraph] =
|
|
48
|
-
useState(relationsGraph);
|
|
49
|
-
|
|
50
|
-
useEffect(() => {
|
|
51
|
-
!_.isEmpty(concept) &&
|
|
52
|
-
setCurrentId(`business_concept:${concept.business_concept_id}`);
|
|
53
|
-
}, [concept]);
|
|
54
|
-
|
|
55
|
-
useEffect(() => {
|
|
56
|
-
if (!_.isEmpty(relationsGraph) && currentId) {
|
|
57
|
-
const useEffectMaxDepth = selectMaxDepth({ relationsGraph }, currentId);
|
|
58
|
-
|
|
59
|
-
setMaxDepth(useEffectMaxDepth);
|
|
60
|
-
if (depth === Infinity) {
|
|
61
|
-
setDepth(useEffectMaxDepth);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}, [relationsGraph, currentId]);
|
|
65
|
-
|
|
66
|
-
useEffect(() => {
|
|
67
|
-
setLimitedRelationGraph(
|
|
68
|
-
selectLimitedGraph({ relationsGraph }, currentId, depth)
|
|
69
|
-
);
|
|
70
|
-
}, [relationsGraph, currentId, depth]);
|
|
71
|
-
|
|
72
55
|
const { formatMessage } = useIntl();
|
|
73
56
|
const history = useHistory();
|
|
74
57
|
const location = useLocation();
|
|
@@ -87,79 +70,177 @@ export const ConceptRelations = ({
|
|
|
87
70
|
);
|
|
88
71
|
};
|
|
89
72
|
|
|
73
|
+
const onTraversalTagsChange = (event, { value }) => {
|
|
74
|
+
setAllowedTags(value);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const [depth, setDepth] = useState(initialDepth || Infinity);
|
|
78
|
+
const [maxDepth, setMaxDepth] = useState(0);
|
|
79
|
+
const [currentId, setCurrentId] = useState();
|
|
80
|
+
const [tagOptions, setTagsOptions] = useState([]);
|
|
81
|
+
const [limitedRelationGraph, setLimitedRelationGraph] =
|
|
82
|
+
useState(relationsGraph);
|
|
83
|
+
const [traversalTags, setAllowedTags] = useState([]);
|
|
84
|
+
|
|
85
|
+
const nonDefaultOptions = (ids, depth, maxDepth) => {
|
|
86
|
+
return !_.isEmpty(ids) || depth !== maxDepth;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
!_.isEmpty(concept) &&
|
|
91
|
+
setCurrentId(`business_concept:${concept.business_concept_id}`);
|
|
92
|
+
}, [concept]);
|
|
93
|
+
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
if (!_.isEmpty(relationsGraph) && currentId) {
|
|
96
|
+
const newMaxDepth = findMaxDepth(
|
|
97
|
+
relationsGraph,
|
|
98
|
+
currentId,
|
|
99
|
+
traversalTags
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
setMaxDepth(newMaxDepth);
|
|
103
|
+
setDepth(newMaxDepth);
|
|
104
|
+
}
|
|
105
|
+
}, [relationsGraph, currentId, traversalTags]);
|
|
106
|
+
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
/* just a little optimization to avoid generating a graph if no limitations
|
|
109
|
+
* (tags, depth) are set
|
|
110
|
+
*/
|
|
111
|
+
if (nonDefaultOptions(traversalTags, depth, maxDepth)) {
|
|
112
|
+
const limitedGraph = pruneGraph(
|
|
113
|
+
relationsGraph,
|
|
114
|
+
currentId,
|
|
115
|
+
depth,
|
|
116
|
+
traversalTags
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
setLimitedRelationGraph(limitedGraph);
|
|
120
|
+
} else setLimitedRelationGraph(relationsGraph);
|
|
121
|
+
}, [relationsGraph, currentId, depth, traversalTags, maxDepth]);
|
|
122
|
+
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
const tagsToOptions = (tags) =>
|
|
125
|
+
_.map(
|
|
126
|
+
({ id, value }) => ({
|
|
127
|
+
text: formatMessage({
|
|
128
|
+
id: `source.${value.type}`,
|
|
129
|
+
defaultMessage: value.type,
|
|
130
|
+
}),
|
|
131
|
+
value: id,
|
|
132
|
+
}),
|
|
133
|
+
[
|
|
134
|
+
{
|
|
135
|
+
id: EMPTY,
|
|
136
|
+
value: { target_type: "business_concept", type: "_empty" },
|
|
137
|
+
},
|
|
138
|
+
...tags,
|
|
139
|
+
]
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
// eslint-disable-next-line prettier/prettier
|
|
143
|
+
_.flow(
|
|
144
|
+
graphDistinctTags,
|
|
145
|
+
tagsToOptions,
|
|
146
|
+
setTagsOptions
|
|
147
|
+
)(relationsGraph);
|
|
148
|
+
}, [relationsGraph]);
|
|
149
|
+
|
|
90
150
|
return (
|
|
91
151
|
<Segment attached="bottom">
|
|
92
|
-
<
|
|
152
|
+
<div className="traversal-tags-container">
|
|
153
|
+
<div>
|
|
154
|
+
<b>
|
|
155
|
+
<label>
|
|
156
|
+
{formatMessage({
|
|
157
|
+
id: "relations.tags.label",
|
|
158
|
+
})}
|
|
159
|
+
</label>
|
|
160
|
+
</b>
|
|
161
|
+
</div>
|
|
162
|
+
<div>
|
|
163
|
+
<Dropdown
|
|
164
|
+
className="traversal-tags"
|
|
165
|
+
placeholder={formatMessage({
|
|
166
|
+
id: `relations.tags.placeholder`,
|
|
167
|
+
})}
|
|
168
|
+
multiple
|
|
169
|
+
search
|
|
170
|
+
selection
|
|
171
|
+
options={tagOptions}
|
|
172
|
+
onChange={onTraversalTagsChange}
|
|
173
|
+
clearable
|
|
174
|
+
/>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
<div className="implementation-actions">
|
|
178
|
+
{_.negate(_.isEmpty)(conceptRelations) ? (
|
|
179
|
+
<Button.Group>
|
|
180
|
+
<Button
|
|
181
|
+
active={graphRender}
|
|
182
|
+
icon
|
|
183
|
+
data-tooltip={formatMessage({
|
|
184
|
+
id: "relations.actions.maps.tooltip",
|
|
185
|
+
})}
|
|
186
|
+
onClick={() => setGraphRender(true)}
|
|
187
|
+
>
|
|
188
|
+
<Icon name="sitemap" />
|
|
189
|
+
</Button>
|
|
190
|
+
<Button
|
|
191
|
+
active={!graphRender}
|
|
192
|
+
icon
|
|
193
|
+
data-tooltip={formatMessage({
|
|
194
|
+
id: "relations.actions.list.tooltip",
|
|
195
|
+
})}
|
|
196
|
+
onClick={() => setGraphRender(false)}
|
|
197
|
+
>
|
|
198
|
+
<Icon name="list ul" />
|
|
199
|
+
</Button>
|
|
200
|
+
</Button.Group>
|
|
201
|
+
) : null}
|
|
202
|
+
<ConceptRelationActions />
|
|
203
|
+
</div>
|
|
204
|
+
|
|
205
|
+
{conceptRelations ? (
|
|
206
|
+
<>
|
|
207
|
+
{graphRender && !_.isEmpty(currentId) ? (
|
|
208
|
+
<>
|
|
209
|
+
<RelationGraphDepth
|
|
210
|
+
onClick={(_e) => setDepth(maxDepth)}
|
|
211
|
+
onChange={(newDepth) => setDepth(parseInt(newDepth))}
|
|
212
|
+
depth={depth}
|
|
213
|
+
maxDepth={maxDepth}
|
|
214
|
+
/>
|
|
215
|
+
<RelationGraph
|
|
216
|
+
navigate={navigate}
|
|
217
|
+
currentId={currentId}
|
|
218
|
+
relationsGraph={limitedRelationGraph}
|
|
219
|
+
/>
|
|
220
|
+
</>
|
|
221
|
+
) : (
|
|
222
|
+
<Table selectable>
|
|
223
|
+
<Table.Body>
|
|
224
|
+
{_.flow(
|
|
225
|
+
_.filter(({ tags }) => anyTraversalTags(traversalTags, tags)),
|
|
226
|
+
_.map((relation) => (
|
|
227
|
+
<ConceptRelationRow
|
|
228
|
+
key={relation.id}
|
|
229
|
+
relationContext={relation.context}
|
|
230
|
+
{...relation}
|
|
231
|
+
/>
|
|
232
|
+
))
|
|
233
|
+
)(conceptRelations)}
|
|
234
|
+
</Table.Body>
|
|
235
|
+
</Table>
|
|
236
|
+
)}
|
|
237
|
+
</>
|
|
238
|
+
) : null}
|
|
239
|
+
{_.isEmpty(conceptRelations) ? (
|
|
93
240
|
<Grid.Row>
|
|
94
|
-
<
|
|
95
|
-
<div className="implementation-actions">
|
|
96
|
-
{_.negate(_.isEmpty)(conceptRelations) && (
|
|
97
|
-
<Button.Group>
|
|
98
|
-
<Button
|
|
99
|
-
active={graphRender}
|
|
100
|
-
icon
|
|
101
|
-
data-tooltip={formatMessage({
|
|
102
|
-
id: "relations.actions.maps.tooltip",
|
|
103
|
-
})}
|
|
104
|
-
onClick={() => setGraphRender(true)}
|
|
105
|
-
>
|
|
106
|
-
<Icon name="sitemap" />
|
|
107
|
-
</Button>
|
|
108
|
-
<Button
|
|
109
|
-
active={!graphRender}
|
|
110
|
-
icon
|
|
111
|
-
data-tooltip={formatMessage({
|
|
112
|
-
id: "relations.actions.list.tooltip",
|
|
113
|
-
})}
|
|
114
|
-
onClick={() => setGraphRender(false)}
|
|
115
|
-
>
|
|
116
|
-
<Icon name="list ul" />
|
|
117
|
-
</Button>
|
|
118
|
-
</Button.Group>
|
|
119
|
-
)}
|
|
120
|
-
<ConceptRelationActions />
|
|
121
|
-
</div>
|
|
122
|
-
</Grid.Column>
|
|
241
|
+
<FormattedMessage id="conceptRelations.empty" />
|
|
123
242
|
</Grid.Row>
|
|
124
|
-
|
|
125
|
-
<Grid.Row>
|
|
126
|
-
<Grid.Column>
|
|
127
|
-
{graphRender && !_.isEmpty(currentId) ? (
|
|
128
|
-
<>
|
|
129
|
-
<RelationGraphDepth
|
|
130
|
-
onClick={(_e) => setDepth(maxDepth)}
|
|
131
|
-
onChange={(newDepth) => setDepth(parseInt(newDepth))}
|
|
132
|
-
depth={depth}
|
|
133
|
-
maxDepth={maxDepth}
|
|
134
|
-
/>
|
|
135
|
-
<RelationGraph
|
|
136
|
-
navigate={navigate}
|
|
137
|
-
currentId={currentId}
|
|
138
|
-
relationsGraph={limitedRelationGraph}
|
|
139
|
-
/>
|
|
140
|
-
</>
|
|
141
|
-
) : (
|
|
142
|
-
<Table selectable>
|
|
143
|
-
<Table.Body>
|
|
144
|
-
{conceptRelations.map((relation, index) => (
|
|
145
|
-
<ConceptRelationRow
|
|
146
|
-
key={`${relation.id}.${index}`}
|
|
147
|
-
relationContext={relation.context}
|
|
148
|
-
{...relation}
|
|
149
|
-
/>
|
|
150
|
-
))}
|
|
151
|
-
</Table.Body>
|
|
152
|
-
</Table>
|
|
153
|
-
)}
|
|
154
|
-
</Grid.Column>
|
|
155
|
-
</Grid.Row>
|
|
156
|
-
)}
|
|
157
|
-
{_.isEmpty(conceptRelations) && (
|
|
158
|
-
<Grid.Row>
|
|
159
|
-
<FormattedMessage id="conceptRelations.empty" />
|
|
160
|
-
</Grid.Row>
|
|
161
|
-
)}
|
|
162
|
-
</Grid>
|
|
243
|
+
) : null}
|
|
163
244
|
</Segment>
|
|
164
245
|
);
|
|
165
246
|
};
|
package/src/concepts/relations/components/__tests__/__snapshots__/ConceptRelations.spec.js.snap
CHANGED
|
@@ -4,33 +4,60 @@ exports[`<ConceptRelations /> matches the latest snapshot 1`] = `
|
|
|
4
4
|
<Segment
|
|
5
5
|
attached="bottom"
|
|
6
6
|
>
|
|
7
|
-
<
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
>
|
|
13
|
-
|
|
14
|
-
</
|
|
15
|
-
</
|
|
16
|
-
</
|
|
17
|
-
<
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
7
|
+
<div
|
|
8
|
+
className="traversal-tags-container"
|
|
9
|
+
>
|
|
10
|
+
<div>
|
|
11
|
+
<b>
|
|
12
|
+
<label>
|
|
13
|
+
relations.tags.label
|
|
14
|
+
</label>
|
|
15
|
+
</b>
|
|
16
|
+
</div>
|
|
17
|
+
<div>
|
|
18
|
+
<Dropdown
|
|
19
|
+
additionLabel="Add "
|
|
20
|
+
additionPosition="top"
|
|
21
|
+
className="traversal-tags"
|
|
22
|
+
clearable={true}
|
|
23
|
+
closeOnBlur={true}
|
|
24
|
+
closeOnEscape={true}
|
|
25
|
+
deburr={false}
|
|
26
|
+
icon="dropdown"
|
|
27
|
+
minCharacters={1}
|
|
28
|
+
multiple={true}
|
|
29
|
+
noResultsMessage="No results found."
|
|
30
|
+
onChange={[Function]}
|
|
31
|
+
openOnFocus={true}
|
|
32
|
+
options={[]}
|
|
33
|
+
placeholder="relations.tags.placeholder"
|
|
34
|
+
renderLabel={[Function]}
|
|
35
|
+
search={true}
|
|
36
|
+
searchInput="text"
|
|
37
|
+
selectOnBlur={true}
|
|
38
|
+
selectOnNavigation={true}
|
|
39
|
+
selection={true}
|
|
40
|
+
wrapSelection={true}
|
|
32
41
|
/>
|
|
33
|
-
</
|
|
34
|
-
</
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
<div
|
|
45
|
+
className="implementation-actions"
|
|
46
|
+
>
|
|
47
|
+
<Connect(ConceptRelationActions) />
|
|
48
|
+
</div>
|
|
49
|
+
<Table
|
|
50
|
+
as="table"
|
|
51
|
+
selectable={true}
|
|
52
|
+
>
|
|
53
|
+
<TableBody
|
|
54
|
+
as="tbody"
|
|
55
|
+
/>
|
|
56
|
+
</Table>
|
|
57
|
+
<GridRow>
|
|
58
|
+
<MemoizedFormattedMessage
|
|
59
|
+
id="conceptRelations.empty"
|
|
60
|
+
/>
|
|
61
|
+
</GridRow>
|
|
35
62
|
</Segment>
|
|
36
63
|
`;
|
package/src/messages/en.js
CHANGED
|
@@ -284,7 +284,10 @@ export default {
|
|
|
284
284
|
"relations.actions.implementation.delete.confirmation.content":
|
|
285
285
|
"Link between implementation and concept will be deleted. Are you sure?",
|
|
286
286
|
"relations.actions.implementation.delete.confirmation.header": "Delete link",
|
|
287
|
+
"relations.tags.label": "Filter by relation types",
|
|
288
|
+
"relations.tags.placeholder": "Relation types",
|
|
287
289
|
"ruleImplementations.props.business_concepts": "Concepts",
|
|
290
|
+
"source._empty": "Empty",
|
|
288
291
|
"source.bc_caculo": "Calculated from",
|
|
289
292
|
"source.bc_padre": "Children",
|
|
290
293
|
"source.bc_parent": "Children",
|
package/src/messages/es.js
CHANGED
|
@@ -288,8 +288,11 @@ export default {
|
|
|
288
288
|
"Se eliminará la vinculación entre la implementación y el concepto. ¿Estás seguro?",
|
|
289
289
|
"relations.actions.implementation.delete.confirmation.header":
|
|
290
290
|
"Borrar vínculación",
|
|
291
|
+
"relations.tags.label": "Filtrar por tipos de relación",
|
|
292
|
+
"relations.tags.placeholder": "Tipos de relación",
|
|
291
293
|
"ruleImplementations.props.business_concepts": "Conceptos",
|
|
292
294
|
"saveConceptFilters.error.name.unique": "Nombre duplicado",
|
|
295
|
+
"source._empty": "Vacío",
|
|
293
296
|
"source.bc_caculo": "Cálculado en base a",
|
|
294
297
|
"source.bc_padre": "Hijos",
|
|
295
298
|
"source.bc_parent": "Hijos",
|
package/src/taxonomy/api.js
CHANGED
|
@@ -5,7 +5,7 @@ const API_DOMAIN_MEMBER = "/api/acl_entries/:id";
|
|
|
5
5
|
const API_DOMAINS = "/api/domains";
|
|
6
6
|
const API_DOMAIN_CONCEPT_CHILDREN =
|
|
7
7
|
"/api/domains/:domain_id/business_concepts/:user_name/count";
|
|
8
|
-
const API_RESOURCE_ACL_ENTRIES = "/api/:
|
|
8
|
+
const API_RESOURCE_ACL_ENTRIES = "/api/acl_entries/domains/:resource_id";
|
|
9
9
|
|
|
10
10
|
export {
|
|
11
11
|
API_ACL_ENTRY,
|
|
@@ -8,7 +8,7 @@ describe("sagas: addDomainMemberSaga", () => {
|
|
|
8
8
|
const acl_entry = { id: 1, name: "foo" };
|
|
9
9
|
const payload = { acl_entry, resource_id };
|
|
10
10
|
const meta = { resource_id };
|
|
11
|
-
const url = "/api/domains/123
|
|
11
|
+
const url = "/api/acl_entries/domains/123"; // Note resource_type is "domains"
|
|
12
12
|
|
|
13
13
|
it("should put a success action when a response is returned", () => {
|
|
14
14
|
const data = [];
|
|
@@ -9,7 +9,7 @@ const toApiPath = compile(API_RESOURCE_ACL_ENTRIES);
|
|
|
9
9
|
export function* addDomainMemberSaga({ payload }) {
|
|
10
10
|
try {
|
|
11
11
|
const { resource_id, acl_entry } = payload;
|
|
12
|
-
const url = toApiPath({ resource_id
|
|
12
|
+
const url = toApiPath({ resource_id });
|
|
13
13
|
const requestData = { acl_entry };
|
|
14
14
|
const meta = { resource_id };
|
|
15
15
|
yield put({ meta, ...addDomainMember.request() });
|