@truedat/bg 4.40.0 → 4.40.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.
Files changed (27) hide show
  1. package/CHANGELOG.md +13 -1
  2. package/package.json +5 -5
  3. package/src/concepts/components/ConceptTabPane.js +6 -1
  4. package/src/concepts/components/ConceptTabs.js +28 -6
  5. package/src/concepts/components/__tests__/ConceptManageDomain.spec.js +0 -2
  6. package/src/concepts/components/__tests__/ConceptTabs.spec.js +32 -0
  7. package/src/concepts/components/__tests__/__snapshots__/ConceptManageDomain.spec.js.snap +1 -0
  8. package/src/concepts/components/__tests__/__snapshots__/ConceptTabPane.spec.js.snap +4 -0
  9. package/src/concepts/components/__tests__/__snapshots__/ConceptTabs.spec.js.snap +52 -0
  10. package/src/concepts/relations/components/ConceptImplementationLinks.js +70 -0
  11. package/src/concepts/relations/components/ConceptRelationRow.js +6 -2
  12. package/src/concepts/relations/components/ConceptRelationsRoutes.js +7 -0
  13. package/src/concepts/relations/components/ImplementationLinksAction.js +35 -0
  14. package/src/concepts/relations/components/__tests__/ConceptImplementationLinks.spec.js +30 -0
  15. package/src/concepts/relations/components/__tests__/ImplementationLinksAction.spec.js +31 -0
  16. package/src/concepts/relations/components/__tests__/__snapshots__/ConceptImplementationLinks.spec.js.snap +20 -0
  17. package/src/concepts/relations/components/__tests__/__snapshots__/ConceptRelationRow.spec.js.snap +1 -0
  18. package/src/concepts/relations/components/__tests__/__snapshots__/ConceptRelationsRoutes.spec.js.snap +5 -0
  19. package/src/concepts/relations/components/__tests__/__snapshots__/ImplementationLinksAction.spec.js.snap +10 -0
  20. package/src/concepts/relations/selectors/__tests__/getConceptImplementationLinks.spec.js +54 -0
  21. package/src/concepts/relations/selectors/getConceptImplementationLinks.js +34 -0
  22. package/src/concepts/relations/selectors/getConceptLinks.js +19 -18
  23. package/src/concepts/relations/selectors/index.js +2 -1
  24. package/src/messages/en.js +13 -1
  25. package/src/messages/es.js +14 -1
  26. package/src/taxonomy/components/DomainDropdownSelector.js +7 -3
  27. package/src/taxonomy/components/__tests__/__snapshots__/DomainDropdownSelector.spec.js.snap +1 -0
package/CHANGELOG.md CHANGED
@@ -1,6 +1,18 @@
1
1
  # Changelog
2
2
 
3
- ## [4.40.0] 2022-03-27
3
+ ## [4.40.3] 2022-03-14
4
+
5
+ ### Added
6
+
7
+ - [TD-4271] Support for linking implementations with business_concepts
8
+
9
+ ## [4.40.2] 2022-03-14
10
+
11
+ ### Added
12
+
13
+ - [TD-4500] Support for data structures with multiple domain_ids
14
+
15
+ ## [4.40.0] 2022-03-07
4
16
 
5
17
  ### Changed
6
18
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/bg",
3
- "version": "4.40.0",
3
+ "version": "4.40.3",
4
4
  "description": "Truedat Web Business Glossary",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -34,7 +34,7 @@
34
34
  "@testing-library/jest-dom": "^5.14.1",
35
35
  "@testing-library/react": "^12.0.0",
36
36
  "@testing-library/user-event": "^13.2.1",
37
- "@truedat/test": "4.39.0",
37
+ "@truedat/test": "4.40.3",
38
38
  "babel-jest": "^27.0.6",
39
39
  "babel-plugin-dynamic-import-node": "^2.3.3",
40
40
  "babel-plugin-lodash": "^3.3.4",
@@ -83,8 +83,8 @@
83
83
  ]
84
84
  },
85
85
  "dependencies": {
86
- "@truedat/core": "4.40.0",
87
- "@truedat/df": "4.40.0",
86
+ "@truedat/core": "4.40.3",
87
+ "@truedat/df": "4.40.3",
88
88
  "file-saver": "^2.0.5",
89
89
  "moment": "^2.24.0",
90
90
  "path-to-regexp": "^1.7.0",
@@ -104,5 +104,5 @@
104
104
  "react-dom": ">= 16.8.6 < 17",
105
105
  "semantic-ui-react": ">= 0.88.2 < 2.1"
106
106
  },
107
- "gitHead": "0bd677584da73a5021a5c96f2e5525fde9a1958a"
107
+ "gitHead": "03104a28941b568007ad53b85874ff58225eaa6a"
108
108
  }
@@ -6,9 +6,10 @@ import {
6
6
  CONCEPT_EVENTS,
7
7
  CONCEPT_LINKS_CONCEPTS,
8
8
  CONCEPT_LINKS_STRUCTURES,
9
+ CONCEPT_LINKS_IMPLEMENTATIONS,
9
10
  CONCEPT_RULES,
10
11
  CONCEPT_RULES_NEW,
11
- CONCEPT_VERSION
12
+ CONCEPT_VERSION,
12
13
  } from "@truedat/core/routes";
13
14
  import ConceptRelationsRoutes from "../relations/components/ConceptRelationsRoutes";
14
15
  import ConceptArchive from "./ConceptArchive";
@@ -39,6 +40,10 @@ export const ConceptTabPane = () => (
39
40
  path={CONCEPT_LINKS_CONCEPTS}
40
41
  render={() => <ConceptRelationsRoutes />}
41
42
  />
43
+ <Route
44
+ path={CONCEPT_LINKS_IMPLEMENTATIONS}
45
+ render={() => <ConceptRelationsRoutes />}
46
+ />
42
47
  <Route
43
48
  path={CONCEPT_RULES}
44
49
  render={() => (
@@ -9,22 +9,28 @@ import { useActiveRoute } from "@truedat/core/hooks";
9
9
  import {
10
10
  CONCEPT_ARCHIVE,
11
11
  CONCEPT_EVENTS,
12
+ CONCEPT_LINKS_IMPLEMENTATIONS,
12
13
  CONCEPT_LINKS_CONCEPTS,
13
14
  CONCEPT_LINKS_STRUCTURES,
14
15
  CONCEPT_RULES,
15
16
  CONCEPT_VERSION,
16
- linkTo
17
+ linkTo,
17
18
  } from "@truedat/core/routes";
18
19
 
19
- const ConceptTabs = ({ concept, emptyConceptTags }) => {
20
+ export const ConceptTabs = ({
21
+ concept,
22
+ emptyConceptTags,
23
+ emptyImplementationLinks,
24
+ }) => {
20
25
  const archiveActive = useActiveRoute(CONCEPT_ARCHIVE);
21
26
  const conceptActive = useActiveRoute({
22
27
  path: CONCEPT_VERSION,
23
- exact: true
28
+ exact: true,
24
29
  });
25
30
  const conceptLinksActive = useActiveRoute(CONCEPT_LINKS_CONCEPTS);
26
31
  const eventsActive = useActiveRoute(CONCEPT_EVENTS);
27
32
  const rulesActive = useActiveRoute(CONCEPT_RULES);
33
+ const implementationsActive = useActiveRoute(CONCEPT_LINKS_IMPLEMENTATIONS);
28
34
  const structureLinksActive = useActiveRoute(CONCEPT_LINKS_STRUCTURES);
29
35
 
30
36
  return _.isEmpty(concept) ? null : (
@@ -63,6 +69,16 @@ const ConceptTabs = ({ concept, emptyConceptTags }) => {
63
69
  >
64
70
  <FormattedMessage id="tabs.bg.qualityRules" />
65
71
  </Menu.Item>
72
+ {!emptyImplementationLinks && (
73
+ <Menu.Item
74
+ active={implementationsActive}
75
+ as={Link}
76
+ to={linkTo.CONCEPT_LINKS_IMPLEMENTATIONS(concept)}
77
+ replace
78
+ >
79
+ <FormattedMessage id="tabs.bg.qualityImplementations" />
80
+ </Menu.Item>
81
+ )}
66
82
  <Menu.Item
67
83
  active={archiveActive}
68
84
  as={Link}
@@ -71,6 +87,7 @@ const ConceptTabs = ({ concept, emptyConceptTags }) => {
71
87
  >
72
88
  <FormattedMessage id="tabs.bg.history" />
73
89
  </Menu.Item>
90
+
74
91
  <Menu.Item
75
92
  active={eventsActive}
76
93
  as={Link}
@@ -85,15 +102,20 @@ const ConceptTabs = ({ concept, emptyConceptTags }) => {
85
102
 
86
103
  ConceptTabs.propTypes = {
87
104
  concept: PropTypes.object,
88
- emptyConceptTags: PropTypes.bool
105
+ emptyConceptTags: PropTypes.bool,
106
+ emptyImplementationLinks: PropTypes.bool,
89
107
  };
90
108
 
91
- const mapStateToProps = ({ concept, relationTags }) => ({
109
+ const mapStateToProps = ({ concept, relationTags, conceptLinks }) => ({
92
110
  emptyConceptTags: _.flow(
93
111
  _.filter(_.pathEq("value.target_type", "business_concept")),
94
112
  _.isEmpty
95
113
  )(relationTags),
96
- concept
114
+ concept,
115
+ emptyImplementationLinks: _.flow(
116
+ _.filter(_.pathEq("resource_type", "implementation")),
117
+ _.isEmpty
118
+ )(conceptLinks),
97
119
  });
98
120
 
99
121
  export default connect(mapStateToProps)(ConceptTabs);
@@ -1,6 +1,4 @@
1
1
  import React from "react";
2
- import { waitFor } from "@testing-library/react";
3
- import userEvent from "@testing-library/user-event";
4
2
  import { render } from "@truedat/test/render";
5
3
  import { ConceptManageDomain } from "../ConceptManageDomain";
6
4
 
@@ -0,0 +1,32 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import { ConceptTabs } from "../ConceptTabs";
4
+
5
+ describe("<ConceptTabs />", () => {
6
+ const renderOpts = {
7
+ messages: {
8
+ en: {
9
+ "tabs.bg.concept": "concept",
10
+ "tabs.bg.relations_data_field": "relations_data_field",
11
+ "tabs.bg.relations_business_concept": "relations_business_concept",
12
+ "tabs.bg.qualityRules": "qualityRules",
13
+ "tabs.bg.qualityImplementations": "qualityImplementations",
14
+ "tabs.bg.history": "history",
15
+ "tabs.bg.audit": "audit",
16
+ },
17
+ },
18
+ };
19
+
20
+ const props = {
21
+ concept: {
22
+ id: 1,
23
+ business_concept_id: 8,
24
+ },
25
+ emptyConceptTags: false,
26
+ emptyImplementationLinks: false,
27
+ };
28
+ it("matches the latest snapshot", () => {
29
+ const { container } = render(<ConceptTabs {...props} />, renderOpts);
30
+ expect(container).toMatchSnapshot();
31
+ });
32
+ });
@@ -50,6 +50,7 @@ exports[`<ConceptManageDomain /> matches the latest snapshot 1`] = `
50
50
  class="dropdown icon"
51
51
  />
52
52
  <div
53
+ aria-multiselectable="false"
53
54
  class="menu transition"
54
55
  role="listbox"
55
56
  >
@@ -16,6 +16,10 @@ exports[`<ConceptTabPane /> matches the latest snapshot 1`] = `
16
16
  path="/concepts/:business_concept_id/versions/:id/links/concepts"
17
17
  render={[Function]}
18
18
  />
19
+ <Route
20
+ path="/concepts/:business_concept_id/versions/:id/links/implementations"
21
+ render={[Function]}
22
+ />
19
23
  <Route
20
24
  path="/concepts/:business_concept_id/versions/:id/rules"
21
25
  render={[Function]}
@@ -0,0 +1,52 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<ConceptTabs /> matches the latest snapshot 1`] = `
4
+ <div>
5
+ <div
6
+ class="ui pointing secondary top attached tabular menu"
7
+ >
8
+ <a
9
+ class="item"
10
+ href="/concepts/8/versions/1"
11
+ >
12
+ concept
13
+ </a>
14
+ <a
15
+ class="item"
16
+ href="/concepts/8/versions/1/links/structures"
17
+ >
18
+ relations_data_field
19
+ </a>
20
+ <a
21
+ class="item"
22
+ href="/concepts/8/versions/1/links/concepts"
23
+ >
24
+ relations_business_concept
25
+ </a>
26
+ <a
27
+ class="item"
28
+ href="/concepts/8/versions/1/rules"
29
+ >
30
+ qualityRules
31
+ </a>
32
+ <a
33
+ class="item"
34
+ href="/concepts/8/versions/1/links/implementations"
35
+ >
36
+ qualityImplementations
37
+ </a>
38
+ <a
39
+ class="item"
40
+ href="/concepts/8/versions/1/archive"
41
+ >
42
+ history
43
+ </a>
44
+ <a
45
+ class="item"
46
+ href="/concepts/8/versions/1/events"
47
+ >
48
+ audit
49
+ </a>
50
+ </div>
51
+ </div>
52
+ `;
@@ -0,0 +1,70 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { connect } from "react-redux";
5
+ import { Segment, Header, Table } from "semantic-ui-react";
6
+ import { FormattedMessage } from "react-intl";
7
+ import { getConceptImplementationLinks } from "../selectors";
8
+ import { conceptLinkAction } from "../routines";
9
+ import ImplementationLinksAction from "./ImplementationLinksAction";
10
+ const RuleImplementationsTable = React.lazy(() =>
11
+ import("@truedat/dq/components/RuleImplementationsTable")
12
+ );
13
+
14
+ export const ConceptImplementationLinks = ({ groups, conceptLinkAction }) => {
15
+ const doDelete = (action) => {
16
+ conceptLinkAction(action);
17
+ };
18
+ const [additionalColumns, additionalCells] = [
19
+ [<Table.HeaderCell key={"status"} width={1} />],
20
+ [
21
+ {
22
+ style: { textAlign: "center", padding: "1px" },
23
+ className: "pointer",
24
+ props: { doDelete },
25
+ content: ImplementationLinksAction,
26
+ },
27
+ ],
28
+ ];
29
+
30
+ return (
31
+ <Segment attached="bottom">
32
+ {groups.map(([tag, implementations], key) =>
33
+ _.isEmpty(implementations) ? null : (
34
+ <Segment vertical key={key}>
35
+ <Header as="h3">
36
+ {tag === "deleted" ? (
37
+ <FormattedMessage
38
+ id={"implementationDeletedRelation.table.title"}
39
+ />
40
+ ) : tag === "\uffee" ? (
41
+ <FormattedMessage id={"implementationRelation.table.title"} />
42
+ ) : (
43
+ tag
44
+ )}
45
+ </Header>
46
+ <RuleImplementationsTable
47
+ ruleImplementations={implementations}
48
+ withoutColumns={["business_concept"]}
49
+ additionalColumns={additionalColumns}
50
+ additionalCells={additionalCells}
51
+ />
52
+ </Segment>
53
+ )
54
+ )}
55
+ </Segment>
56
+ );
57
+ };
58
+
59
+ ConceptImplementationLinks.propTypes = {
60
+ groups: PropTypes.array,
61
+ conceptLinkAction: PropTypes.object,
62
+ };
63
+
64
+ const mapStateToProps = (state) => ({
65
+ groups: getConceptImplementationLinks(state),
66
+ });
67
+
68
+ export default connect(mapStateToProps, {
69
+ conceptLinkAction,
70
+ })(ConceptImplementationLinks);
@@ -33,7 +33,9 @@ export const ConceptRelationRow = ({
33
33
  return (
34
34
  <Table.Row>
35
35
  <Table.Cell textAlign="center">
36
- {canDeleteConceptRelation && <ConfirmDeleteRelation id={id} />}
36
+ {canDeleteConceptRelation && (
37
+ <ConfirmDeleteRelation id={id} resourceType="concept" />
38
+ )}
37
39
  </Table.Cell>
38
40
  </Table.Row>
39
41
  );
@@ -69,7 +71,9 @@ export const ConceptRelationRow = ({
69
71
  onClick={() => goToConcept({ history, concept })}
70
72
  />
71
73
  <Table.Cell textAlign="center">
72
- {canDeleteConceptRelation && <ConfirmDeleteRelation id={id} />}
74
+ {canDeleteConceptRelation && (
75
+ <ConfirmDeleteRelation id={id} resourceType="concept" />
76
+ )}
73
77
  </Table.Cell>
74
78
  </Table.Row>
75
79
  );
@@ -8,10 +8,12 @@ import {
8
8
  CONCEPT_LINKS_CONCEPTS_NEW,
9
9
  CONCEPT_LINKS_STRUCTURES,
10
10
  CONCEPT_LINKS_STRUCTURES_NEW,
11
+ CONCEPT_LINKS_IMPLEMENTATIONS,
11
12
  } from "@truedat/core/routes";
12
13
  import ConceptStructureLinks from "./ConceptStructureLinks";
13
14
  import ConceptRelations from "./ConceptRelations";
14
15
  import ConceptRelationForm from "./ConceptRelationForm";
16
+ import ConceptImplementationLinks from "./ConceptImplementationLinks";
15
17
 
16
18
  const ConceptStructureLinkForm = React.lazy(() =>
17
19
  import("@truedat/lm/components/ConceptStructureLinkForm")
@@ -52,6 +54,11 @@ export const ConceptRelationsRoutes = ({ conceptLoaded, relationsLoading }) => (
52
54
  render={() => (conceptLoaded ? <ConceptRelations /> : null)}
53
55
  />
54
56
  )}
57
+ <Route
58
+ exact
59
+ path={CONCEPT_LINKS_IMPLEMENTATIONS}
60
+ render={() => (conceptLoaded ? <ConceptImplementationLinks /> : null)}
61
+ />
55
62
  </Switch>
56
63
  );
57
64
 
@@ -0,0 +1,35 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { useIntl } from "react-intl";
5
+ import { Icon } from "semantic-ui-react";
6
+ import { ConfirmModal } from "@truedat/core/components";
7
+
8
+ export const ImplementationLinksAction = ({ _actions: actions, doDelete }) => {
9
+ const { formatMessage } = useIntl();
10
+ const deleteAction = _.prop("delete")(actions);
11
+ return deleteAction ? (
12
+ <>
13
+ <ConfirmModal
14
+ icon="trash"
15
+ trigger={<Icon name="trash alternate outline" color="red" />}
16
+ header={formatMessage({
17
+ id: `conceptRelation.implementation.actions.delete.confirmation.header`,
18
+ })}
19
+ content={formatMessage({
20
+ id: `conceptRelation.implementation.actions.delete.confirmation.content`,
21
+ })}
22
+ onConfirm={() => doDelete(deleteAction)}
23
+ onOpen={(e) => e.stopPropagation()}
24
+ onClose={(e) => e.stopPropagation()}
25
+ />
26
+ </>
27
+ ) : null;
28
+ };
29
+
30
+ ImplementationLinksAction.propTypes = {
31
+ doDelete: PropTypes.func,
32
+ _actions: PropTypes.object,
33
+ };
34
+
35
+ export default ImplementationLinksAction;
@@ -0,0 +1,30 @@
1
+ import _ from "lodash/fp";
2
+ import React, { Suspense } from "react";
3
+ import { render } from "@truedat/test/render";
4
+
5
+ import { ConceptImplementationLinks } from "../ConceptImplementationLinks";
6
+
7
+ describe("<ConceptImplementationLinks />", () => {
8
+ const renderOpts = {
9
+ messages: {
10
+ en: {
11
+ "implementationDeletedRelation.table.title": "Deleted Implementations",
12
+ "implementationRelation.table.title": "Link to Implementation",
13
+ },
14
+ },
15
+ };
16
+
17
+ const props = {
18
+ groups: [["group", [{ implementation_key: "foo" }]]],
19
+ };
20
+
21
+ it("matches the latest snapshot", () => {
22
+ const { container } = render(
23
+ <Suspense fallback={null}>
24
+ <ConceptImplementationLinks {...props} />
25
+ </Suspense>,
26
+ renderOpts
27
+ );
28
+ expect(container).toMatchSnapshot();
29
+ });
30
+ });
@@ -0,0 +1,31 @@
1
+ import _ from "lodash/fp";
2
+ import React, { Suspense } from "react";
3
+ import { render } from "@truedat/test/render";
4
+ import { ImplementationLinksAction } from "../ImplementationLinksAction";
5
+
6
+ describe("<ImplementationLinksAction />", () => {
7
+ const renderOpts = {
8
+ messages: {
9
+ en: {
10
+ "conceptRelation.implementation.actions.delete.confirmation.header":
11
+ "Delete Link Implementation",
12
+ "conceptRelation.implementation.actions.delete.confirmation.content":
13
+ "You are going to delete this link. Are you sure?",
14
+ },
15
+ },
16
+ };
17
+ const props = {
18
+ id: 1,
19
+ _actions: { delete: { foo: "bar" } },
20
+ doDelete: jest.fn(),
21
+ };
22
+ it("matches the latest snapshot", () => {
23
+ const { container } = render(
24
+ <Suspense fallback={null}>
25
+ <ImplementationLinksAction {...props} />
26
+ </Suspense>,
27
+ renderOpts
28
+ );
29
+ expect(container).toMatchSnapshot();
30
+ });
31
+ });
@@ -0,0 +1,20 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<ConceptImplementationLinks /> matches the latest snapshot 1`] = `
4
+ <div>
5
+ <div
6
+ class="ui bottom attached segment"
7
+ style="display: none;"
8
+ >
9
+ <div
10
+ class="ui vertical segment"
11
+ >
12
+ <h3
13
+ class="ui header"
14
+ >
15
+ group
16
+ </h3>
17
+ </div>
18
+ </div>
19
+ </div>
20
+ `;
@@ -31,6 +31,7 @@ exports[`<ConceptRelationRow /> matches the latest snapshot 1`] = `
31
31
  >
32
32
  <lazy
33
33
  id={42}
34
+ resourceType="concept"
34
35
  />
35
36
  </TableCell>
36
37
  </TableRow>
@@ -22,5 +22,10 @@ exports[`<ConceptRelationsRoutes /> matches the latest snapshot 1`] = `
22
22
  path="/concepts/:business_concept_id/versions/:id/links/concepts"
23
23
  render={[Function]}
24
24
  />
25
+ <Route
26
+ exact={true}
27
+ path="/concepts/:business_concept_id/versions/:id/links/implementations"
28
+ render={[Function]}
29
+ />
25
30
  </Switch>
26
31
  `;
@@ -0,0 +1,10 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<ImplementationLinksAction /> matches the latest snapshot 1`] = `
4
+ <div>
5
+ <i
6
+ aria-hidden="true"
7
+ class="red trash alternate outline icon"
8
+ />
9
+ </div>
10
+ `;
@@ -0,0 +1,54 @@
1
+ import _ from "lodash/fp";
2
+ import { getConceptImplementationLinks } from "..";
3
+
4
+ const foo = {
5
+ deleted_at: "2020-05-29 09:22:24Z",
6
+ id: "11",
7
+ name: "FOO",
8
+ resource_id: 5454,
9
+ resource_type: "implementation",
10
+ tags: [],
11
+ updated_at: "2020-05-20 13:24:33Z",
12
+ };
13
+
14
+ const bar = {
15
+ deleted_at: "",
16
+ id: "12",
17
+ name: "BAR",
18
+ resource_id: 5427,
19
+ resource_type: "implementation",
20
+ tags: [],
21
+ updated_at: "2020-05-20 13:24:33Z",
22
+ };
23
+ const baz = {
24
+ deleted_at: "",
25
+ id: "14",
26
+ name: "BAZ",
27
+ resource_id: 5430,
28
+ resource_type: "implementation",
29
+ tags: ["tag"],
30
+ updated_at: "2020-05-20 13:24:33Z",
31
+ };
32
+ const other = {
33
+ deleted_at: "",
34
+ id: "15",
35
+ name: "OTHER",
36
+ resource_id: 5450,
37
+ resource_type: "data_structure",
38
+ tags: ["business_concept_to_field_master"],
39
+ updated_at: "2020-05-20 13:24:33Z",
40
+ };
41
+ const conceptLinks = [foo, bar, baz, other];
42
+
43
+ describe("selectors: getConceptImplementationLinks", () => {
44
+ const state = { conceptLinks };
45
+
46
+ it("should return implementation links with resource_id as id", () => {
47
+ const links = _.flow(getConceptImplementationLinks)(state);
48
+ expect(links).toMatchObject([
49
+ ["tag", [{ ...baz, id: baz.resource_id }]],
50
+ ["\uffee", [{ ...bar, id: bar.resource_id }]],
51
+ ["deleted", [{ ...foo, id: foo.resource_id }]],
52
+ ]);
53
+ });
54
+ });
@@ -0,0 +1,34 @@
1
+ import _ from "lodash/fp";
2
+
3
+ import { createSelector } from "reselect";
4
+ import { accentInsensitivePathOrder } from "@truedat/core/services/sort";
5
+
6
+ const getLinks = ({ conceptLinks }) => conceptLinks;
7
+ const orderedLinks = (links) =>
8
+ _.flow(
9
+ _.filter(_.propEq("resource_type", "implementation")),
10
+ _.map((impl) => ({ ...impl, id: impl.resource_id })),
11
+ _.sortBy(accentInsensitivePathOrder("implementation_key"))
12
+ )(links);
13
+
14
+ const implementationLinks = _.flow(
15
+ orderedLinks,
16
+ _.filter(({ deleted_at }) => _.isEmpty(deleted_at)),
17
+ _.groupBy(_.pathOr("\uffee")("tags[0]")),
18
+ _.toPairs,
19
+ _.sortBy(([k]) => k)
20
+ );
21
+
22
+ const deletedImplementationLinks = _.flow(
23
+ orderedLinks,
24
+ _.filter(({ deleted_at }) => _.negate(_.isEmpty)(deleted_at))
25
+ );
26
+ export const getConceptImplementationLinks = createSelector(
27
+ [getLinks],
28
+ (links) => {
29
+ return [
30
+ ...implementationLinks(links),
31
+ ["deleted", deletedImplementationLinks(links)],
32
+ ];
33
+ }
34
+ );
@@ -19,10 +19,10 @@ export const StructureLink = ({ resource_type, resource_id: id, name }) =>
19
19
  StructureLink.propTypes = {
20
20
  resource_type: PropTypes.string,
21
21
  resource_id: PropTypes.number,
22
- name: PropTypes.string
22
+ name: PropTypes.string,
23
23
  };
24
24
 
25
- const dateDecorator = date => (
25
+ const dateDecorator = (date) => (
26
26
  <Label className={"alert warning"}>
27
27
  <Icon name="warning circle" color="red" />
28
28
  <Moment locale="es" date={date} format="YYYY-MM-DD HH:mm" />
@@ -33,17 +33,17 @@ const getLinks = ({ conceptLinks }) => conceptLinks;
33
33
  const getColumns = ({ structuresColumns }) => structuresColumns;
34
34
  const getStructureTypes = ({ structureTypes }) => structureTypes;
35
35
 
36
- const mapColumn = column =>
36
+ const mapColumn = (column) =>
37
37
  _.propEq("name", "name")(column)
38
38
  ? {
39
39
  ...column,
40
40
  fieldSelector: _.pick(["resource_type", "resource_id", "name"]),
41
- fieldDecorator: StructureLink
41
+ fieldDecorator: StructureLink,
42
42
  }
43
43
  : column;
44
44
 
45
45
  const withActions = (conceptLinks, columns) =>
46
- _.all(c => _.isEmpty(_.prop("_actions")(c)))(conceptLinks)
46
+ _.all((c) => _.isEmpty(_.prop("_actions")(c)))(conceptLinks)
47
47
  ? _.map(mapColumn)(columns)
48
48
  : [
49
49
  ..._.map(mapColumn)(columns),
@@ -51,20 +51,20 @@ const withActions = (conceptLinks, columns) =>
51
51
  textAlign: "center",
52
52
  name: "_actions",
53
53
  fieldSelector: _.prop("_actions"),
54
- fieldDecorator: ConceptLinkActions
55
- }
54
+ fieldDecorator: ConceptLinkActions,
55
+ },
56
56
  ];
57
57
 
58
58
  const withDeletedAttribute = (conceptLinks, columns) =>
59
- _.all(c => _.negate(_.isEmpty)(_.prop("deleted_at")(c)))(conceptLinks)
60
- ? _.map(c =>
59
+ _.all((c) => _.negate(_.isEmpty)(_.prop("deleted_at")(c)))(conceptLinks)
60
+ ? _.map((c) =>
61
61
  _.propEq("name", "updated_at")(c)
62
62
  ? {
63
63
  name: "deleted_at",
64
64
  sort: { name: "deleted_at" },
65
65
  fieldDecorator: dateDecorator,
66
66
  textAlign: "center",
67
- width: 2
67
+ width: 2,
68
68
  }
69
69
  : c
70
70
  )(columns)
@@ -72,22 +72,22 @@ const withDeletedAttribute = (conceptLinks, columns) =>
72
72
 
73
73
  const mapColumns = (conceptLinks, columns) =>
74
74
  _.flow(
75
- cols => withActions(conceptLinks, cols),
76
- cols => withDeletedAttribute(conceptLinks, cols)
75
+ (cols) => withActions(conceptLinks, cols),
76
+ (cols) => withDeletedAttribute(conceptLinks, cols)
77
77
  )(columns);
78
78
 
79
79
  const withHeaders = (pair, columns) => [
80
80
  mapColumns(_.last(pair), columns),
81
- pair
81
+ pair,
82
82
  ];
83
83
 
84
84
  const add_structure_type = (link, structureTypes) => ({
85
85
  ...link,
86
86
  data_structure_type:
87
- _.find(_.propEq("structure_type", link.type))(structureTypes) || {}
87
+ _.find(_.propEq("structure_type", link.type))(structureTypes) || {},
88
88
  });
89
89
 
90
- const orderedLinks = links =>
90
+ const orderedLinks = (links) =>
91
91
  _.flow(
92
92
  _.filter(_.propEq("resource_type", "data_structure")),
93
93
  _.sortBy(accentInsensitivePathOrder("name"))
@@ -99,7 +99,7 @@ const conceptLinks = (links, columns) =>
99
99
  _.groupBy(_.pathOr("\uffee")("tags[0]")),
100
100
  _.toPairs,
101
101
  _.sortBy(([k]) => k),
102
- _.map(pair => withHeaders(pair, columns))
102
+ _.map((pair) => withHeaders(pair, columns))
103
103
  )(links);
104
104
  const conceptDeletedLinks = (links, columns) => {
105
105
  const deleted = _.flow(
@@ -111,12 +111,13 @@ const conceptDeletedLinks = (links, columns) => {
111
111
  export const getConceptLinks = createSelector(
112
112
  [getLinks, getColumns, getStructureTypes],
113
113
  (links, columns, structureTypes) => {
114
- const linksWithDataStructureType = _.map(link =>
114
+ const linksWithDataStructureType = _.map((link) =>
115
115
  add_structure_type(link, structureTypes)
116
116
  )(links);
117
+
117
118
  return [
118
119
  ...conceptLinks(linksWithDataStructureType, columns),
119
- ...conceptDeletedLinks(linksWithDataStructureType, columns)
120
+ ...conceptDeletedLinks(linksWithDataStructureType, columns),
120
121
  ];
121
122
  }
122
123
  );
@@ -2,6 +2,7 @@ export {
2
2
  getConceptSourceRelations,
3
3
  getConceptTargetRelations,
4
4
  getConceptRelations,
5
- getConceptToConceptRelations
5
+ getConceptToConceptRelations,
6
6
  } from "./getConceptRelations";
7
7
  export { getConceptLinks } from "./getConceptLinks";
8
+ export { getConceptImplementationLinks } from "./getConceptImplementationLinks";
@@ -58,6 +58,8 @@ export default {
58
58
  "concept.sharedTo.header": "Share in",
59
59
  "concept.changeDomain.header": "Edit Domain",
60
60
  "concept.changeDomain.label": "Domain",
61
+ "implementationDeletedRelation.table.title": "Deleted Implementations",
62
+ "implementationRelation.table.title": "Link to Implementation",
61
63
  "conceptRelation.actions.create": "New Link",
62
64
  "conceptRelation.actions.delete.confirmation.content":
63
65
  "The Concept will be unlinked from the field. Are you sure?",
@@ -67,6 +69,10 @@ export default {
67
69
  "conceptRelation.group": "Group",
68
70
  "conceptRelation.group": "Group",
69
71
  "conceptRelation.group.placeholder": "Select Group",
72
+ "conceptRelation.implementation.actions.delete.confirmation.header":
73
+ "Delete Link Implementation",
74
+ "conceptRelation.implementation.actions.delete.confirmation.content":
75
+ "You are going to delete this link. Are you sure?",
70
76
  "conceptRelation.master": "Master relation",
71
77
  "conceptRelation.master.table.title": "Master Link to Structure",
72
78
  "conceptDeletedRelation.table.title": "Deleted Structures",
@@ -242,7 +248,6 @@ export default {
242
248
  "fields.pairlist.url.value.placeholder": "https://example.com",
243
249
  "filters._confidential": "Confidential",
244
250
  "filters.domain": "Domain",
245
- "filters.domain_parents": "Domain",
246
251
  "filters.filiacion": "Filiacion",
247
252
  "filters.link_count": "Data fields",
248
253
  "filters.rule_count": "Quality Rules",
@@ -262,6 +267,12 @@ export default {
262
267
  "Link will be deleted. Are you sure?",
263
268
  "relations.actions.data_field.delete.confirmation.header": "Delete link",
264
269
  "saveConceptFilters.error.name.unique": "Duplicated name",
270
+ "relations.actions.concept.delete.confirmation.header": "Delete link",
271
+ "relations.actions.concept.delete.confirmation.content":
272
+ "Link between concepts will be deleted. Are you sure?",
273
+ "relations.actions.implementation.delete.confirmation.content":
274
+ "Link between implementation and concept will be deleted. Are you sure?",
275
+ "relations.actions.implementation.delete.confirmation.header": "Delete link",
265
276
  "source.bc_caculo": "Calculated from",
266
277
  "source.bc_padre": "Children",
267
278
  "source.bc_parent": "Children",
@@ -272,6 +283,7 @@ export default {
272
283
  "tabs.bg.concept": "Concept",
273
284
  "tabs.bg.history": "History",
274
285
  "tabs.bg.qualityRules": "Quality Rules",
286
+ "tabs.bg.qualityImplementations": "Quality Implementations",
275
287
  "tabs.bg.relations_business_concept": "Related Concepts",
276
288
  "tabs.bg.relations_data_field": "Linkage",
277
289
  "tabs.domains": "Domains",
@@ -60,6 +60,8 @@ export default {
60
60
  "concept.sharedTo.header": "Compartir en",
61
61
  "concept.changeDomain.label": "Dominios",
62
62
  "concept.changeDomain.header": "Editar Dominio",
63
+ "implementationDeletedRelation.table.title": "Implementaciones Eliminadas",
64
+ "implementationRelation.table.title": "Relaciones a Implementación",
63
65
  "conceptRelation.actions.create": "Crear vínculo",
64
66
  "conceptRelation.actions.delete.confirmation.content":
65
67
  "Se eliminará esta vinculación. ¿Estás seguro?",
@@ -68,6 +70,10 @@ export default {
68
70
  "conceptRelation.field.placeholder": "Seleccionar Campo",
69
71
  "conceptRelation.group": "Grupo",
70
72
  "conceptRelation.group.placeholder": "Seleccionar Grupo",
73
+ "conceptRelation.implementation.actions.delete.confirmation.header":
74
+ "Borrar relación",
75
+ "conceptRelation.implementation.actions.delete.confirmation.content":
76
+ "Se eliminará la relación con la implementacion. ¿Está seguro?",
71
77
  "conceptRelation.master": "Relación master",
72
78
  "conceptRelation.master.table.title": "Enlace a Estructura Tipo Master",
73
79
  "conceptDeletedRelation.table.title": "Estructuras Eliminadas",
@@ -244,7 +250,6 @@ export default {
244
250
  "fields.pairlist.url.value.placeholder": "https://ejemplo.es",
245
251
  "filters._confidential": "Confidencial",
246
252
  "filters.domain": "Dominio",
247
- "filters.domain_parents": "Dominio",
248
253
  "filters.filiacion": "Filiacion",
249
254
  "filters.link_count": "Datos",
250
255
  "filters.rule_count": "Reglas de calidad",
@@ -264,6 +269,13 @@ export default {
264
269
  "Se eliminará la vinculación entre el concepto y la estructura. ¿Estás seguro?",
265
270
  "relations.actions.data_field.delete.confirmation.header":
266
271
  "Borrar vínculación",
272
+ "relations.actions.concept.delete.confirmation.header": "Borrar vínculación",
273
+ "relations.actions.concept.delete.confirmation.content":
274
+ "Se eliminará la vinculación entre el conceptos. ¿Estás seguro?",
275
+ "relations.actions.implementation.delete.confirmation.content":
276
+ "Se eliminará la vinculación entre la implementación y el concepto. ¿Estás seguro?",
277
+ "relations.actions.implementation.delete.confirmation.header":
278
+ "Borrar vínculación",
267
279
  "saveConceptFilters.error.name.unique": "Nombre duplicado",
268
280
  "source.bc_caculo": "Cálculado en base a",
269
281
  "source.bc_padre": "Hijos",
@@ -275,6 +287,7 @@ export default {
275
287
  "tabs.bg.concept": "Concepto",
276
288
  "tabs.bg.history": "Historial",
277
289
  "tabs.bg.qualityRules": "Reglas de Calidad",
290
+ "tabs.bg.qualityImplementations": "Implementaciones",
278
291
  "tabs.bg.relations_business_concept": "Conceptos relacionados",
279
292
  "tabs.bg.relations_data_field": "Vinculación",
280
293
  "tabs.domains": "Dominios",
@@ -37,6 +37,7 @@ export const DomainDropdownSelector = ({
37
37
  domainOptions: options,
38
38
  hideLabel,
39
39
  invalid,
40
+ multiple = false,
40
41
  onChange,
41
42
  value: defaultValue,
42
43
  }) => {
@@ -95,8 +96,9 @@ export const DomainDropdownSelector = ({
95
96
 
96
97
  const handleBlur = () => {
97
98
  setQuery();
98
- if (!_.isNil(value)) {
99
- const domain = _.find({ id: value })(options);
99
+ const lastesSelection = _.isNumber(value) ? value : _.last(value);
100
+ if (!_.isNil(lastesSelection)) {
101
+ const domain = _.find({ id: lastesSelection })(options);
100
102
  const ancestors = _.map("id")(domain.ancestors);
101
103
  const ids = [...ancestors, domain.id];
102
104
  setOpen(ancestors);
@@ -169,6 +171,7 @@ export const DomainDropdownSelector = ({
169
171
  )(filteredOptions)}
170
172
  placeholder={formatMessage({ id: "domain.selector.placeholder" })}
171
173
  search={_.identity}
174
+ multiple={multiple}
172
175
  selection
173
176
  value={value}
174
177
  />
@@ -182,8 +185,9 @@ DomainDropdownSelector.propTypes = {
182
185
  domainOptions: PropTypes.array,
183
186
  hideLabel: PropTypes.bool,
184
187
  invalid: PropTypes.bool,
188
+ multiple: PropTypes.bool,
185
189
  onChange: PropTypes.func,
186
- value: PropTypes.number,
190
+ value: PropTypes.oneOfType([PropTypes.number, PropTypes.array]),
187
191
  };
188
192
 
189
193
  const mapStateToProps = (state, props) => ({
@@ -38,6 +38,7 @@ exports[`<DomainDropdownSelector /> matches the latest snapshot 1`] = `
38
38
  class="dropdown icon"
39
39
  />
40
40
  <div
41
+ aria-multiselectable="false"
41
42
  class="menu transition"
42
43
  role="listbox"
43
44
  >