@truedat/dd 4.43.3 → 4.43.6
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/CHANGELOG.md +12 -0
- package/package.json +6 -6
- package/src/api.js +3 -0
- package/src/components/DictionaryRoutes.js +10 -0
- package/src/components/StructuresUploadEvents.js +38 -0
- package/src/components/StructuresUploadEventsTable.js +183 -0
- package/src/components/__tests__/StructuresUploadEventsTable.spec.js +84 -0
- package/src/components/__tests__/__snapshots__/DictionaryRoutes.spec.js.snap +4 -0
- package/src/components/__tests__/__snapshots__/StructuresUploadEventsTable.spec.js.snap +130 -0
- package/src/messages/en.js +18 -0
- package/src/messages/es.js +21 -0
- package/src/reducers/__tests__/structuresUploadEvents.spec.js +46 -0
- package/src/reducers/__tests__/structuresUploadEventsLoading.spec.js +26 -0
- package/src/reducers/ddMessage.js +53 -30
- package/src/reducers/index.js +4 -0
- package/src/reducers/structuresUploadEvents.js +16 -0
- package/src/reducers/structuresUploadEventsLoading.js +16 -0
- package/src/routines.js +1 -0
- package/src/sagas/__tests__/fetchStructuresUploadEvents.spec.js +79 -0
- package/src/sagas/__tests__/uploadStructures.spec.js +3 -2
- package/src/sagas/fetchStructuresUploadEvents.js +29 -0
- package/src/sagas/index.js +3 -0
- package/src/sagas/uploadStructures.js +2 -2
- package/src/selectors/__tests__/getStructuresUploadEventColumns.spec.js +20 -0
- package/src/selectors/getStructuresUploadEventColumns.js +74 -0
- package/src/selectors/index.js +4 -0
- package/src/styles/structuresUploadEventColumns.less +24 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [4.43.6] 2022-05-06
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- [TD-4586] Simplify accepted CSV message and include link to 'my loads'.
|
|
8
|
+
|
|
9
|
+
## [4.43.4] 2022-05-05
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- [TD-4586] CSV bulk update events table
|
|
14
|
+
|
|
3
15
|
## [4.43.3] 2022-05-04
|
|
4
16
|
|
|
5
17
|
### Changed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/dd",
|
|
3
|
-
"version": "4.43.
|
|
3
|
+
"version": "4.43.6",
|
|
4
4
|
"description": "Truedat Web Data Dictionary",
|
|
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.43.
|
|
37
|
+
"@truedat/test": "4.43.6",
|
|
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",
|
|
@@ -84,9 +84,9 @@
|
|
|
84
84
|
]
|
|
85
85
|
},
|
|
86
86
|
"dependencies": {
|
|
87
|
-
"@truedat/auth": "4.43.
|
|
88
|
-
"@truedat/core": "4.43.
|
|
89
|
-
"@truedat/df": "4.43.
|
|
87
|
+
"@truedat/auth": "4.43.6",
|
|
88
|
+
"@truedat/core": "4.43.6",
|
|
89
|
+
"@truedat/df": "4.43.6",
|
|
90
90
|
"lodash": "^4.17.21",
|
|
91
91
|
"path-to-regexp": "^1.7.0",
|
|
92
92
|
"prop-types": "^15.7.2",
|
|
@@ -107,5 +107,5 @@
|
|
|
107
107
|
"react-dom": ">= 16.8.6 < 17",
|
|
108
108
|
"semantic-ui-react": ">= 0.88.2 < 2.1"
|
|
109
109
|
},
|
|
110
|
-
"gitHead": "
|
|
110
|
+
"gitHead": "848143d79486499dd9deadea6a545929b5cdb39c"
|
|
111
111
|
}
|
package/src/api.js
CHANGED
|
@@ -40,6 +40,8 @@ const API_USER_SEARCH_FILTER = "/api/user_search_filters/:id";
|
|
|
40
40
|
const API_GET_USER_SEARCH_FILTERS = "/api/user_search_filters/me";
|
|
41
41
|
const API_STRUCTURES_UPLOAD =
|
|
42
42
|
"/api/data_structures/bulk_update_template_content";
|
|
43
|
+
const API_STRUCTURES_UPLOAD_EVENTS =
|
|
44
|
+
"/api/data_structures/bulk_update_template_content_events";
|
|
43
45
|
const API_STRUCTURE_NOTE = "/api/data_structures/:data_structure_id/notes/:id";
|
|
44
46
|
const API_STRUCTURE_NOTES = "/api/data_structures/:data_structure_id/notes";
|
|
45
47
|
const API_UNIT_DOMAINS = "/api/unit_domains";
|
|
@@ -85,6 +87,7 @@ export {
|
|
|
85
87
|
API_STRUCTURES_TAG,
|
|
86
88
|
API_STRUCTURES_TAGS,
|
|
87
89
|
API_STRUCTURES_UPLOAD,
|
|
90
|
+
API_STRUCTURES_UPLOAD_EVENTS,
|
|
88
91
|
API_STRUCTURE_NOTE,
|
|
89
92
|
API_STRUCTURE_NOTES,
|
|
90
93
|
API_UNIT_DOMAINS,
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
STRUCTURE_TAGS,
|
|
9
9
|
STRUCTURE_TYPES,
|
|
10
10
|
STRUCTURES,
|
|
11
|
+
STRUCTURES_UPLOAD_EVENTS,
|
|
11
12
|
SYSTEMS,
|
|
12
13
|
} from "@truedat/core/routes";
|
|
13
14
|
import PendingStructureNotes from "./PendingStructureNotes";
|
|
@@ -19,6 +20,7 @@ import ProfileGroup from "./ProfileGroup";
|
|
|
19
20
|
import StructureFiltersLoader from "./StructureFiltersLoader";
|
|
20
21
|
import StructuresLoader from "./StructuresLoader";
|
|
21
22
|
import StructuresRoutes from "./StructuresRoutes";
|
|
23
|
+
import StructuresUploadEvents from "./StructuresUploadEvents";
|
|
22
24
|
import StructureTypesLoader from "./StructureTypesLoader";
|
|
23
25
|
import StructureTagsRoutes from "./StructureTagsRoutes";
|
|
24
26
|
import StructureTypesRoutes from "./StructureTypesRoutes";
|
|
@@ -88,6 +90,14 @@ export const DictionaryRoutes = () => {
|
|
|
88
90
|
</>
|
|
89
91
|
)}
|
|
90
92
|
/>
|
|
93
|
+
<Route
|
|
94
|
+
path={STRUCTURES_UPLOAD_EVENTS}
|
|
95
|
+
render={() => (
|
|
96
|
+
<>
|
|
97
|
+
<StructuresUploadEvents />
|
|
98
|
+
</>
|
|
99
|
+
)}
|
|
100
|
+
/>
|
|
91
101
|
<Route
|
|
92
102
|
path={PENDING_STRUCTURE_NOTES}
|
|
93
103
|
render={() => (
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import PropTypes from "prop-types";
|
|
3
|
+
import { connect } from "react-redux";
|
|
4
|
+
import { Header, Icon, Segment, Dimmer, Loader } from "semantic-ui-react";
|
|
5
|
+
import { FormattedMessage } from "react-intl";
|
|
6
|
+
import StructuresUploadEventsTable from "./StructuresUploadEventsTable";
|
|
7
|
+
|
|
8
|
+
export const StructuresUploadEvents = ({ structuresUploadEventsLoading }) => (
|
|
9
|
+
<Segment>
|
|
10
|
+
<Header as="h2">
|
|
11
|
+
<Icon circular name="cogs" />
|
|
12
|
+
<Header.Content>
|
|
13
|
+
<FormattedMessage id="structures.bulkUploadEvents.header" />
|
|
14
|
+
<Header.Subheader>
|
|
15
|
+
<FormattedMessage id="structures.bulkUploadEvents.subheader" />
|
|
16
|
+
</Header.Subheader>
|
|
17
|
+
</Header.Content>
|
|
18
|
+
</Header>
|
|
19
|
+
<Dimmer.Dimmable dimmed={structuresUploadEventsLoading}>
|
|
20
|
+
<Segment attached="bottom">
|
|
21
|
+
<StructuresUploadEventsTable />
|
|
22
|
+
<Dimmer inverted active={structuresUploadEventsLoading}>
|
|
23
|
+
<Loader />
|
|
24
|
+
</Dimmer>
|
|
25
|
+
</Segment>
|
|
26
|
+
</Dimmer.Dimmable>
|
|
27
|
+
</Segment>
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
StructuresUploadEvents.propTypes = {
|
|
31
|
+
structuresUploadEventsLoading: PropTypes.bool,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const mapStateToProps = ({ structuresUploadEventsLoading }) => ({
|
|
35
|
+
structuresUploadEventsLoading,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export default connect(mapStateToProps)(StructuresUploadEvents);
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React, { useEffect, useState } from "react";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
import { FormattedMessage } from "react-intl";
|
|
5
|
+
import { Header, Icon, Table } from "semantic-ui-react";
|
|
6
|
+
import { connect } from "react-redux";
|
|
7
|
+
import { useIntl } from "react-intl";
|
|
8
|
+
import { columnDecorator } from "@truedat/core/services";
|
|
9
|
+
import { fetchStructuresUploadEvents } from "../routines";
|
|
10
|
+
import { getStructuresUploadEventColumns } from "../selectors";
|
|
11
|
+
|
|
12
|
+
import "../styles/structuresUploadEventColumns.less";
|
|
13
|
+
|
|
14
|
+
export const getColumnsWithData = (ruleResults, columns) =>
|
|
15
|
+
_.filter((column) =>
|
|
16
|
+
_.any(_.flow(columnDecorator(column), _.negate(_.isEmpty)))(ruleResults)
|
|
17
|
+
)(columns);
|
|
18
|
+
|
|
19
|
+
export const StructuresUploadEventsTable = ({
|
|
20
|
+
columns,
|
|
21
|
+
fetchStructuresUploadEvents,
|
|
22
|
+
structuresUploadEvents,
|
|
23
|
+
structuresUploadEventsLoading,
|
|
24
|
+
}) => {
|
|
25
|
+
const { formatMessage } = useIntl();
|
|
26
|
+
const [events, setEvents] = useState();
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
fetchStructuresUploadEvents();
|
|
30
|
+
}, [fetchStructuresUploadEvents]);
|
|
31
|
+
|
|
32
|
+
const messagesImp = (data) => {
|
|
33
|
+
return _.has("errors")(data)
|
|
34
|
+
? _.flow(
|
|
35
|
+
_.propOr([], "errors"),
|
|
36
|
+
_.map((error) => ({
|
|
37
|
+
id: `structures.bulkUpdateStructures.failed.note.${error.message}`,
|
|
38
|
+
fields: {
|
|
39
|
+
row: error.row,
|
|
40
|
+
id: error.id,
|
|
41
|
+
field: error.field,
|
|
42
|
+
externalId: error.external_id,
|
|
43
|
+
message: error.message,
|
|
44
|
+
},
|
|
45
|
+
defaultMessage: "structures.bulkUploadStructures.success.errors",
|
|
46
|
+
}))
|
|
47
|
+
)(data)
|
|
48
|
+
: [];
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
const events = _.reduce(
|
|
53
|
+
(acc, event) => ({
|
|
54
|
+
...acc,
|
|
55
|
+
[`${event.task_reference}_${event.status}`]: {
|
|
56
|
+
...event,
|
|
57
|
+
response: {
|
|
58
|
+
...event.response,
|
|
59
|
+
translatedHeader: !event.response
|
|
60
|
+
? ""
|
|
61
|
+
: formatMessage(
|
|
62
|
+
{ id: "structures.bulkUpdateStructures.success.header" },
|
|
63
|
+
{
|
|
64
|
+
count_ids: event.response.ids.length,
|
|
65
|
+
count_errors: event.response.errors.length,
|
|
66
|
+
}
|
|
67
|
+
),
|
|
68
|
+
translatedErrors: _.map((error) => {
|
|
69
|
+
return formatMessage({ id: error.id }, error.fields);
|
|
70
|
+
})(messagesImp(event.response)),
|
|
71
|
+
},
|
|
72
|
+
expanded: false,
|
|
73
|
+
},
|
|
74
|
+
}),
|
|
75
|
+
{},
|
|
76
|
+
structuresUploadEvents
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
setEvents(events);
|
|
80
|
+
}, [structuresUploadEvents, formatMessage]);
|
|
81
|
+
|
|
82
|
+
const toggleExpanded = (eventKey) => {
|
|
83
|
+
setEvents({
|
|
84
|
+
...events,
|
|
85
|
+
[eventKey]: {
|
|
86
|
+
...events[eventKey],
|
|
87
|
+
expanded: !events[eventKey].expanded,
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<>
|
|
94
|
+
{!_.isEmpty(events) && (
|
|
95
|
+
<Table sortable selectable>
|
|
96
|
+
<Table.Header>
|
|
97
|
+
<Table.Row>
|
|
98
|
+
{columns.map((column, i) => {
|
|
99
|
+
return (
|
|
100
|
+
<Table.HeaderCell
|
|
101
|
+
key={i}
|
|
102
|
+
width={column.width}
|
|
103
|
+
content={formatMessage({
|
|
104
|
+
id: `structures.bulkUploadEvents.props.${column.name}`,
|
|
105
|
+
defaultMessage: column.name,
|
|
106
|
+
})}
|
|
107
|
+
/>
|
|
108
|
+
);
|
|
109
|
+
})}
|
|
110
|
+
</Table.Row>
|
|
111
|
+
</Table.Header>
|
|
112
|
+
<Table.Body>
|
|
113
|
+
{_.map((eventKey) => {
|
|
114
|
+
return (
|
|
115
|
+
<Table.Row
|
|
116
|
+
className={
|
|
117
|
+
events[eventKey].expanded ? "expanded" : "contracted"
|
|
118
|
+
}
|
|
119
|
+
key={eventKey}
|
|
120
|
+
onClick={() => toggleExpanded(eventKey)}
|
|
121
|
+
>
|
|
122
|
+
{!events[eventKey].expanded ? (
|
|
123
|
+
columns &&
|
|
124
|
+
columns.map((column, key) => (
|
|
125
|
+
<Table.Cell
|
|
126
|
+
key={key}
|
|
127
|
+
textAlign={column.textAlign}
|
|
128
|
+
content={columnDecorator(column)({
|
|
129
|
+
...events[eventKey],
|
|
130
|
+
formatMessage,
|
|
131
|
+
})}
|
|
132
|
+
/>
|
|
133
|
+
))
|
|
134
|
+
) : (
|
|
135
|
+
<Table.Cell
|
|
136
|
+
colSpan={columns.length}
|
|
137
|
+
content={columns.map((column, key) => (
|
|
138
|
+
<div key={key}>
|
|
139
|
+
{columnDecorator(column)({
|
|
140
|
+
...events[eventKey],
|
|
141
|
+
formatMessage,
|
|
142
|
+
})}
|
|
143
|
+
</div>
|
|
144
|
+
))}
|
|
145
|
+
/>
|
|
146
|
+
)}
|
|
147
|
+
</Table.Row>
|
|
148
|
+
);
|
|
149
|
+
}, Object.keys(events))}
|
|
150
|
+
</Table.Body>
|
|
151
|
+
</Table>
|
|
152
|
+
)}
|
|
153
|
+
{_.isEmpty(structuresUploadEvents) && !structuresUploadEventsLoading && (
|
|
154
|
+
<Header as="h4">
|
|
155
|
+
<Icon name="search" />
|
|
156
|
+
<Header.Content>
|
|
157
|
+
<FormattedMessage id="structures.bulkUploadEvents.results.empty" />
|
|
158
|
+
</Header.Content>
|
|
159
|
+
</Header>
|
|
160
|
+
)}
|
|
161
|
+
</>
|
|
162
|
+
);
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
StructuresUploadEventsTable.propTypes = {
|
|
166
|
+
columns: PropTypes.array,
|
|
167
|
+
structuresUploadEvents: PropTypes.array,
|
|
168
|
+
fetchStructuresUploadEvents: PropTypes.func,
|
|
169
|
+
structuresUploadEventsLoading: PropTypes.bool,
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const mapStateToProps = (state) => ({
|
|
173
|
+
columns: getColumnsWithData(
|
|
174
|
+
state.structuresUploadEvents,
|
|
175
|
+
getStructuresUploadEventColumns(state)
|
|
176
|
+
),
|
|
177
|
+
structuresUploadEvents: state.structuresUploadEvents,
|
|
178
|
+
structuresUploadEventsLoading: state.structuresUploadEventsLoading,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
export default connect(mapStateToProps, { fetchStructuresUploadEvents })(
|
|
182
|
+
StructuresUploadEventsTable
|
|
183
|
+
);
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import { render } from "@truedat/test/render";
|
|
5
|
+
import en from "../../messages/en";
|
|
6
|
+
import StructuresUploadEventsTable from "../StructuresUploadEventsTable";
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
const events = [
|
|
11
|
+
{
|
|
12
|
+
"csv_hash": "99E400B91D164F8F0A39F400CA323C8F",
|
|
13
|
+
"inserted_at": "2022-04-23T15:53:24.638484Z",
|
|
14
|
+
"message": null,
|
|
15
|
+
"response": {
|
|
16
|
+
"errors": [],
|
|
17
|
+
"ids": [
|
|
18
|
+
4825521,
|
|
19
|
+
4825561,
|
|
20
|
+
4825593
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
"status": "COMPLETED",
|
|
24
|
+
"task_reference": "0.3225053307.1609564164.100549",
|
|
25
|
+
"user_id": 467
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"csv_hash": "47D90FDF1AD967BD7DBBDAE28664278E",
|
|
29
|
+
"inserted_at": "2022-04-23T15:14:48.770275Z",
|
|
30
|
+
"message": null,
|
|
31
|
+
"response": {
|
|
32
|
+
"errors": [
|
|
33
|
+
{
|
|
34
|
+
"external_id": "file://corporate-landing-s3/cep-pro-ld-cor/batch/SAP/FSCM/vendor_issue/SAP_FSCM_CM_VENDOR_ISUE/ZFSCM_AC",
|
|
35
|
+
"field": "gdpr",
|
|
36
|
+
"message": "df_content.inclusion",
|
|
37
|
+
"row": 4
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"external_id": "file://corporate-landing-s3/cep-pro-ld-cor/batch/SAP/FSCM/vendor_issue/SAP_FSCM_CM_VENDOR_ISUE/ZFSCM_FN",
|
|
41
|
+
"field": "business_description",
|
|
42
|
+
"message": "df_content.required",
|
|
43
|
+
"row": 2
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"external_id": "file://corporate-landing-s3/cep-pro-ld-cor/batch/SAP/FSCM/vendor_issue/SAP_FSCM_CM_VENDOR_ISUE/ZFSCM_PC",
|
|
47
|
+
"field": "business_description",
|
|
48
|
+
"message": "df_content.required",
|
|
49
|
+
"row": 3
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
"ids": []
|
|
53
|
+
},
|
|
54
|
+
"status": "COMPLETED",
|
|
55
|
+
"task_reference": "0.3225053307.1609564162.101463",
|
|
56
|
+
"user_id": 467
|
|
57
|
+
}
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
const renderOpts = {
|
|
61
|
+
messages: { en },
|
|
62
|
+
state: { structuresUploadEvents: events },
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
describe("<StructuresUploadEventsTable />", () => {
|
|
67
|
+
it("matches the latest snapshot", () => {
|
|
68
|
+
const { container } = render(<StructuresUploadEventsTable />, renderOpts);
|
|
69
|
+
expect(container).toMatchSnapshot();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("toggles row horizontal view when row is clicked", () => {
|
|
73
|
+
const { getAllByRole } = render(
|
|
74
|
+
<StructuresUploadEventsTable />,
|
|
75
|
+
renderOpts
|
|
76
|
+
);
|
|
77
|
+
const dataRow = getAllByRole("row")[1];
|
|
78
|
+
expect(dataRow).toHaveClass("contracted");
|
|
79
|
+
userEvent.click(dataRow);
|
|
80
|
+
expect(dataRow).toHaveClass("expanded");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
});
|
|
@@ -21,6 +21,10 @@ exports[`<DictionaryRoutes /> matches the latest snapshot 1`] = `
|
|
|
21
21
|
path="/structure_types"
|
|
22
22
|
render={[Function]}
|
|
23
23
|
/>
|
|
24
|
+
<Route
|
|
25
|
+
path="/bulk_update_template_content_events"
|
|
26
|
+
render={[Function]}
|
|
27
|
+
/>
|
|
24
28
|
<Route
|
|
25
29
|
path="/structure_notes"
|
|
26
30
|
render={[Function]}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<StructuresUploadEventsTable /> matches the latest snapshot 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<table
|
|
6
|
+
class="ui selectable sortable table"
|
|
7
|
+
>
|
|
8
|
+
<thead
|
|
9
|
+
class=""
|
|
10
|
+
>
|
|
11
|
+
<tr
|
|
12
|
+
class=""
|
|
13
|
+
>
|
|
14
|
+
<th
|
|
15
|
+
class="two wide"
|
|
16
|
+
>
|
|
17
|
+
CSV hash
|
|
18
|
+
</th>
|
|
19
|
+
<th
|
|
20
|
+
class="two wide"
|
|
21
|
+
>
|
|
22
|
+
Status
|
|
23
|
+
</th>
|
|
24
|
+
<th
|
|
25
|
+
class="two wide"
|
|
26
|
+
>
|
|
27
|
+
Response
|
|
28
|
+
</th>
|
|
29
|
+
<th
|
|
30
|
+
class="two wide"
|
|
31
|
+
>
|
|
32
|
+
Date
|
|
33
|
+
</th>
|
|
34
|
+
</tr>
|
|
35
|
+
</thead>
|
|
36
|
+
<tbody
|
|
37
|
+
class=""
|
|
38
|
+
>
|
|
39
|
+
<tr
|
|
40
|
+
class="contracted"
|
|
41
|
+
>
|
|
42
|
+
<td
|
|
43
|
+
class=""
|
|
44
|
+
>
|
|
45
|
+
99E400B91D164F8F0A39F400CA323C8F
|
|
46
|
+
</td>
|
|
47
|
+
<td
|
|
48
|
+
class=""
|
|
49
|
+
>
|
|
50
|
+
Completed
|
|
51
|
+
</td>
|
|
52
|
+
<td
|
|
53
|
+
class=""
|
|
54
|
+
>
|
|
55
|
+
<p>
|
|
56
|
+
Bulk update completed. Loaded 3 errors 0
|
|
57
|
+
</p>
|
|
58
|
+
<ul
|
|
59
|
+
class="csv-bulk-upload-error"
|
|
60
|
+
/>
|
|
61
|
+
</td>
|
|
62
|
+
<td
|
|
63
|
+
class="center aligned"
|
|
64
|
+
>
|
|
65
|
+
<time
|
|
66
|
+
datetime="1650729204638"
|
|
67
|
+
>
|
|
68
|
+
2022-04-23 15:53
|
|
69
|
+
</time>
|
|
70
|
+
</td>
|
|
71
|
+
</tr>
|
|
72
|
+
<tr
|
|
73
|
+
class="contracted"
|
|
74
|
+
>
|
|
75
|
+
<td
|
|
76
|
+
class=""
|
|
77
|
+
>
|
|
78
|
+
47D90FDF1AD967BD7DBBDAE28664278E
|
|
79
|
+
</td>
|
|
80
|
+
<td
|
|
81
|
+
class=""
|
|
82
|
+
>
|
|
83
|
+
Completed
|
|
84
|
+
</td>
|
|
85
|
+
<td
|
|
86
|
+
class=""
|
|
87
|
+
>
|
|
88
|
+
<p>
|
|
89
|
+
Bulk update completed. Loaded 0 errors 3
|
|
90
|
+
</p>
|
|
91
|
+
<ul
|
|
92
|
+
class="csv-bulk-upload-error"
|
|
93
|
+
>
|
|
94
|
+
<li>
|
|
95
|
+
External id: file://corporate-landing-s3/cep-pro-ld-cor/batch/SAP/FSCM/vendor_issue/SAP_FSCM_CM_VENDOR_ISUE/ZFSCM_AC The field <gdpr> does not have a permitted value
|
|
96
|
+
</li>
|
|
97
|
+
<li>
|
|
98
|
+
External id: file://corporate-landing-s3/cep-pro-ld-cor/batch/SAP/FSCM/vendor_issue/SAP_FSCM_CM_VENDOR_ISUE/ZFSCM_FN The field <business_description> is required
|
|
99
|
+
</li>
|
|
100
|
+
<li>
|
|
101
|
+
External id: file://corporate-landing-s3/cep-pro-ld-cor/batch/SAP/FSCM/vendor_issue/SAP_FSCM_CM_VENDOR_ISUE/ZFSCM_PC The field <business_description> is required
|
|
102
|
+
</li>
|
|
103
|
+
</ul>
|
|
104
|
+
<a
|
|
105
|
+
class="alert-exporter-in-table"
|
|
106
|
+
download="errors.csv"
|
|
107
|
+
href="data:text/csv;charset=utf-8,\\"RENDEREDMESSAGE\\",\\"EXTERNAL_ID\\",\\"FIELD\\",\\"MESSAGE\\",\\"ROW\\"
|
|
108
|
+
\\"External id: file://corporate-landing-s3/cep-pro-ld-cor/batch/SAP/FSCM/vendor_issue/SAP_FSCM_CM_VENDOR_ISUE/ZFSCM_AC The field <gdpr> does not have a permitted value\\",\\"file://corporate-landing-s3/cep-pro-ld-cor/batch/SAP/FSCM/vendor_issue/SAP_FSCM_CM_VENDOR_ISUE/ZFSCM_AC\\",\\"gdpr\\",\\"df_content.inclusion\\",\\"4\\"
|
|
109
|
+
\\"External id: file://corporate-landing-s3/cep-pro-ld-cor/batch/SAP/FSCM/vendor_issue/SAP_FSCM_CM_VENDOR_ISUE/ZFSCM_FN The field <business_description> is required\\",\\"file://corporate-landing-s3/cep-pro-ld-cor/batch/SAP/FSCM/vendor_issue/SAP_FSCM_CM_VENDOR_ISUE/ZFSCM_FN\\",\\"business_description\\",\\"df_content.required\\",\\"2\\"
|
|
110
|
+
\\"External id: file://corporate-landing-s3/cep-pro-ld-cor/batch/SAP/FSCM/vendor_issue/SAP_FSCM_CM_VENDOR_ISUE/ZFSCM_PC The field <business_description> is required\\",\\"file://corporate-landing-s3/cep-pro-ld-cor/batch/SAP/FSCM/vendor_issue/SAP_FSCM_CM_VENDOR_ISUE/ZFSCM_PC\\",\\"business_description\\",\\"df_content.required\\",\\"3\\""
|
|
111
|
+
style="float: right;"
|
|
112
|
+
target="_self"
|
|
113
|
+
>
|
|
114
|
+
Export to CSV
|
|
115
|
+
</a>
|
|
116
|
+
</td>
|
|
117
|
+
<td
|
|
118
|
+
class="center aligned"
|
|
119
|
+
>
|
|
120
|
+
<time
|
|
121
|
+
datetime="1650726888770"
|
|
122
|
+
>
|
|
123
|
+
2022-04-23 15:14
|
|
124
|
+
</time>
|
|
125
|
+
</td>
|
|
126
|
+
</tr>
|
|
127
|
+
</tbody>
|
|
128
|
+
</table>
|
|
129
|
+
</div>
|
|
130
|
+
`;
|
package/src/messages/en.js
CHANGED
|
@@ -395,8 +395,26 @@ export default {
|
|
|
395
395
|
"structures.bulkUpdateStructures.no.structures.header": "Empty Search",
|
|
396
396
|
"structures.bulkUpdateStructures.success.header":
|
|
397
397
|
"Bulk update completed. Loaded {count_ids} errors {count_errors}",
|
|
398
|
+
"structures.bulkUpdateStructures.success.accepted.header":
|
|
399
|
+
"CSV accepted. Status can be checked at the link below:",
|
|
398
400
|
"structures.bulkUpdateStructures.success.errors":
|
|
399
401
|
"Error in external id {externalId}, message: {message}",
|
|
402
|
+
"structures.bulkUploadEvents.header": "Notes upload events",
|
|
403
|
+
"structures.bulkUploadEvents.subheader": "Structures notes CSV upload events",
|
|
404
|
+
"structures.bulkUploadEvents.props.csv_hash": "CSV hash",
|
|
405
|
+
"structures.bulkUploadEvents.props.status": "Status",
|
|
406
|
+
"structures.bulkUploadEvents.props.response": "Response",
|
|
407
|
+
"structures.bulkUploadEvents.props.message": "Message",
|
|
408
|
+
"structures.bulkUploadEvents.props.inserted_at": "Date",
|
|
409
|
+
"structures.bulkUploadEvents.results.empty":
|
|
410
|
+
"No structure notes CSV bulk upload events found",
|
|
411
|
+
"structures.bulkUploadEvents.status.already_calculated": "Already calculated",
|
|
412
|
+
"structures.bulkUploadEvents.status.already_started": "Already started",
|
|
413
|
+
"structures.bulkUploadEvents.status.completed": "Completed",
|
|
414
|
+
"structures.bulkUploadEvents.status.failed": "Failed",
|
|
415
|
+
"structures.bulkUploadEvents.status.just_started": "Just started",
|
|
416
|
+
"structures.bulkUploadEvents.status.started": "Started",
|
|
417
|
+
"structures.bulkUploadEvents.status.timed_out": "Timed out",
|
|
400
418
|
"structures.bulkUploadStructuresDomains.success.errors.domain: not_exist":
|
|
401
419
|
"Error in line {row}, in external id {externalId}, domain does not exist",
|
|
402
420
|
"structures.bulkUploadStructuresDomains.success.errors.update_domain: forbbiden":
|
package/src/messages/es.js
CHANGED
|
@@ -401,8 +401,29 @@ export default {
|
|
|
401
401
|
"structures.bulkUpdateStructures.no.structures.header": "Búsqueda vacía",
|
|
402
402
|
"structures.bulkUpdateStructures.success.header":
|
|
403
403
|
"¡Actualización finalizada! subidos {count_ids} errores {count_errors}",
|
|
404
|
+
"structures.bulkUpdateStructures.success.accepted.header":
|
|
405
|
+
"CSV aceptado. Puede comprobar el estado en el siguiente enlace:",
|
|
404
406
|
"structures.bulkUpdateStructures.success.errors":
|
|
405
407
|
"Error en external id {externalId}, mensaje: {message}",
|
|
408
|
+
"structures.bulkUploadEvents.header": "Eventos de subida de notas",
|
|
409
|
+
"structures.bulkUploadEvents.subheader":
|
|
410
|
+
"Eventos de subida de notas de estructuras por CSV",
|
|
411
|
+
"structures.bulkUploadEvents.props.csv_hash": "Hash de CSV",
|
|
412
|
+
"structures.bulkUploadEvents.props.status": "Estado",
|
|
413
|
+
"structures.bulkUploadEvents.props.response": "Respuesta",
|
|
414
|
+
"structures.bulkUploadEvents.props.message": "Mensaje",
|
|
415
|
+
"structures.bulkUploadEvents.props.inserted_at": "Fecha",
|
|
416
|
+
"structures.bulkUploadEvents.results.empty":
|
|
417
|
+
"Ningún evento de subida de notas por CSV encontrado",
|
|
418
|
+
"structures.bulkUploadEvents.status.already_calculated":
|
|
419
|
+
"Previamente calculado",
|
|
420
|
+
"structures.bulkUploadEvents.status.already_started": "Previamente iniciado",
|
|
421
|
+
"structures.bulkUploadEvents.status.completed": "Finalizado",
|
|
422
|
+
"structures.bulkUploadEvents.status.failed": "Fallido",
|
|
423
|
+
"structures.bulkUploadEvents.status.just_started": "Acaba de iniciarse",
|
|
424
|
+
"structures.bulkUploadEvents.status.started": "Iniciado",
|
|
425
|
+
"structures.bulkUploadEvents.status.timed_out":
|
|
426
|
+
"Tiempo de espera máximo sobrepasado",
|
|
406
427
|
"structures.bulkUploadStructuresDomains.success.errors.domain: not_exist":
|
|
407
428
|
"Error en linea {row}, en external id {externalId}, el dominio no existe",
|
|
408
429
|
"structures.bulkUploadStructuresDomains.success.errors.structure: not_exist":
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { fetchStructuresUploadEvents } from "../../routines";
|
|
2
|
+
import { structuresUploadEvents } from "..";
|
|
3
|
+
|
|
4
|
+
const fooState = { foo: "bar" };
|
|
5
|
+
|
|
6
|
+
describe("reducers: structuresUploadEvents", () => {
|
|
7
|
+
const initialState = [];
|
|
8
|
+
|
|
9
|
+
it("should provide the initial state", () => {
|
|
10
|
+
expect(structuresUploadEvents(undefined, {})).toEqual(initialState);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("should handle the fetchStructuresUploadEvents.SUCCESS action", () => {
|
|
14
|
+
const someEvents = [
|
|
15
|
+
{
|
|
16
|
+
csv_hash: "99E400B91D164F8F0A39F400CA323C8F",
|
|
17
|
+
inserted_at: "2022-04-23T15:53:24.638484Z",
|
|
18
|
+
message: null,
|
|
19
|
+
response: {},
|
|
20
|
+
status: "COMPLETED",
|
|
21
|
+
task_reference: "0.3225053307.1609564164.100549",
|
|
22
|
+
user_id: 467,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
csv_hash: "47D90FDF1AD967BD7DBBDAE28664278E",
|
|
26
|
+
inserted_at: "2022-04-23T15:14:48.770275Z",
|
|
27
|
+
message: null,
|
|
28
|
+
response: {},
|
|
29
|
+
status: "COMPLETED",
|
|
30
|
+
task_reference: "0.3225053307.1609564162.101463",
|
|
31
|
+
user_id: 467,
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
expect(
|
|
36
|
+
structuresUploadEvents(fooState, {
|
|
37
|
+
type: fetchStructuresUploadEvents.SUCCESS,
|
|
38
|
+
payload: someEvents,
|
|
39
|
+
})
|
|
40
|
+
).toMatchObject(someEvents);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("should ignore unknown actions", () => {
|
|
44
|
+
expect(structuresUploadEvents(fooState, { type: "FOO" })).toBe(fooState);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { fetchStructuresUploadEvents } from "../../routines";
|
|
2
|
+
import { structuresUploadEventsLoading } from "..";
|
|
3
|
+
|
|
4
|
+
const fooState = { foo: "bar" };
|
|
5
|
+
|
|
6
|
+
describe("reducers: structuresUploadEventsLoading", () => {
|
|
7
|
+
it("should provide the initial state", () => {
|
|
8
|
+
expect(structuresUploadEventsLoading(undefined, {})).toBe(false);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("should be true after receiving the fetchStructuresUploadEvents.REQUEST action", () => {
|
|
12
|
+
expect(structuresUploadEventsLoading(fooState, { type: fetchStructuresUploadEvents.REQUEST })).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("should be false after receiving the fetchStructuresUploadEvents.SUCCESS action", () => {
|
|
16
|
+
expect(structuresUploadEventsLoading(fooState, { type: fetchStructuresUploadEvents.SUCCESS })).toBe(false);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should be false after receiving the fetchStructuresUploadEvents.FAILURE action", () => {
|
|
20
|
+
expect(structuresUploadEventsLoading(fooState, { type: fetchStructuresUploadEvents.FAILURE })).toBe(false);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("should ignore unhandled actions", () => {
|
|
24
|
+
expect(structuresUploadEventsLoading(fooState, { type: "FOO" })).toBe(fooState);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import _ from "lodash/fp";
|
|
2
2
|
import { dismissAlert } from "@truedat/core/routines";
|
|
3
|
+
import { STRUCTURES_UPLOAD_EVENTS } from "@truedat/core/routes";
|
|
3
4
|
import {
|
|
4
5
|
bulkUpdateStructures,
|
|
5
6
|
uploadStructures,
|
|
@@ -9,42 +10,64 @@ import {
|
|
|
9
10
|
|
|
10
11
|
const initialState = null;
|
|
11
12
|
|
|
13
|
+
const bulkUpdateErrors = (payload) => {
|
|
14
|
+
const messagesImp = _.has("errors")(payload.data)
|
|
15
|
+
? _.flow(
|
|
16
|
+
_.propOr([], "errors"),
|
|
17
|
+
_.map((error) => ({
|
|
18
|
+
id: `structures.bulkUpdateStructures.failed.note.${error.message}`,
|
|
19
|
+
fields: {
|
|
20
|
+
row: error.row,
|
|
21
|
+
id: error.id,
|
|
22
|
+
field: error.field,
|
|
23
|
+
externalId: error.external_id,
|
|
24
|
+
message: error.message,
|
|
25
|
+
},
|
|
26
|
+
defaultMessage: "structures.bulkUploadStructures.success.errors",
|
|
27
|
+
}))
|
|
28
|
+
)(payload.data)
|
|
29
|
+
: "";
|
|
30
|
+
return {
|
|
31
|
+
error: false,
|
|
32
|
+
header: "structures.bulkUpdateStructures.success.header",
|
|
33
|
+
icon: "check",
|
|
34
|
+
color: payload.data.errors.length >= 1 ? "orange" : "green",
|
|
35
|
+
text: "",
|
|
36
|
+
messages: messagesImp,
|
|
37
|
+
fields: {
|
|
38
|
+
count_ids: payload.data.ids.length,
|
|
39
|
+
count_errors: payload.data.errors.length,
|
|
40
|
+
},
|
|
41
|
+
downloadable: true,
|
|
42
|
+
initialMaxListItems: 5,
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const bulkUpdateAccepted = () => {
|
|
47
|
+
return {
|
|
48
|
+
error: false,
|
|
49
|
+
header: "structures.bulkUpdateStructures.success.accepted.header",
|
|
50
|
+
icon: "check",
|
|
51
|
+
color: "blue",
|
|
52
|
+
text: "",
|
|
53
|
+
anchor: {
|
|
54
|
+
reference: STRUCTURES_UPLOAD_EVENTS,
|
|
55
|
+
label: "sidemenu.structures_upload_events",
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
|
|
12
60
|
const ddMessage = (state = initialState, { type, payload }) => {
|
|
13
61
|
switch (type) {
|
|
14
62
|
case dismissAlert.TRIGGER:
|
|
15
63
|
return initialState;
|
|
16
64
|
case uploadStructures.SUCCESS:
|
|
17
65
|
case bulkUpdateStructures.SUCCESS: {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
fields: {
|
|
24
|
-
row: error.row,
|
|
25
|
-
id: error.id,
|
|
26
|
-
field: error.field,
|
|
27
|
-
externalId: error.external_id,
|
|
28
|
-
message: error.message,
|
|
29
|
-
},
|
|
30
|
-
defaultMessage: "structures.bulkUploadStructures.success.errors",
|
|
31
|
-
}))
|
|
32
|
-
)(payload.data)
|
|
33
|
-
: "";
|
|
34
|
-
return {
|
|
35
|
-
error: false,
|
|
36
|
-
header: "structures.bulkUpdateStructures.success.header",
|
|
37
|
-
icon: "check",
|
|
38
|
-
color: payload.data.errors.length >= 1 ? "orange" : "green",
|
|
39
|
-
text: "",
|
|
40
|
-
messages: messagesImp,
|
|
41
|
-
fields: {
|
|
42
|
-
count_ids: payload.data.ids.length,
|
|
43
|
-
count_errors: payload.data.errors.length,
|
|
44
|
-
},
|
|
45
|
-
downloadable: true,
|
|
46
|
-
initialMaxListItems: 5,
|
|
47
|
-
};
|
|
66
|
+
if (payload.status === 202) {
|
|
67
|
+
return bulkUpdateAccepted(payload.data);
|
|
68
|
+
} else if (payload.status === 200) {
|
|
69
|
+
return bulkUpdateErrors(payload);
|
|
70
|
+
}
|
|
48
71
|
}
|
|
49
72
|
|
|
50
73
|
case uploadStructuresDomains.SUCCESS: {
|
package/src/reducers/index.js
CHANGED
|
@@ -83,6 +83,8 @@ import { structuresEditableDownloading } from "./structuresEditableDownloading";
|
|
|
83
83
|
import { structuresLoading } from "./structuresLoading";
|
|
84
84
|
import { structuresPageSize } from "./structuresPageSize";
|
|
85
85
|
import { structuresSiblings } from "./structureSiblings";
|
|
86
|
+
import { structuresUploadEvents } from "./structuresUploadEvents";
|
|
87
|
+
import { structuresUploadEventsLoading } from "./structuresUploadEventsLoading";
|
|
86
88
|
import { system } from "./system";
|
|
87
89
|
import { systemId } from "./systemId";
|
|
88
90
|
import { systemStructures } from "./systemStructures";
|
|
@@ -183,6 +185,8 @@ export {
|
|
|
183
185
|
structuresLoading,
|
|
184
186
|
structuresPageSize,
|
|
185
187
|
structuresSiblings,
|
|
188
|
+
structuresUploadEvents,
|
|
189
|
+
structuresUploadEventsLoading,
|
|
186
190
|
system,
|
|
187
191
|
systemId,
|
|
188
192
|
systemStructures,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import { fetchStructuresUploadEvents } from "../routines";
|
|
3
|
+
|
|
4
|
+
export const initialState = [];
|
|
5
|
+
|
|
6
|
+
export const structuresUploadEvents = (
|
|
7
|
+
state = initialState,
|
|
8
|
+
{ type, payload }
|
|
9
|
+
) => {
|
|
10
|
+
switch (type) {
|
|
11
|
+
case fetchStructuresUploadEvents.SUCCESS:
|
|
12
|
+
return payload;
|
|
13
|
+
default:
|
|
14
|
+
return state;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { fetchStructuresUploadEvents } from "../routines";
|
|
2
|
+
|
|
3
|
+
const structuresUploadEventsLoading = (state = false, { type }) => {
|
|
4
|
+
switch (type) {
|
|
5
|
+
case fetchStructuresUploadEvents.REQUEST:
|
|
6
|
+
return true;
|
|
7
|
+
case fetchStructuresUploadEvents.SUCCESS:
|
|
8
|
+
return false;
|
|
9
|
+
case fetchStructuresUploadEvents.FAILURE:
|
|
10
|
+
return false;
|
|
11
|
+
default:
|
|
12
|
+
return state;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export { structuresUploadEventsLoading };
|
package/src/routines.js
CHANGED
|
@@ -86,6 +86,7 @@ export const fetchUserSearchFilters = createRoutine(
|
|
|
86
86
|
"FETCH_USER_SEARCH_FILTERS"
|
|
87
87
|
);
|
|
88
88
|
export const fetchStructures = createRoutine("FETCH_STRUCTURES");
|
|
89
|
+
export const fetchStructuresUploadEvents = createRoutine("FETCH_STRUCTURES_UPLOAD_EVENTS");
|
|
89
90
|
export const fetchSystem = createRoutine("FETCH_SYSTEM");
|
|
90
91
|
export const fetchSystemStructures = createRoutine("FETCH_SYSTEM_STRUCTURES");
|
|
91
92
|
export const fetchSystems = createRoutine("FETCH_SYSTEMS");
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { testSaga } from "redux-saga-test-plan";
|
|
2
|
+
import { apiJson, JSON_OPTS } from "@truedat/core/services/api";
|
|
3
|
+
import {
|
|
4
|
+
fetchStructuresUploadEventsRequestSaga,
|
|
5
|
+
fetchStructuresUploadEventsSaga,
|
|
6
|
+
} from "../fetchStructuresUploadEvents";
|
|
7
|
+
import { fetchStructuresUploadEvents } from "../../routines";
|
|
8
|
+
import { API_STRUCTURES_UPLOAD_EVENTS } from "../../api";
|
|
9
|
+
|
|
10
|
+
describe("sagas: fetchStructuresUploadEventsRequestSaga", () => {
|
|
11
|
+
it("should invoke fetchStructuresUploadEventsSaga on trigger", () => {
|
|
12
|
+
expect(() => {
|
|
13
|
+
testSaga(fetchStructuresUploadEventsRequestSaga)
|
|
14
|
+
.next()
|
|
15
|
+
.takeLatest(
|
|
16
|
+
fetchStructuresUploadEvents.TRIGGER,
|
|
17
|
+
fetchStructuresUploadEventsSaga
|
|
18
|
+
)
|
|
19
|
+
.finish()
|
|
20
|
+
.isDone();
|
|
21
|
+
}).not.toThrow();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("should throw exception if an unhandled action is received", () => {
|
|
25
|
+
expect(() => {
|
|
26
|
+
testSaga(fetchStructuresUploadEventsRequestSaga)
|
|
27
|
+
.next()
|
|
28
|
+
.takeLatest("FOO", fetchStructuresUploadEventsSaga);
|
|
29
|
+
}).toThrow();
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe("sagas: fetchStructuresUploadEventsSaga", () => {
|
|
34
|
+
it("should put a success action when a response is returned", () => {
|
|
35
|
+
const url = API_STRUCTURES_UPLOAD_EVENTS;
|
|
36
|
+
const data = [
|
|
37
|
+
{
|
|
38
|
+
csv_hash: "99E400B91D164F8F0A39F400CA323C8F",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
csv_hash: "47D90FDF1AD967BD7DBBDAE28664278E",
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
expect(() => {
|
|
46
|
+
testSaga(fetchStructuresUploadEventsSaga)
|
|
47
|
+
.next()
|
|
48
|
+
.put(fetchStructuresUploadEvents.request())
|
|
49
|
+
.next()
|
|
50
|
+
.call(apiJson, url, JSON_OPTS)
|
|
51
|
+
.next({ data })
|
|
52
|
+
.put(fetchStructuresUploadEvents.success(data))
|
|
53
|
+
.next()
|
|
54
|
+
.put(fetchStructuresUploadEvents.fulfill())
|
|
55
|
+
.next()
|
|
56
|
+
.isDone();
|
|
57
|
+
}).not.toThrow();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("should put a failure action when the call returns an error", () => {
|
|
61
|
+
const message = "Request failed";
|
|
62
|
+
const error = { message };
|
|
63
|
+
const url = API_STRUCTURES_UPLOAD_EVENTS;
|
|
64
|
+
|
|
65
|
+
expect(() => {
|
|
66
|
+
testSaga(fetchStructuresUploadEventsSaga)
|
|
67
|
+
.next()
|
|
68
|
+
.put(fetchStructuresUploadEvents.request())
|
|
69
|
+
.next()
|
|
70
|
+
.call(apiJson, url, JSON_OPTS)
|
|
71
|
+
.throw(error)
|
|
72
|
+
.put(fetchStructuresUploadEvents.failure(message))
|
|
73
|
+
.next()
|
|
74
|
+
.put(fetchStructuresUploadEvents.fulfill())
|
|
75
|
+
.next()
|
|
76
|
+
.isDone();
|
|
77
|
+
}).not.toThrow();
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -30,6 +30,7 @@ describe("sagas: uploadStructuresRequestSaga", () => {
|
|
|
30
30
|
describe("sagas: uploadStructuresSaga", () => {
|
|
31
31
|
const body = { structures: "content" };
|
|
32
32
|
const payload = { data: body };
|
|
33
|
+
const status = "JUST_STARTED";
|
|
33
34
|
const data = { message: [1, 2] };
|
|
34
35
|
it("should put a success action when a response is returned", () => {
|
|
35
36
|
expect(() => {
|
|
@@ -38,8 +39,8 @@ describe("sagas: uploadStructuresSaga", () => {
|
|
|
38
39
|
.put(uploadStructures.request())
|
|
39
40
|
.next()
|
|
40
41
|
.call(apiJsonPost, API_STRUCTURES_UPLOAD, body, UPLOAD_JSON_OPTS)
|
|
41
|
-
.next({ data })
|
|
42
|
-
.put(uploadStructures.success({ data }))
|
|
42
|
+
.next({ status, data })
|
|
43
|
+
.put(uploadStructures.success({ status, data }))
|
|
43
44
|
.next()
|
|
44
45
|
.put(uploadStructures.fulfill())
|
|
45
46
|
.next()
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { call, put, takeLatest } from "redux-saga/effects";
|
|
2
|
+
import { apiJson, JSON_OPTS } from "@truedat/core/services/api";
|
|
3
|
+
import { fetchStructuresUploadEvents as fetchStructuresUploadEvents } from "../routines";
|
|
4
|
+
import { API_STRUCTURES_UPLOAD_EVENTS } from "../api";
|
|
5
|
+
|
|
6
|
+
export function* fetchStructuresUploadEventsSaga() {
|
|
7
|
+
try {
|
|
8
|
+
const url = API_STRUCTURES_UPLOAD_EVENTS;
|
|
9
|
+
yield put(fetchStructuresUploadEvents.request());
|
|
10
|
+
const { data } = yield call(apiJson, url, JSON_OPTS);
|
|
11
|
+
yield put(fetchStructuresUploadEvents.success(data));
|
|
12
|
+
} catch (error) {
|
|
13
|
+
if (error.response) {
|
|
14
|
+
const { status, data } = error.response;
|
|
15
|
+
yield put(fetchStructuresUploadEvents.failure({ status, data }));
|
|
16
|
+
} else {
|
|
17
|
+
yield put(fetchStructuresUploadEvents.failure(error.message));
|
|
18
|
+
}
|
|
19
|
+
} finally {
|
|
20
|
+
yield put(fetchStructuresUploadEvents.fulfill());
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function* fetchStructuresUploadEventsRequestSaga() {
|
|
25
|
+
yield takeLatest(
|
|
26
|
+
fetchStructuresUploadEvents.TRIGGER,
|
|
27
|
+
fetchStructuresUploadEventsSaga
|
|
28
|
+
);
|
|
29
|
+
}
|
package/src/sagas/index.js
CHANGED
|
@@ -35,6 +35,7 @@ import { fetchStructureTypeRequestSaga } from "./fetchStructureType";
|
|
|
35
35
|
import { fetchStructureTypesRequestSaga } from "./fetchStructureTypes";
|
|
36
36
|
import { fetchUserSearchFiltersRequestSaga } from "./fetchUserSearchFilters";
|
|
37
37
|
import { fetchStructuresRequestSaga } from "./fetchStructures";
|
|
38
|
+
import { fetchStructuresUploadEventsRequestSaga } from "./fetchStructuresUploadEvents";
|
|
38
39
|
import { fetchSystemRequestSaga } from "./fetchSystem";
|
|
39
40
|
import { fetchSystemStructuresRequestSaga } from "./fetchSystemStructures";
|
|
40
41
|
import { fetchSystemsRequestSaga } from "./fetchSystems";
|
|
@@ -85,6 +86,7 @@ export {
|
|
|
85
86
|
fetchStructureTagsRequestSaga,
|
|
86
87
|
fetchStructureTypeRequestSaga,
|
|
87
88
|
fetchStructureTypesRequestSaga,
|
|
89
|
+
fetchStructuresUploadEventsRequestSaga,
|
|
88
90
|
fetchUserSearchFiltersRequestSaga,
|
|
89
91
|
fetchStructuresRequestSaga,
|
|
90
92
|
fetchSystemRequestSaga,
|
|
@@ -140,6 +142,7 @@ export default [
|
|
|
140
142
|
fetchStructureTypesRequestSaga(),
|
|
141
143
|
fetchUserSearchFiltersRequestSaga(),
|
|
142
144
|
fetchStructuresRequestSaga(),
|
|
145
|
+
fetchStructuresUploadEventsRequestSaga(),
|
|
143
146
|
fetchSystemRequestSaga(),
|
|
144
147
|
fetchSystemStructuresRequestSaga(),
|
|
145
148
|
fetchSystemsRequestSaga(),
|
|
@@ -7,14 +7,14 @@ export function* uploadStructuresSaga({ payload }) {
|
|
|
7
7
|
try {
|
|
8
8
|
const { data: body } = payload;
|
|
9
9
|
yield put(uploadStructures.request());
|
|
10
|
-
const { data } = yield call(
|
|
10
|
+
const { status, data } = yield call(
|
|
11
11
|
apiJsonPost,
|
|
12
12
|
API_STRUCTURES_UPLOAD,
|
|
13
13
|
body,
|
|
14
14
|
UPLOAD_JSON_OPTS
|
|
15
15
|
);
|
|
16
16
|
|
|
17
|
-
yield put(uploadStructures.success({ data }));
|
|
17
|
+
yield put(uploadStructures.success({ status, data }));
|
|
18
18
|
} catch (error) {
|
|
19
19
|
if (error.response) {
|
|
20
20
|
const { status, data } = error.response;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getStructuresUploadEventColumns,
|
|
3
|
+
defaultStructuresUploadEventColumns,
|
|
4
|
+
} from "..";
|
|
5
|
+
|
|
6
|
+
describe("selectors: getStructuresUploadEventColumns", () => {
|
|
7
|
+
it("should return custom structuresUploadEventColumns when present", () => {
|
|
8
|
+
const structuresUploadEventColumns = [{ name: "test" }];
|
|
9
|
+
const res = getStructuresUploadEventColumns({
|
|
10
|
+
structuresUploadEventColumns
|
|
11
|
+
});
|
|
12
|
+
expect(res).toHaveLength(1);
|
|
13
|
+
expect(res).toEqual(structuresUploadEventColumns);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("should return default defaultRuleImplementationColumns when no customized", () => {
|
|
17
|
+
const res = getStructuresUploadEventColumns({});
|
|
18
|
+
expect(res).toHaveLength(defaultStructuresUploadEventColumns.length);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { createSelector } from "reselect";
|
|
4
|
+
import { FormattedMessage } from "react-intl";
|
|
5
|
+
import Moment from "react-moment";
|
|
6
|
+
import { AlertExporter } from "@truedat/core/components/AlertExporter";
|
|
7
|
+
|
|
8
|
+
const bulkUpdateErrors = (response) => {
|
|
9
|
+
return _.isEmpty(response) ? (
|
|
10
|
+
""
|
|
11
|
+
) : (
|
|
12
|
+
<>
|
|
13
|
+
<p>{response.translatedHeader}</p>
|
|
14
|
+
<ul className="csv-bulk-upload-error">
|
|
15
|
+
{_.map.convert({ cap: false })((translatedError, key) => {
|
|
16
|
+
return <li key={`error_${key}`}>{translatedError}</li>;
|
|
17
|
+
})(response.translatedErrors)}
|
|
18
|
+
</ul>
|
|
19
|
+
{_.isEmpty(response.errors) ? null : (
|
|
20
|
+
<AlertExporter
|
|
21
|
+
className="alert-exporter-in-table"
|
|
22
|
+
onClick={(e) => e.stopPropagation()}
|
|
23
|
+
{...{
|
|
24
|
+
list: response.translatedErrors,
|
|
25
|
+
messages: response.errors,
|
|
26
|
+
}}
|
|
27
|
+
/>
|
|
28
|
+
)}
|
|
29
|
+
</>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const dateDecorator = (date) =>
|
|
34
|
+
date && <Moment locale="es" date={date} format="YYYY-MM-DD HH:mm" />;
|
|
35
|
+
|
|
36
|
+
const statusDecorator = (status) => (
|
|
37
|
+
<FormattedMessage
|
|
38
|
+
id={`structures.bulkUploadEvents.status.${_.toLower(status)}`}
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
export const defaultStructuresUploadEventColumns = [
|
|
43
|
+
{
|
|
44
|
+
name: "csv_hash",
|
|
45
|
+
width: 2,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: "status",
|
|
49
|
+
fieldDecorator: statusDecorator,
|
|
50
|
+
width: 2,
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: "response",
|
|
54
|
+
fieldDecorator: bulkUpdateErrors,
|
|
55
|
+
width: 2,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: "message",
|
|
59
|
+
width: 2,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: "inserted_at",
|
|
63
|
+
fieldDecorator: dateDecorator,
|
|
64
|
+
textAlign: "center",
|
|
65
|
+
width: 2,
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
const getColumns = (state) => state.structuresUploadEventColumns;
|
|
70
|
+
|
|
71
|
+
export const getStructuresUploadEventColumns = createSelector(
|
|
72
|
+
[getColumns],
|
|
73
|
+
(columns) => _.defaultTo(defaultStructuresUploadEventColumns)(columns)
|
|
74
|
+
);
|
package/src/selectors/index.js
CHANGED
|
@@ -39,6 +39,10 @@ export { getStructureSelectedFilterValues } from "./getStructureSelectedFilterVa
|
|
|
39
39
|
export { getStructuresFields } from "./getStructuresFields";
|
|
40
40
|
export { getStructureSortingCriteria } from "./getStructureSortingCriteria";
|
|
41
41
|
export { getStructureUpdatedAt } from "./getStructureUpdatedAt";
|
|
42
|
+
export {
|
|
43
|
+
getStructuresUploadEventColumns,
|
|
44
|
+
defaultStructuresUploadEventColumns,
|
|
45
|
+
} from "./getStructuresUploadEventColumns";
|
|
42
46
|
export { getSystem } from "./getSystem";
|
|
43
47
|
export { getSystemTemplate } from "./getSystemTemplate";
|
|
44
48
|
export { getTabVisibility } from "./getTabVisibility";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
.contracted ul.csv-bulk-upload-error {
|
|
2
|
+
max-height: 100px;
|
|
3
|
+
overflow: hidden;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.contracted ul.csv-bulk-upload-error {
|
|
7
|
+
.fade-up();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.fade-up {
|
|
11
|
+
background: -webkit-linear-gradient(rgba(0,0,0,1), rgba(0,0,0,0));
|
|
12
|
+
background-clip: text;
|
|
13
|
+
-webkit-background-clip: text;
|
|
14
|
+
-webkit-text-fill-color: transparent;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.alert-exporter-in-table {
|
|
18
|
+
padding: 3px;
|
|
19
|
+
margin: 5px;
|
|
20
|
+
&:hover {
|
|
21
|
+
padding: 2px;
|
|
22
|
+
border: 1px solid blue;
|
|
23
|
+
}
|
|
24
|
+
}
|