@truedat/df 5.0.1 → 5.0.2

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 (25) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/package.json +5 -5
  3. package/src/api/hierarchies.js +4 -0
  4. package/src/components/hierarchies/Hierarchies.js +43 -0
  5. package/src/components/hierarchies/HierarchiesView.js +32 -0
  6. package/src/components/hierarchies/Hierarchy.js +301 -0
  7. package/src/components/hierarchies/HierarchyCrumbs.js +22 -0
  8. package/src/components/hierarchies/HierarchyRoutes.js +105 -0
  9. package/src/components/hierarchies/HierarchyView.js +261 -0
  10. package/src/components/hierarchies/__tests__/Hierarchies.spec.js +34 -0
  11. package/src/components/hierarchies/__tests__/HierarchiesView.spec.js +34 -0
  12. package/src/components/hierarchies/__tests__/Hierarchy.spec.js +42 -0
  13. package/src/components/hierarchies/__tests__/HierarchyCrumbs.spec.js +11 -0
  14. package/src/components/hierarchies/__tests__/HierarchyView.spec.js +36 -0
  15. package/src/components/hierarchies/__tests__/__snapshots__/Hierarchies.spec.js.snap +48 -0
  16. package/src/components/hierarchies/__tests__/__snapshots__/HierarchiesView.spec.js.snap +81 -0
  17. package/src/components/hierarchies/__tests__/__snapshots__/Hierarchy.spec.js.snap +186 -0
  18. package/src/components/hierarchies/__tests__/__snapshots__/HierarchyCrumbs.spec.js.snap +102 -0
  19. package/src/components/hierarchies/__tests__/__snapshots__/HierarchyView.spec.js.snap +145 -0
  20. package/src/components/hierarchies/index.js +3 -0
  21. package/src/components/index.js +1 -0
  22. package/src/hooks/useHierarchies.js +111 -0
  23. package/src/messages/en.js +94 -72
  24. package/src/messages/es.js +99 -77
  25. package/src/styles/hierarchy.less +91 -0
@@ -0,0 +1,261 @@
1
+ import _ from "lodash/fp";
2
+ import React, { useState, useEffect } from "react";
3
+ import PropTypes from "prop-types";
4
+ import { Button, Header, Icon, Segment, Label } from "semantic-ui-react";
5
+ import { useIntl } from "react-intl";
6
+ import { Link } from "react-router-dom";
7
+ import { linkTo } from "@truedat/core/routes";
8
+ import { CSVFileModal } from "@truedat/core/components";
9
+ import { HistoryBackButton } from "@truedat/core/components";
10
+ import { ConfirmModal } from "@truedat/core/components";
11
+ import HierarchyCrumbs from "./HierarchyCrumbs";
12
+ import Hierarchy from "./Hierarchy";
13
+
14
+ const getLastId = _.flow(
15
+ _.pathOr([], "nodes"),
16
+ _.map("id"),
17
+ _.concat(0),
18
+ _.max
19
+ );
20
+
21
+ const validateHierarchy = (hierarchy) => {
22
+ const validatedHierarchy = {
23
+ ...hierarchy,
24
+ nodes: _.flow(
25
+ _.pathOr([], "nodes"),
26
+ _.map((node) => {
27
+ const hasError =
28
+ _.isEmpty(node.name) ||
29
+ node.name === "" ||
30
+ hasDuplicatedSiblings(node, hierarchy);
31
+ return {
32
+ ...node,
33
+ error: hasError,
34
+ };
35
+ })
36
+ )(hierarchy),
37
+ };
38
+
39
+ const valid =
40
+ hierarchy.name !== "" &&
41
+ _.flow(
42
+ _.pathOr([], "nodes"),
43
+ _.filter({ error: true }),
44
+ _.isEmpty
45
+ )(validatedHierarchy);
46
+ return [valid, validatedHierarchy];
47
+ };
48
+
49
+ const hasDuplicatedSiblings = ({ parentId, name }, { nodes }) => {
50
+ return _.size(_.filter({ parentId, name })(nodes)) > 1;
51
+ };
52
+
53
+ export const HierarchyView = ({
54
+ isEditionMode = false,
55
+ originalHierarchy,
56
+ onSubmit,
57
+ onDelete,
58
+ }) => {
59
+ const { formatMessage } = useIntl();
60
+ const [hierarchy, setHierarchy] = useState(originalHierarchy);
61
+ const [maxId, setMaxId] = useState(getLastId(originalHierarchy));
62
+ const [deletedNodes, setDeletedNodes] = useState(0);
63
+ const [isValid, setIsValid] = useState();
64
+ useEffect(() => {
65
+ setIsValid(validateHierarchy(originalHierarchy)[0]);
66
+ }, [originalHierarchy]);
67
+
68
+ const validateAndSetHierarchy = (hierarchy) => {
69
+ const [isValid, validatedHierarchy] = validateHierarchy(hierarchy);
70
+ setIsValid(isValid);
71
+ setHierarchy(validatedHierarchy);
72
+ };
73
+
74
+ const addedNodes = _.flow(
75
+ _.pathOr([], "nodes"),
76
+ _.filter({ editionStatus: "added" }),
77
+ _.size
78
+ )(hierarchy);
79
+
80
+ const modifiedNodes = _.flow(
81
+ _.pathOr([], "nodes"),
82
+ _.filter({ editionStatus: "dirty" }),
83
+ _.size
84
+ )(hierarchy);
85
+
86
+ const updateMetadata = ({ name, description }) =>
87
+ validateAndSetHierarchy({
88
+ ...hierarchy,
89
+ name: name,
90
+ description: description,
91
+ });
92
+
93
+ const updateNode = (id, { name, description }) => {
94
+ validateAndSetHierarchy({
95
+ ...hierarchy,
96
+ nodes: _.map((node) =>
97
+ node.id == id
98
+ ? {
99
+ ...node,
100
+ name: name,
101
+ description: description,
102
+ error: false,
103
+ editionStatus: _.flow(_.find({ id }), (originalNode) =>
104
+ originalNode !== undefined &&
105
+ (originalNode?.name != name ||
106
+ originalNode?.description != description)
107
+ ? "dirty"
108
+ : node.editionStatus == "dirty"
109
+ ? null
110
+ : node.editionStatus
111
+ )(originalHierarchy.nodes),
112
+ }
113
+ : node
114
+ )(hierarchy.nodes),
115
+ });
116
+ };
117
+
118
+ const addNode = (parentId = null, name) => {
119
+ const id = maxId + 1;
120
+ validateAndSetHierarchy({
121
+ ...hierarchy,
122
+ nodes: [
123
+ ...hierarchy.nodes,
124
+ { id, parentId, name, editionStatus: "added", description: null },
125
+ ],
126
+ });
127
+ setMaxId(id);
128
+ };
129
+
130
+ const removeNode = (id) => {
131
+ _.flow(
132
+ _.pathOr([], "nodes"),
133
+ _.find({ id: id }),
134
+ _.isEmpty
135
+ )(originalHierarchy)
136
+ ? null
137
+ : setDeletedNodes(deletedNodes + 1);
138
+ validateAndSetHierarchy({
139
+ ...hierarchy,
140
+ nodes: _.reject({ id })(hierarchy.nodes),
141
+ });
142
+ };
143
+
144
+ return (
145
+ <>
146
+ <HierarchyCrumbs name={hierarchy.name} />
147
+ <Segment>
148
+ <Header as="h2">
149
+ <div className="actions_wrapper">
150
+ <div className="actions">
151
+ {isEditionMode ? (
152
+ <>
153
+ <Button
154
+ floated="right"
155
+ onClick={() => onSubmit(hierarchy)}
156
+ primary
157
+ loading={false}
158
+ disabled={!isValid}
159
+ content={formatMessage({ id: "actions.save" })}
160
+ title={formatMessage(
161
+ {
162
+ id: "hierarchies.modifications.summary",
163
+ },
164
+ {
165
+ added: addedNodes,
166
+ modified: modifiedNodes,
167
+ deleted: deletedNodes,
168
+ }
169
+ )}
170
+ />
171
+
172
+ <HistoryBackButton
173
+ floated="right"
174
+ content={formatMessage({ id: "actions.cancel" })}
175
+ />
176
+
177
+ <CSVFileModal
178
+ param="hierarchy"
179
+ onSubmit={(nodes) => {
180
+ const csvHierarchy = {
181
+ ...hierarchy,
182
+ nodes: _.map((node) => {
183
+ const id = parseInt(node.id);
184
+ const parentId = parseInt(node.parentId);
185
+ return {
186
+ ...node,
187
+ id: _.isNaN(id) ? null : id,
188
+ parentId: _.isNaN(parentId) ? null : parentId,
189
+ };
190
+ })(nodes),
191
+ };
192
+ validateAndSetHierarchy(csvHierarchy);
193
+ setMaxId(getLastId(hierarchy));
194
+ }}
195
+ />
196
+ </>
197
+ ) : (
198
+ <>
199
+ <Button
200
+ floated="right"
201
+ primary
202
+ as={Link}
203
+ to={linkTo.HIERARCHY_EDIT({ hierarchyId: hierarchy.id })}
204
+ content={formatMessage({ id: "actions.edit" })}
205
+ />
206
+
207
+ <ConfirmModal
208
+ icon="trash"
209
+ trigger={
210
+ <Button
211
+ floated="right"
212
+ secondary
213
+ loading={false}
214
+ disabled={false}
215
+ content={formatMessage({ id: "actions.delete" })}
216
+ />
217
+ }
218
+ header={formatMessage({
219
+ id: "hierarchy.actions.delete.confirmation.header",
220
+ })}
221
+ size="small"
222
+ content={formatMessage({
223
+ id: "hierarchy.actions.delete.confirmation.content",
224
+ })}
225
+ onConfirm={() => onDelete(hierarchy.id)}
226
+ />
227
+ </>
228
+ )}
229
+ </div>
230
+ </div>
231
+ <Icon circular name="tree" />
232
+ <Header.Content>
233
+ {hierarchy.name
234
+ ? hierarchy.name
235
+ : formatMessage({ id: "hierarchy.name" })}
236
+ <Header.Subheader>{hierarchy.description}</Header.Subheader>
237
+ </Header.Content>
238
+ </Header>
239
+ <Segment attached="bottom">
240
+ <Hierarchy
241
+ hierarchy={hierarchy}
242
+ updateMetadata={updateMetadata}
243
+ updateNode={updateNode}
244
+ addNode={addNode}
245
+ removeNode={removeNode}
246
+ isEditionMode={isEditionMode}
247
+ />
248
+ </Segment>
249
+ </Segment>
250
+ </>
251
+ );
252
+ };
253
+
254
+ HierarchyView.propTypes = {
255
+ isEditionMode: PropTypes.bool,
256
+ originalHierarchy: PropTypes.object,
257
+ onDelete: PropTypes.func,
258
+ onSubmit: PropTypes.func,
259
+ };
260
+
261
+ export default HierarchyView;
@@ -0,0 +1,34 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import Hierarchies from "../Hierarchies";
4
+ import en from "../../../messages/en";
5
+
6
+ const renderOpts = { messages: { en: en } };
7
+
8
+ const data = [
9
+ {
10
+ id: 1,
11
+ name: "Baggins",
12
+ nodes: [
13
+ {
14
+ id: 11,
15
+ name: "Fosco",
16
+ parentId: null,
17
+ },
18
+ ],
19
+ },
20
+ ];
21
+
22
+ jest.mock("../../../hooks/useHierarchies", () => ({
23
+ useHierarchies: jest.fn(() => ({
24
+ loading: false,
25
+ data: data,
26
+ })),
27
+ }));
28
+
29
+ describe("<Hierarchies />", () => {
30
+ it("matches the last snapshot", () => {
31
+ const { container } = render(<Hierarchies />, renderOpts);
32
+ expect(container).toMatchSnapshot();
33
+ });
34
+ });
@@ -0,0 +1,34 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import HierarchiesView from "../HierarchiesView";
4
+ import en from "../../../messages/en";
5
+
6
+ const renderOpts = { messages: { en: en } };
7
+
8
+ const data = [
9
+ {
10
+ id: 1,
11
+ name: "Baggins",
12
+ nodes: [
13
+ {
14
+ id: 11,
15
+ name: "Fosco",
16
+ parentId: null,
17
+ },
18
+ ],
19
+ },
20
+ ];
21
+
22
+ jest.mock("../../../hooks/useHierarchies", () => ({
23
+ useHierarchies: jest.fn(() => ({
24
+ loading: false,
25
+ data: data,
26
+ })),
27
+ }));
28
+
29
+ describe("<HierarchiesView />", () => {
30
+ it("matche the last snapshot", () => {
31
+ const { container } = render(<HierarchiesView />, renderOpts);
32
+ expect(container).toMatchSnapshot();
33
+ });
34
+ });
@@ -0,0 +1,42 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import Hierarchy from "../Hierarchy";
4
+ import en from "../../../messages/en";
5
+
6
+ const hierarchy = {
7
+ id: 1,
8
+ name: "Baggins",
9
+ description: "bar",
10
+ nodes: [
11
+ {
12
+ id: 11,
13
+ name: "Fosco",
14
+ parentId: null,
15
+ },
16
+ {
17
+ id: 12,
18
+ name: "Fosco children",
19
+ parentId: 11,
20
+ },
21
+ ],
22
+ };
23
+ const props = { hierarchy: hierarchy, isEditionMode: false };
24
+
25
+ const renderOpts = {
26
+ messages: { en: en },
27
+ };
28
+
29
+ describe("<Hierarchy />", () => {
30
+ it("matches the last snapshot with edition mode false", () => {
31
+ const { container } = render(<Hierarchy {...props} />, renderOpts);
32
+ expect(container).toMatchSnapshot();
33
+ });
34
+
35
+ it("matches snapshot with edition mode", () => {
36
+ const { container } = render(
37
+ <Hierarchy {...{ ...props, isEditionMode: true }} />,
38
+ renderOpts
39
+ );
40
+ expect(container).toMatchSnapshot();
41
+ });
42
+ });
@@ -0,0 +1,11 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import HierarchyCrumbs from "../HierarchyCrumbs";
4
+
5
+ describe("<HierarchyCrumbs />", () => {
6
+ it("matches the latest snapshot", () => {
7
+ const props = { name: "foo" };
8
+ const container = render(<HierarchyCrumbs {...props} />);
9
+ expect(container).toMatchSnapshot();
10
+ });
11
+ });
@@ -0,0 +1,36 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import HierarchyView from "../HierarchyView";
4
+ import en from "../../../messages/en";
5
+
6
+ const renderOpts = {
7
+ messages: {
8
+ en: { ...en, "actions.edit": "Edit", "actions.delete": "Delete" },
9
+ },
10
+ };
11
+
12
+ const originalHierarchy = {
13
+ id: 1,
14
+ name: "Baggins",
15
+ nodes: [
16
+ {
17
+ id: 11,
18
+ name: "Fosco",
19
+ parentId: null,
20
+ },
21
+ ],
22
+ };
23
+
24
+ const opts = {
25
+ isEditionMode: false,
26
+ originalHierarchy,
27
+ loading: false,
28
+ onSubmit: jest.fn(),
29
+ };
30
+
31
+ describe("<HierarchyView />", () => {
32
+ it("matches the last snapshot", () => {
33
+ const { container } = render(<HierarchyView {...opts} />, renderOpts);
34
+ expect(container).toMatchSnapshot();
35
+ });
36
+ });
@@ -0,0 +1,48 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<Hierarchies /> matches the last snapshot 1`] = `
4
+ <div>
5
+ <table
6
+ class="ui celled table"
7
+ >
8
+ <thead
9
+ class=""
10
+ >
11
+ <tr
12
+ class=""
13
+ >
14
+ <th
15
+ class=""
16
+ >
17
+ Hierarchy name
18
+ </th>
19
+ <th
20
+ class=""
21
+ >
22
+ Description
23
+ </th>
24
+ </tr>
25
+ </thead>
26
+ <tbody
27
+ class=""
28
+ >
29
+ <tr
30
+ class=""
31
+ >
32
+ <td
33
+ class=""
34
+ >
35
+ <a
36
+ href="/hierarchies/1"
37
+ >
38
+ Baggins
39
+ </a>
40
+ </td>
41
+ <td
42
+ class=""
43
+ />
44
+ </tr>
45
+ </tbody>
46
+ </table>
47
+ </div>
48
+ `;
@@ -0,0 +1,81 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<HierarchiesView /> matche the last snapshot 1`] = `
4
+ <div>
5
+ <div
6
+ class="ui segment"
7
+ >
8
+ <h2
9
+ class="ui header"
10
+ >
11
+ <a
12
+ class="ui primary right floated button"
13
+ href="/hierarchies/new"
14
+ role="button"
15
+ >
16
+ New hierarchy
17
+ </a>
18
+ <i
19
+ aria-hidden="true"
20
+ class="tree circular icon"
21
+ />
22
+ <div
23
+ class="content"
24
+ >
25
+ Hierarchies
26
+ <div
27
+ class="sub header"
28
+ >
29
+ List of hierarchies
30
+ </div>
31
+ </div>
32
+ </h2>
33
+ <div
34
+ class="ui bottom attached segment"
35
+ >
36
+ <table
37
+ class="ui celled table"
38
+ >
39
+ <thead
40
+ class=""
41
+ >
42
+ <tr
43
+ class=""
44
+ >
45
+ <th
46
+ class=""
47
+ >
48
+ Hierarchy name
49
+ </th>
50
+ <th
51
+ class=""
52
+ >
53
+ Description
54
+ </th>
55
+ </tr>
56
+ </thead>
57
+ <tbody
58
+ class=""
59
+ >
60
+ <tr
61
+ class=""
62
+ >
63
+ <td
64
+ class=""
65
+ >
66
+ <a
67
+ href="/hierarchies/1"
68
+ >
69
+ Baggins
70
+ </a>
71
+ </td>
72
+ <td
73
+ class=""
74
+ />
75
+ </tr>
76
+ </tbody>
77
+ </table>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ `;