@truedat/bg 6.0.3 → 6.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -6
- package/src/concepts/api.js +3 -0
- package/src/concepts/components/ConceptRoutes.js +9 -0
- package/src/concepts/components/ConceptsActions.js +4 -1
- package/src/concepts/components/ConceptsUploadButton.js +21 -2
- package/src/concepts/components/ConceptsUploadEvents.js +24 -0
- package/src/concepts/components/ConceptsUploadEventsTable.js +166 -0
- package/src/concepts/components/__tests__/ConceptsUploadButton.spec.js +35 -13
- package/src/concepts/components/__tests__/ConceptsUploadEventsTable.spec.js +98 -0
- package/src/concepts/components/__tests__/__snapshots__/ConceptsUploadButton.spec.js.snap +75 -24
- package/src/concepts/components/__tests__/__snapshots__/ConceptsUploadEventsTable.spec.js.snap +115 -0
- package/src/concepts/hooks/useUploadEvents.js +11 -0
- package/src/concepts/reducers/uploadConceptsFile.js +0 -1
- package/src/concepts/selectors/__tests__/getConceptUploadEventColumns.spec.js +20 -0
- package/src/concepts/selectors/getConceptUploadEventColumns.js +72 -0
- package/src/concepts/selectors/index.js +4 -0
- package/src/concepts/styles/conceptsUploadEventColumns.less +24 -0
- package/src/messages/en.js +45 -9
- package/src/messages/es.js +46 -9
- package/src/reducers/__tests__/bgMessage.spec.js +12 -9
- package/src/reducers/bgMessage.js +12 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/bg",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.5",
|
|
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.16.5",
|
|
35
35
|
"@testing-library/react": "^12.0.0",
|
|
36
36
|
"@testing-library/user-event": "^13.2.1",
|
|
37
|
-
"@truedat/test": "6.0.
|
|
37
|
+
"@truedat/test": "6.0.5",
|
|
38
38
|
"babel-jest": "^28.1.0",
|
|
39
39
|
"babel-plugin-dynamic-import-node": "^2.3.3",
|
|
40
40
|
"babel-plugin-lodash": "^3.3.4",
|
|
@@ -86,9 +86,9 @@
|
|
|
86
86
|
]
|
|
87
87
|
},
|
|
88
88
|
"dependencies": {
|
|
89
|
-
"@truedat/core": "6.0.
|
|
90
|
-
"@truedat/df": "6.0.
|
|
91
|
-
"@truedat/lm": "6.0.
|
|
89
|
+
"@truedat/core": "6.0.5",
|
|
90
|
+
"@truedat/df": "6.0.5",
|
|
91
|
+
"@truedat/lm": "6.0.5",
|
|
92
92
|
"decode-uri-component": "^0.2.2",
|
|
93
93
|
"file-saver": "^2.0.5",
|
|
94
94
|
"moment": "^2.29.4",
|
|
@@ -111,5 +111,5 @@
|
|
|
111
111
|
"react-dom": ">= 16.8.6 < 17",
|
|
112
112
|
"semantic-ui-react": ">= 2.0.3 < 2.2"
|
|
113
113
|
},
|
|
114
|
-
"gitHead": "
|
|
114
|
+
"gitHead": "abbaf37328ae24646d2a2c2851f77a611e5476d8"
|
|
115
115
|
}
|
package/src/concepts/api.js
CHANGED
|
@@ -16,6 +16,8 @@ const API_GET_CONCEPT_USER_FILTERS =
|
|
|
16
16
|
"/api/business_concept_user_filters/user/me";
|
|
17
17
|
const API_BUSINESS_CONCEPT_BULK_UPDATE =
|
|
18
18
|
"/api/business_concept_versions/bulk_update";
|
|
19
|
+
const API_BUSINESS_CONCEPT_BULK_UPLOAD =
|
|
20
|
+
"/api/business_concepts/bulk_upload_event";
|
|
19
21
|
const API_BUSINESS_CONCEPT_SET_CONFIDENTIAL =
|
|
20
22
|
"/api/business_concept_versions/:id/set_confidential";
|
|
21
23
|
|
|
@@ -32,5 +34,6 @@ export {
|
|
|
32
34
|
API_CONCEPT_USER_FILTERS,
|
|
33
35
|
API_GET_CONCEPT_USER_FILTERS,
|
|
34
36
|
API_BUSINESS_CONCEPT_BULK_UPDATE,
|
|
37
|
+
API_BUSINESS_CONCEPT_BULK_UPLOAD,
|
|
35
38
|
API_BUSINESS_CONCEPT_SET_CONFIDENTIAL,
|
|
36
39
|
};
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
CONCEPT_LINKS_MANAGEMENT,
|
|
11
11
|
CONCEPT_VERSION,
|
|
12
12
|
CONCEPTS_BULK_UPDATE,
|
|
13
|
+
CONCEPTS_BULK_UPLOAD_EVENTS,
|
|
13
14
|
CONCEPTS_NEW,
|
|
14
15
|
CONCEPTS_PENDING,
|
|
15
16
|
CONCEPTS_DEPRECATED,
|
|
@@ -29,6 +30,7 @@ import Concepts from "./Concepts";
|
|
|
29
30
|
import ConceptsBulkUpdate from "./ConceptsBulkUpdate";
|
|
30
31
|
import ConceptsLoader from "./ConceptsLoader";
|
|
31
32
|
import ConceptSubscriptionLoader from "./ConceptSubscriptionLoader";
|
|
33
|
+
import ConceptsUploadEvents from "./ConceptsUploadEvents";
|
|
32
34
|
|
|
33
35
|
const RelationTagsLoader = React.lazy(() =>
|
|
34
36
|
import("@truedat/lm/components/RelationTagsLoader")
|
|
@@ -56,6 +58,13 @@ export const ConceptRoutes = ({ concept, conceptLoaded, templatesLoaded }) => {
|
|
|
56
58
|
};
|
|
57
59
|
return (
|
|
58
60
|
<>
|
|
61
|
+
<Route
|
|
62
|
+
exact
|
|
63
|
+
path={CONCEPTS_BULK_UPLOAD_EVENTS}
|
|
64
|
+
render={() =>
|
|
65
|
+
authorized ? <ConceptsUploadEvents /> : <Unauthorized />
|
|
66
|
+
}
|
|
67
|
+
/>
|
|
59
68
|
<Route
|
|
60
69
|
exact
|
|
61
70
|
path={CONCEPT_LINKS_MANAGEMENT}
|
|
@@ -18,6 +18,7 @@ export const ConceptsActions = ({
|
|
|
18
18
|
hidden,
|
|
19
19
|
upload,
|
|
20
20
|
update,
|
|
21
|
+
canAutoPublish,
|
|
21
22
|
}) => {
|
|
22
23
|
const { formatMessage, locale } = useIntl();
|
|
23
24
|
|
|
@@ -42,7 +43,7 @@ export const ConceptsActions = ({
|
|
|
42
43
|
id: "concepts.actions.download.tooltip",
|
|
43
44
|
})}
|
|
44
45
|
/>
|
|
45
|
-
{upload ? <ConceptsUploadButton /> : null}
|
|
46
|
+
{upload ? <ConceptsUploadButton canAutoPublish={canAutoPublish} /> : null}
|
|
46
47
|
{update ? <ConceptsUpdateButton /> : null}
|
|
47
48
|
</div>
|
|
48
49
|
);
|
|
@@ -56,6 +57,7 @@ ConceptsActions.propTypes = {
|
|
|
56
57
|
downloadConcepts: PropTypes.func,
|
|
57
58
|
upload: PropTypes.bool,
|
|
58
59
|
update: PropTypes.bool,
|
|
60
|
+
canAutoPublish: PropTypes.bool,
|
|
59
61
|
};
|
|
60
62
|
|
|
61
63
|
ConceptsActions.defaultProps = {
|
|
@@ -71,6 +73,7 @@ const mapStateToProps = ({
|
|
|
71
73
|
_.isEmpty(conceptActions) && conceptsActions?.create ? CONCEPTS_NEW : null,
|
|
72
74
|
conceptsDownloading: conceptsDownloading,
|
|
73
75
|
hidden: _.isEmpty(conceptsActions),
|
|
76
|
+
canAutoPublish: conceptsActions?.autoPublish && true,
|
|
74
77
|
});
|
|
75
78
|
|
|
76
79
|
export default connect(mapStateToProps, { downloadConcepts, uploadConcepts })(
|
|
@@ -11,11 +11,29 @@ const uploadAction = {
|
|
|
11
11
|
method: "POST",
|
|
12
12
|
href: API_BUSINESS_CONCEPT_VERSIONS_UPLOAD,
|
|
13
13
|
};
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
export const ConceptsUploadButton = ({
|
|
15
|
+
uploadConcepts,
|
|
16
|
+
loading,
|
|
17
|
+
canAutoPublish,
|
|
18
|
+
}) => {
|
|
16
19
|
const { formatMessage, locale } = useIntl();
|
|
20
|
+
const extraAction = {
|
|
21
|
+
key: "yesWithAutoPublish",
|
|
22
|
+
primary: true,
|
|
23
|
+
content: formatMessage({ id: "uploadModal.accept.publish" }),
|
|
24
|
+
onClick: (data) => {
|
|
25
|
+
data.append("auto_publish", canAutoPublish);
|
|
26
|
+
return uploadConcepts({
|
|
27
|
+
action: "upload",
|
|
28
|
+
data,
|
|
29
|
+
lang: locale,
|
|
30
|
+
...uploadAction,
|
|
31
|
+
});
|
|
32
|
+
},
|
|
33
|
+
};
|
|
17
34
|
return (
|
|
18
35
|
<UploadModal
|
|
36
|
+
extraAction={canAutoPublish && extraAction}
|
|
19
37
|
icon="upload"
|
|
20
38
|
trigger={
|
|
21
39
|
<Button
|
|
@@ -50,6 +68,7 @@ export const ConceptsUploadButton = ({ uploadConcepts, loading }) => {
|
|
|
50
68
|
ConceptsUploadButton.propTypes = {
|
|
51
69
|
uploadConcepts: PropTypes.func,
|
|
52
70
|
loading: PropTypes.bool,
|
|
71
|
+
canAutoPublish: PropTypes.bool,
|
|
53
72
|
};
|
|
54
73
|
|
|
55
74
|
const mapStateToProps = ({ uploadConceptsFile: { loading } }) => ({
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { Header, Icon, Segment } from "semantic-ui-react";
|
|
4
|
+
import { FormattedMessage } from "react-intl";
|
|
5
|
+
import ConceptsUploadEventsTable from "./ConceptsUploadEventsTable";
|
|
6
|
+
|
|
7
|
+
export const ConceptsUploadEvents = () => (
|
|
8
|
+
<Segment>
|
|
9
|
+
<Header as="h2">
|
|
10
|
+
<Icon circular name="cogs" />
|
|
11
|
+
<Header.Content>
|
|
12
|
+
<FormattedMessage id="concepts.upload.header" />
|
|
13
|
+
<Header.Subheader>
|
|
14
|
+
<FormattedMessage id="concepts.upload.subheader" />
|
|
15
|
+
</Header.Subheader>
|
|
16
|
+
</Header.Content>
|
|
17
|
+
</Header>
|
|
18
|
+
<Segment attached="bottom">
|
|
19
|
+
<ConceptsUploadEventsTable />
|
|
20
|
+
</Segment>
|
|
21
|
+
</Segment>
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
export default ConceptsUploadEvents;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import PropTypes from "prop-types";
|
|
3
|
+
import { connect } from "react-redux";
|
|
4
|
+
|
|
5
|
+
import _ from "lodash/fp";
|
|
6
|
+
|
|
7
|
+
import { FormattedMessage } from "react-intl";
|
|
8
|
+
import { Table } from "semantic-ui-react";
|
|
9
|
+
import { useIntl } from "react-intl";
|
|
10
|
+
import { columnDecorator } from "@truedat/core/services";
|
|
11
|
+
|
|
12
|
+
import { useUploadEvents } from "../hooks/useUploadEvents";
|
|
13
|
+
import { getConceptUploadEventColumns } from "../selectors";
|
|
14
|
+
|
|
15
|
+
import "../styles/conceptsUploadEventColumns.less";
|
|
16
|
+
|
|
17
|
+
const getColumnsWithData = (conceptsResults, columns) =>
|
|
18
|
+
_.filter((column) =>
|
|
19
|
+
_.any(_.flow(columnDecorator(column), _.negate(_.isEmpty)))(conceptsResults)
|
|
20
|
+
)(columns);
|
|
21
|
+
|
|
22
|
+
const messagesImp = (data) => {
|
|
23
|
+
return _.has("errors")(data)
|
|
24
|
+
? _.flow(
|
|
25
|
+
_.propOr([], "errors"),
|
|
26
|
+
_.map((error) => ({
|
|
27
|
+
id: error.body.message,
|
|
28
|
+
context: {
|
|
29
|
+
...error.body.context,
|
|
30
|
+
},
|
|
31
|
+
defaultMessage: "concepts.upload.failed.success.errors",
|
|
32
|
+
}))
|
|
33
|
+
)(data)
|
|
34
|
+
: [];
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const ConceptsUploadEventsTable = ({ getColumns }) => {
|
|
38
|
+
const { formatMessage } = useIntl();
|
|
39
|
+
const [eventExpandedIndex, setEventExpandedIndex] = useState(null);
|
|
40
|
+
|
|
41
|
+
const { data, loading } = useUploadEvents();
|
|
42
|
+
|
|
43
|
+
const events =
|
|
44
|
+
data && !loading
|
|
45
|
+
? _.reduce(
|
|
46
|
+
(acc, event) => ({
|
|
47
|
+
...acc,
|
|
48
|
+
[`${event.task_reference}_${event.status}`]: {
|
|
49
|
+
...event,
|
|
50
|
+
response: {
|
|
51
|
+
...event.response,
|
|
52
|
+
translatedHeader: !event.response
|
|
53
|
+
? ""
|
|
54
|
+
: formatMessage(
|
|
55
|
+
{
|
|
56
|
+
id: _.isEmpty(event.response.errors)
|
|
57
|
+
? "concepts.upload.success.header"
|
|
58
|
+
: "concepts.upload.success.header_with_errors",
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
count_created: event.response.created.length,
|
|
62
|
+
count_updated: event.response.updated.length,
|
|
63
|
+
count_errors: event.response.errors.length,
|
|
64
|
+
}
|
|
65
|
+
),
|
|
66
|
+
translatedErrors: _.map((error) =>
|
|
67
|
+
formatMessage({ id: error.id }, error.context)
|
|
68
|
+
)(messagesImp(event.response)),
|
|
69
|
+
},
|
|
70
|
+
expanded: false,
|
|
71
|
+
},
|
|
72
|
+
}),
|
|
73
|
+
{},
|
|
74
|
+
data.data
|
|
75
|
+
)
|
|
76
|
+
: [];
|
|
77
|
+
|
|
78
|
+
const columns = getColumnsWithData(events, getColumns());
|
|
79
|
+
|
|
80
|
+
const toggleExpanded = (eventKey) =>
|
|
81
|
+
eventExpandedIndex === eventKey
|
|
82
|
+
? setEventExpandedIndex(null)
|
|
83
|
+
: setEventExpandedIndex(eventKey);
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<>
|
|
87
|
+
{!_.isEmpty(events) && (
|
|
88
|
+
<Table sortable selectable>
|
|
89
|
+
<Table.Header>
|
|
90
|
+
<Table.Row>
|
|
91
|
+
{columns.map((column, i) => {
|
|
92
|
+
return (
|
|
93
|
+
<Table.HeaderCell
|
|
94
|
+
key={i}
|
|
95
|
+
width={column.width}
|
|
96
|
+
content={formatMessage({
|
|
97
|
+
id: `concepts.upload.props.${column.name}`,
|
|
98
|
+
defaultMessage: column.name,
|
|
99
|
+
})}
|
|
100
|
+
/>
|
|
101
|
+
);
|
|
102
|
+
})}
|
|
103
|
+
</Table.Row>
|
|
104
|
+
</Table.Header>
|
|
105
|
+
<Table.Body>
|
|
106
|
+
{_.map(
|
|
107
|
+
(eventKey) => (
|
|
108
|
+
<Table.Row
|
|
109
|
+
className={
|
|
110
|
+
eventKey === eventExpandedIndex ? "expanded" : "contracted"
|
|
111
|
+
}
|
|
112
|
+
key={eventKey}
|
|
113
|
+
onClick={() => toggleExpanded(eventKey)}
|
|
114
|
+
>
|
|
115
|
+
{eventKey !== eventExpandedIndex ? (
|
|
116
|
+
columns &&
|
|
117
|
+
columns.map((column, key) => (
|
|
118
|
+
<Table.Cell
|
|
119
|
+
key={key}
|
|
120
|
+
textAlign={column.textAlign}
|
|
121
|
+
content={columnDecorator(column)({
|
|
122
|
+
...events[eventKey],
|
|
123
|
+
formatMessage,
|
|
124
|
+
})}
|
|
125
|
+
/>
|
|
126
|
+
))
|
|
127
|
+
) : (
|
|
128
|
+
<Table.Cell colSpan={columns.length}>
|
|
129
|
+
{events[eventKey].file_hash ? (
|
|
130
|
+
<>
|
|
131
|
+
<FormattedMessage
|
|
132
|
+
id={`concepts.upload.props.file_hash`}
|
|
133
|
+
/>
|
|
134
|
+
: {events[eventKey].file_hash}
|
|
135
|
+
</>
|
|
136
|
+
) : null}
|
|
137
|
+
{columns.map((column, key) => (
|
|
138
|
+
<div key={key}>
|
|
139
|
+
{columnDecorator(column)({
|
|
140
|
+
...events[eventKey],
|
|
141
|
+
formatMessage,
|
|
142
|
+
})}
|
|
143
|
+
</div>
|
|
144
|
+
))}
|
|
145
|
+
</Table.Cell>
|
|
146
|
+
)}
|
|
147
|
+
</Table.Row>
|
|
148
|
+
),
|
|
149
|
+
Object.keys(events)
|
|
150
|
+
)}
|
|
151
|
+
</Table.Body>
|
|
152
|
+
</Table>
|
|
153
|
+
)}
|
|
154
|
+
</>
|
|
155
|
+
);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
ConceptsUploadEventsTable.propTypes = {
|
|
159
|
+
getColumns: PropTypes.func,
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const mapStateToProps = (state) => ({
|
|
163
|
+
getColumns: () => getConceptUploadEventColumns(state),
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
export default connect(mapStateToProps)(ConceptsUploadEventsTable);
|
|
@@ -1,23 +1,45 @@
|
|
|
1
1
|
// only admin user
|
|
2
2
|
import React from "react";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
3
|
+
import { render } from "@truedat/test/render";
|
|
4
|
+
import { screen, waitFor } from "@testing-library/react";
|
|
5
|
+
import userEvent from "@testing-library/user-event";
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
// see https://github.com/airbnb/enzyme/issues/2176#issuecomment-532361526
|
|
9
|
-
jest.spyOn(React, "useContext").mockImplementation(() => intl);
|
|
7
|
+
import { ConceptsUploadButton } from "../ConceptsUploadButton";
|
|
10
8
|
|
|
11
9
|
jest.mock("@truedat/core/hooks", () => ({
|
|
12
|
-
useAuthorized: jest.fn(() => true)
|
|
10
|
+
useAuthorized: jest.fn(() => true),
|
|
13
11
|
}));
|
|
14
12
|
|
|
13
|
+
const props = {
|
|
14
|
+
uploadConcepts: jest.fn(),
|
|
15
|
+
loading: false,
|
|
16
|
+
canAutoPublish: false,
|
|
17
|
+
};
|
|
18
|
+
|
|
15
19
|
describe("<ConceptsUploadButton />", () => {
|
|
16
|
-
it("matches the latest snapshot", () => {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
it("matches the latest snapshot", async () => {
|
|
21
|
+
const { getByRole } = render(<ConceptsUploadButton {...props} />);
|
|
22
|
+
userEvent.click(getByRole("button"));
|
|
23
|
+
await waitFor(() =>
|
|
24
|
+
expect(screen.getByRole("presentation")).toBeInTheDocument()
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
expect(screen.getByRole("presentation")).toMatchSnapshot();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("autopublish button is displayed if canAutoPublish is true", async () => {
|
|
31
|
+
const customProps = {
|
|
32
|
+
...props,
|
|
33
|
+
canAutoPublish: true,
|
|
34
|
+
};
|
|
35
|
+
const { getByRole } = render(<ConceptsUploadButton {...customProps} />);
|
|
36
|
+
userEvent.click(getByRole("button"));
|
|
37
|
+
await waitFor(() =>
|
|
38
|
+
expect(screen.getByRole("presentation")).toBeInTheDocument()
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
expect(
|
|
42
|
+
screen.getByRole("button", { name: /publish/i })
|
|
43
|
+
).toBeInTheDocument();
|
|
22
44
|
});
|
|
23
45
|
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import userEvent from "@testing-library/user-event";
|
|
3
|
+
import { render } from "@truedat/test/render";
|
|
4
|
+
import ConceptsUploadEventsTable from "../ConceptsUploadEventsTable";
|
|
5
|
+
|
|
6
|
+
const events = [
|
|
7
|
+
{
|
|
8
|
+
file_hash: "99E400B91D164F8F0A39F400CA323C8F",
|
|
9
|
+
inserted_at: "2022-04-23T15:53:24.638484Z",
|
|
10
|
+
message: null,
|
|
11
|
+
response: {
|
|
12
|
+
errors: [],
|
|
13
|
+
created: [4825521, 4825561, 4825593],
|
|
14
|
+
updated: [4825522, 4825562, 4825594],
|
|
15
|
+
},
|
|
16
|
+
status: "COMPLETED",
|
|
17
|
+
task_reference: "0.3225053307.1609564164.100549",
|
|
18
|
+
user_id: 467,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
file_hash: "47D90FDF1AD967BD7DBBDAE28664278E",
|
|
22
|
+
inserted_at: "2022-04-23T15:14:48.770275Z",
|
|
23
|
+
message: null,
|
|
24
|
+
response: {
|
|
25
|
+
errors: [
|
|
26
|
+
{
|
|
27
|
+
error_type: "test_field_error",
|
|
28
|
+
body: {
|
|
29
|
+
message: "concepts.upload.failed.invalid_field_value",
|
|
30
|
+
context: {
|
|
31
|
+
field: "gdpr",
|
|
32
|
+
type: "grpd_template",
|
|
33
|
+
row: 4,
|
|
34
|
+
error: "Missing value",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
error_type: "test_forbidden_update",
|
|
40
|
+
body: {
|
|
41
|
+
message: "concepts.upload.failed.forbidden_update",
|
|
42
|
+
context: {
|
|
43
|
+
type: "test_template",
|
|
44
|
+
row: 2,
|
|
45
|
+
domain: "forbidden_domain",
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
error_type: "test_name_not_available",
|
|
51
|
+
body: {
|
|
52
|
+
message: "concepts.upload.failed.name_not_available",
|
|
53
|
+
context: {
|
|
54
|
+
type: "test_template",
|
|
55
|
+
row: 7,
|
|
56
|
+
name: "test_name_not_available",
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
created: [],
|
|
62
|
+
updated: [],
|
|
63
|
+
},
|
|
64
|
+
status: "COMPLETED",
|
|
65
|
+
task_reference: "0.3225053307.1609564162.101463",
|
|
66
|
+
user_id: 467,
|
|
67
|
+
},
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
jest.mock("../../hooks/useUploadEvents.js", () => {
|
|
71
|
+
const originalModule = jest.requireActual("../../hooks/useUploadEvents.js");
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
__esModule: true,
|
|
75
|
+
...originalModule,
|
|
76
|
+
useUploadEvents: jest.fn(() => ({
|
|
77
|
+
data: {
|
|
78
|
+
data: events,
|
|
79
|
+
},
|
|
80
|
+
loading: false,
|
|
81
|
+
})),
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe("<ConceptsUploadEventsTable />", () => {
|
|
86
|
+
it("matches the latest snapshot", () => {
|
|
87
|
+
const { container } = render(<ConceptsUploadEventsTable />);
|
|
88
|
+
expect(container).toMatchSnapshot();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("toggles row horizontal view when row is clicked", () => {
|
|
92
|
+
const { getAllByRole } = render(<ConceptsUploadEventsTable />);
|
|
93
|
+
const dataRow = getAllByRole("row")[1];
|
|
94
|
+
expect(dataRow).toHaveClass("contracted");
|
|
95
|
+
userEvent.click(dataRow);
|
|
96
|
+
expect(dataRow).toHaveClass("expanded");
|
|
97
|
+
});
|
|
98
|
+
});
|
|
@@ -1,29 +1,80 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
2
|
|
|
3
3
|
exports[`<ConceptsUploadButton /> matches the latest snapshot 1`] = `
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
<Memo(MemoizedFormattedMessage)
|
|
7
|
-
id="concepts.actions.upload.confirmation.content"
|
|
8
|
-
/>
|
|
9
|
-
}
|
|
10
|
-
handleSubmit={[Function]}
|
|
11
|
-
header={
|
|
12
|
-
<Memo(MemoizedFormattedMessage)
|
|
13
|
-
id="concepts.actions.upload.confirmation.header"
|
|
14
|
-
/>
|
|
15
|
-
}
|
|
4
|
+
<div
|
|
5
|
+
class="ui small modal transition visible active"
|
|
16
6
|
icon="upload"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
7
|
+
role="presentation"
|
|
8
|
+
>
|
|
9
|
+
<i
|
|
10
|
+
aria-hidden="true"
|
|
11
|
+
class="close icon"
|
|
12
|
+
/>
|
|
13
|
+
<div
|
|
14
|
+
class="header"
|
|
15
|
+
>
|
|
16
|
+
Confirm bulk upload
|
|
17
|
+
</div>
|
|
18
|
+
<div
|
|
19
|
+
class="content"
|
|
20
|
+
>
|
|
21
|
+
<div
|
|
22
|
+
aria-disabled="false"
|
|
23
|
+
class=""
|
|
24
|
+
data-testid="fileDropZone"
|
|
25
|
+
style="position: relative; width: 100%; margin-bottom: 10px; border: 1px dashed gray; border-radius: 5px;"
|
|
26
|
+
>
|
|
27
|
+
<div
|
|
28
|
+
style="padding: 20px; width: 100%; height: 100%;"
|
|
29
|
+
>
|
|
30
|
+
<div
|
|
31
|
+
class="ui center aligned container"
|
|
32
|
+
>
|
|
33
|
+
<i
|
|
34
|
+
aria-hidden="true"
|
|
35
|
+
class="cloud upload huge icon"
|
|
36
|
+
/>
|
|
37
|
+
<p>
|
|
38
|
+
Drag an drop file or click to select file
|
|
39
|
+
</p>
|
|
40
|
+
<button
|
|
41
|
+
class="ui secondary button"
|
|
42
|
+
>
|
|
43
|
+
<i
|
|
44
|
+
aria-hidden="true"
|
|
45
|
+
class="upload icon"
|
|
46
|
+
/>
|
|
47
|
+
Upload file
|
|
48
|
+
</button>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
<input
|
|
52
|
+
autocomplete="off"
|
|
53
|
+
multiple=""
|
|
54
|
+
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px; opacity: 0.00001; pointer-events: none;"
|
|
55
|
+
type="file"
|
|
56
|
+
/>
|
|
57
|
+
</div>
|
|
58
|
+
<h2>
|
|
59
|
+
Selected file
|
|
60
|
+
</h2>
|
|
61
|
+
<ul />
|
|
62
|
+
</div>
|
|
63
|
+
<div
|
|
64
|
+
class="actions"
|
|
65
|
+
>
|
|
66
|
+
<button
|
|
67
|
+
class="ui secondary button"
|
|
68
|
+
>
|
|
69
|
+
Cancel
|
|
70
|
+
</button>
|
|
71
|
+
<button
|
|
72
|
+
class="ui primary disabled button"
|
|
73
|
+
disabled=""
|
|
74
|
+
tabindex="-1"
|
|
75
|
+
>
|
|
76
|
+
Upload and update
|
|
77
|
+
</button>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
29
80
|
`;
|
package/src/concepts/components/__tests__/__snapshots__/ConceptsUploadEventsTable.spec.js.snap
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<ConceptsUploadEventsTable /> 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
|
+
Status
|
|
18
|
+
</th>
|
|
19
|
+
<th
|
|
20
|
+
class="two wide"
|
|
21
|
+
>
|
|
22
|
+
Response
|
|
23
|
+
</th>
|
|
24
|
+
<th
|
|
25
|
+
class="two wide"
|
|
26
|
+
>
|
|
27
|
+
Date
|
|
28
|
+
</th>
|
|
29
|
+
</tr>
|
|
30
|
+
</thead>
|
|
31
|
+
<tbody
|
|
32
|
+
class=""
|
|
33
|
+
>
|
|
34
|
+
<tr
|
|
35
|
+
class="contracted"
|
|
36
|
+
>
|
|
37
|
+
<td
|
|
38
|
+
class=""
|
|
39
|
+
>
|
|
40
|
+
Completed
|
|
41
|
+
</td>
|
|
42
|
+
<td
|
|
43
|
+
class=""
|
|
44
|
+
>
|
|
45
|
+
<p>
|
|
46
|
+
Upload success! Created: 3, updated: 3
|
|
47
|
+
</p>
|
|
48
|
+
<ul
|
|
49
|
+
class="bulk-upload-error"
|
|
50
|
+
/>
|
|
51
|
+
</td>
|
|
52
|
+
<td
|
|
53
|
+
class="center aligned"
|
|
54
|
+
>
|
|
55
|
+
<time
|
|
56
|
+
datetime="1650729204638"
|
|
57
|
+
>
|
|
58
|
+
2022-04-23 15:53
|
|
59
|
+
</time>
|
|
60
|
+
</td>
|
|
61
|
+
</tr>
|
|
62
|
+
<tr
|
|
63
|
+
class="contracted"
|
|
64
|
+
>
|
|
65
|
+
<td
|
|
66
|
+
class=""
|
|
67
|
+
>
|
|
68
|
+
Completed
|
|
69
|
+
</td>
|
|
70
|
+
<td
|
|
71
|
+
class=""
|
|
72
|
+
>
|
|
73
|
+
<p>
|
|
74
|
+
Upload success with errors. Created: 0, updated: 0, errors: 3
|
|
75
|
+
</p>
|
|
76
|
+
<ul
|
|
77
|
+
class="bulk-upload-error"
|
|
78
|
+
>
|
|
79
|
+
<li>
|
|
80
|
+
The field gdpr of row 4 of type grpd_template is invalid. Error: Missing value
|
|
81
|
+
</li>
|
|
82
|
+
<li>
|
|
83
|
+
Can't update concept of row 2 of type test_template. Unauthorized concept updating for forbidden_domain domain
|
|
84
|
+
</li>
|
|
85
|
+
<li>
|
|
86
|
+
Concept name not available. Row 7 of type test_template. Name test_name_not_available.
|
|
87
|
+
</li>
|
|
88
|
+
</ul>
|
|
89
|
+
<a
|
|
90
|
+
class="alert-exporter-in-table"
|
|
91
|
+
download="errors.csv"
|
|
92
|
+
href="data:text/csv;charset=utf-8,"RENDEREDMESSAGE","ERROR_TYPE","BODY.MESSAGE","BODY.CONTEXT.FIELD","BODY.CONTEXT.TYPE","BODY.CONTEXT.ROW","BODY.CONTEXT.ERROR","BODY.CONTEXT.DOMAIN","BODY.CONTEXT.NAME"
|
|
93
|
+
"The field gdpr of row 4 of type grpd_template is invalid. Error: Missing value","test_field_error","concepts.upload.failed.invalid_field_value","gdpr","grpd_template","4","Missing value","",""
|
|
94
|
+
"Can't update concept of row 2 of type test_template. Unauthorized concept updating for forbidden_domain domain","test_forbidden_update","concepts.upload.failed.forbidden_update","","test_template","2","","forbidden_domain",""
|
|
95
|
+
"Concept name not available. Row 7 of type test_template. Name test_name_not_available.","test_name_not_available","concepts.upload.failed.name_not_available","","test_template","7","","","test_name_not_available""
|
|
96
|
+
style="float: right;"
|
|
97
|
+
target="_self"
|
|
98
|
+
>
|
|
99
|
+
Export to CSV
|
|
100
|
+
</a>
|
|
101
|
+
</td>
|
|
102
|
+
<td
|
|
103
|
+
class="center aligned"
|
|
104
|
+
>
|
|
105
|
+
<time
|
|
106
|
+
datetime="1650726888770"
|
|
107
|
+
>
|
|
108
|
+
2022-04-23 15:14
|
|
109
|
+
</time>
|
|
110
|
+
</td>
|
|
111
|
+
</tr>
|
|
112
|
+
</tbody>
|
|
113
|
+
</table>
|
|
114
|
+
</div>
|
|
115
|
+
`;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import useSWR from "swr";
|
|
2
|
+
import { apiJson } from "@truedat/core/services/api";
|
|
3
|
+
import { API_BUSINESS_CONCEPT_BULK_UPLOAD } from "../api";
|
|
4
|
+
|
|
5
|
+
export const useUploadEvents = () => {
|
|
6
|
+
const { data, error, mutate } = useSWR(
|
|
7
|
+
API_BUSINESS_CONCEPT_BULK_UPLOAD,
|
|
8
|
+
apiJson
|
|
9
|
+
);
|
|
10
|
+
return { data: data?.data, error, loading: !error && !data, mutate };
|
|
11
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getConceptUploadEventColumns,
|
|
3
|
+
defaultConceptsUploadEventColumns,
|
|
4
|
+
} from "..";
|
|
5
|
+
|
|
6
|
+
describe("selectors: getConceptUploadEventColumns", () => {
|
|
7
|
+
it("should return custom conceptsUploadEventColumns when present", () => {
|
|
8
|
+
const conceptsUploadEventColumns = [{ name: "test" }];
|
|
9
|
+
const res = getConceptUploadEventColumns({
|
|
10
|
+
conceptsUploadEventColumns,
|
|
11
|
+
});
|
|
12
|
+
expect(res).toHaveLength(1);
|
|
13
|
+
expect(res).toEqual(conceptsUploadEventColumns);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("should return default defaultRuleImplementationColumns when no customized", () => {
|
|
17
|
+
const res = getConceptUploadEventColumns({});
|
|
18
|
+
expect(res).toHaveLength(defaultConceptsUploadEventColumns.length);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
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="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 id={`concepts.upload.status.${_.toLower(status)}`} />
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
export const defaultConceptsUploadEventColumns = [
|
|
41
|
+
{
|
|
42
|
+
name: "filename",
|
|
43
|
+
width: 2,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: "status",
|
|
47
|
+
fieldDecorator: statusDecorator,
|
|
48
|
+
width: 2,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: "response",
|
|
52
|
+
fieldDecorator: bulkUpdateErrors,
|
|
53
|
+
width: 2,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: "message",
|
|
57
|
+
width: 2,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: "inserted_at",
|
|
61
|
+
fieldDecorator: dateDecorator,
|
|
62
|
+
textAlign: "center",
|
|
63
|
+
width: 2,
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
const getColumns = (state) => state.conceptsUploadEventColumns;
|
|
68
|
+
|
|
69
|
+
export const getConceptUploadEventColumns = createSelector(
|
|
70
|
+
[getColumns],
|
|
71
|
+
(columns) => _.defaultTo(defaultConceptsUploadEventColumns)(columns)
|
|
72
|
+
);
|
|
@@ -5,6 +5,10 @@ export { getConceptFilterTypes } from "./getConceptFilterTypes";
|
|
|
5
5
|
export { getConceptSelectedFilters } from "./getConceptSelectedFilters";
|
|
6
6
|
export { getConceptSelectedFilterActiveValues } from "./getConceptSelectedFilterActiveValues";
|
|
7
7
|
export { getConceptSelectedFilterValues } from "./getConceptSelectedFilterValues";
|
|
8
|
+
export {
|
|
9
|
+
getConceptUploadEventColumns,
|
|
10
|
+
defaultConceptsUploadEventColumns,
|
|
11
|
+
} from "./getConceptUploadEventColumns";
|
|
8
12
|
export { getParsedEvents } from "./getParsedEvents";
|
|
9
13
|
export { getPreviousConceptQuery } from "./getPreviousConceptQuery";
|
|
10
14
|
export { getTemplateFields } from "./getTemplateFields";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
.contracted ul.bulk-upload-error {
|
|
2
|
+
max-height: 100px;
|
|
3
|
+
overflow: hidden;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.contracted ul.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
|
+
}
|
package/src/messages/en.js
CHANGED
|
@@ -183,25 +183,61 @@ export default {
|
|
|
183
183
|
"concepts.subheader.view": "Query business concepts",
|
|
184
184
|
"concepts.summary": "Summary",
|
|
185
185
|
"concepts.taxonomy": "Taxonomy",
|
|
186
|
+
"concepts.upload.header": "Upload concepts events",
|
|
187
|
+
"concepts.upload.subheader": "Upload concepts events by XLSX",
|
|
188
|
+
"concepts.upload.failed.business_concept_not_exists":
|
|
189
|
+
"The concept with id {id} not exists. File {row} of type {type}",
|
|
190
|
+
"concepts.upload.failed.domain_changed":
|
|
191
|
+
"Domain can't be changed. Row {row} of type {type} domain {domain}",
|
|
192
|
+
"concepts.upload.failed.empty_template_name": "Empty template name",
|
|
193
|
+
"concepts.upload.failed.forbidden":
|
|
194
|
+
"Access forbidden to domain. Row {row}. Domain {domain}.",
|
|
195
|
+
"concepts.upload.failed.forbidden_creation":
|
|
196
|
+
"Can't create concept of row {row} of type {type}. Unauthorized concept creation for {domain} domain",
|
|
197
|
+
"concepts.upload.failed.forbidden_publish":
|
|
198
|
+
"Can't publish concept of row {row} of type {type}. Unauthorized concept publishing for {domain} domain",
|
|
199
|
+
"concepts.upload.failed.forbidden_update":
|
|
200
|
+
"Can't update concept of row {row} of type {type}. Unauthorized concept updating for {domain} domain",
|
|
201
|
+
"concepts.upload.failed.forbidden_version":
|
|
202
|
+
"The concept with {id} of row {row} of type {type} can't be versioned",
|
|
186
203
|
"concepts.upload.failed.header":
|
|
187
|
-
"
|
|
188
|
-
"
|
|
189
|
-
"
|
|
204
|
+
"Headers {headers} of type {type} are missing but are required",
|
|
205
|
+
"concepts.upload.failed.invalid_concept":
|
|
206
|
+
"Invalid concept id {id}, doesn't exists. Row {row} of type {type}",
|
|
190
207
|
"concepts.upload.failed.invalid_domain":
|
|
191
|
-
"Invalid domain. Row {row}. Domain {domain}.",
|
|
208
|
+
"Invalid domain. Row {row} of type {type}. Domain {domain}.",
|
|
209
|
+
"concepts.upload.failed.invalid_field_value":
|
|
210
|
+
"The field {field} of row {row} of type {type} is invalid. Error: {error}",
|
|
192
211
|
"concepts.upload.failed.invalid_file_format": "File format is invalid.",
|
|
193
212
|
"concepts.upload.failed.invalid_template":
|
|
194
|
-
"
|
|
213
|
+
"Template {template} doesn't exists",
|
|
195
214
|
"concepts.upload.failed.missing_required_columns":
|
|
196
215
|
"Missing required columns. Expected [{expected}]. Found [{found}].",
|
|
197
216
|
"concepts.upload.failed.missing_value":
|
|
198
217
|
"Missing required value. Row {row}. Field {field}.",
|
|
199
218
|
"concepts.upload.failed.name_not_available":
|
|
200
|
-
"Concept name not available. Row {row}. Name {name}.",
|
|
201
|
-
"concepts.upload.failed.
|
|
202
|
-
|
|
219
|
+
"Concept name not available. Row {row} of type {type}. Name {name}.",
|
|
220
|
+
"concepts.upload.failed.success.errors": "Error in line {row} of type {type}",
|
|
221
|
+
"concepts.upload.props.filename": "File",
|
|
222
|
+
"concepts.upload.props.file_hash": "File hash",
|
|
223
|
+
"concepts.upload.props.inserted_at": "Date",
|
|
224
|
+
"concepts.upload.props.message": "Message",
|
|
225
|
+
"concepts.upload.props.response": "Response",
|
|
226
|
+
"concepts.upload.props.status": "Status",
|
|
227
|
+
"concepts.upload.status.already_calculated": "Already calculated",
|
|
228
|
+
"concepts.upload.status.already_started": "Already started",
|
|
229
|
+
"concepts.upload.status.completed": "Completed",
|
|
230
|
+
"concepts.upload.status.failed": "Failed",
|
|
231
|
+
"concepts.upload.status.just_started": "Just started",
|
|
232
|
+
"concepts.upload.status.started": "Started",
|
|
233
|
+
"concepts.upload.status.timed_out": "Timed out",
|
|
234
|
+
"concepts.upload.success.accepted.header":
|
|
235
|
+
"XLSX accepted. Status can be checked at the link below:",
|
|
203
236
|
"concepts.upload.success.content": "Loaded {count} concepts successfully",
|
|
204
|
-
"concepts.upload.success.header":
|
|
237
|
+
"concepts.upload.success.header":
|
|
238
|
+
"Upload success! Created: {count_created}, updated: {count_updated}",
|
|
239
|
+
"concepts.upload.success.header_with_errors":
|
|
240
|
+
"Upload success with errors. Created: {count_created}, updated: {count_updated}, errors: {count_errors}",
|
|
205
241
|
"createDomain.error.external_id.blank": "Empty external id",
|
|
206
242
|
"createDomain.error.external_id.unique":
|
|
207
243
|
"A domain with the same external id exists",
|
package/src/messages/es.js
CHANGED
|
@@ -184,27 +184,64 @@ export default {
|
|
|
184
184
|
"concepts.subheader.view": "Consultar conceptos de negocio",
|
|
185
185
|
"concepts.summary": "Resumen",
|
|
186
186
|
"concepts.taxonomy": "Taxonomía",
|
|
187
|
+
"concepts.upload.header": "Eventos de subida de conceptos",
|
|
188
|
+
"concepts.upload.subheader": "Eventos de subida de conceptos por XLSX",
|
|
189
|
+
"concepts.upload.failed.business_concept_not_exists":
|
|
190
|
+
"El concepto con id {id} no existe. Fila {row} del tipo {type}",
|
|
191
|
+
"concepts.upload.failed.domain_changed":
|
|
192
|
+
"El dominio no puede ser cambiado. Fila {row} of tipo {type} dominio {domain}",
|
|
193
|
+
"concepts.upload.failed.empty_template_name": "Nombre de plantilla vacío",
|
|
194
|
+
"concepts.upload.failed.forbidden":
|
|
195
|
+
"Acceso a dominio no permitido. Línea: {row}. Dominio: {domain}.",
|
|
196
|
+
"concepts.upload.failed.forbidden_creation":
|
|
197
|
+
"No se puede crear el concepto de la fila {row} de la template {template}. La creación de conceptos en el dominio {domain} no está autorizada",
|
|
198
|
+
"concepts.upload.failed.forbidden_publish":
|
|
199
|
+
"No se puede publicar el concepto de la fila {row} del tipo {type}. La publicación de conceptos en el dominio {domain} no está autorizada",
|
|
200
|
+
"concepts.upload.failed.forbidden_update":
|
|
201
|
+
"No se puede actualizar el concepto de la fila {row} del tipo {type}. La actualización de conceptos en el dominio {domain} no está autorizada",
|
|
202
|
+
"concepts.upload.failed.forbidden_version":
|
|
203
|
+
"No se puede versionar el concepto con id {id} de la fila {row} del tipo {type}",
|
|
187
204
|
"concepts.upload.failed.header":
|
|
188
|
-
"
|
|
189
|
-
"
|
|
190
|
-
"
|
|
205
|
+
"Las cabeceras {headers} del tipo {type} son requeridas pero no están definidas",
|
|
206
|
+
"concepts.upload.failed.invalid_concept":
|
|
207
|
+
"El concepto con id {id} no existe. Fila {row} del tipo {type}",
|
|
191
208
|
"concepts.upload.failed.invalid_domain":
|
|
192
|
-
"
|
|
209
|
+
"Dominio inválido. Línea {row} del tipo {type}. Dominio: {domain}.",
|
|
210
|
+
"concepts.upload.failed.invalid_field_value":
|
|
211
|
+
"El campo {field} de la fila {row} del tipo {type} es inválido. Error: {error}",
|
|
193
212
|
"concepts.upload.failed.invalid_file_format":
|
|
194
213
|
"Formato del fichero es inválido.",
|
|
195
214
|
"concepts.upload.failed.invalid_template":
|
|
196
|
-
"
|
|
215
|
+
"La plantilla {template} no existe",
|
|
197
216
|
"concepts.upload.failed.missing_required_columns":
|
|
198
217
|
"Faltan columnas obligatorias en el fichero. Esperado [{expected}]. Recibido [{found}].",
|
|
199
218
|
"concepts.upload.failed.missing_value":
|
|
200
219
|
"Campo obligatorio no informado. Linea: {row}. Campo: {field}",
|
|
201
220
|
"concepts.upload.failed.name_not_available":
|
|
202
|
-
"Nombre de concepto no disponible. Linea: {row}. Nombre: {name}",
|
|
203
|
-
"concepts.upload.failed.
|
|
204
|
-
"
|
|
221
|
+
"Nombre de concepto no disponible. Linea: {row} tipo {type}. Nombre: {name}",
|
|
222
|
+
"concepts.upload.failed.success.errors":
|
|
223
|
+
"Error en linea {row} del tipo {type}",
|
|
224
|
+
"concepts.upload.props.filename": "Fichero",
|
|
225
|
+
"concepts.upload.props.file_hash": "Hash de Fichero",
|
|
226
|
+
"concepts.upload.props.inserted_at": "Fecha",
|
|
227
|
+
"concepts.upload.props.message": "Mensaje",
|
|
228
|
+
"concepts.upload.props.response": "Respuesta",
|
|
229
|
+
"concepts.upload.props.status": "Estado",
|
|
230
|
+
"concepts.upload.status.already_calculated": "Previamente calculado",
|
|
231
|
+
"concepts.upload.status.already_started": "Previamente iniciado",
|
|
232
|
+
"concepts.upload.status.completed": "Finalizado",
|
|
233
|
+
"concepts.upload.status.failed": "Fallido",
|
|
234
|
+
"concepts.upload.status.just_started": "Acaba de iniciarse",
|
|
235
|
+
"concepts.upload.status.started": "Iniciado",
|
|
236
|
+
"concepts.upload.status.timed_out": "Tiempo de espera máximo sobrepasado",
|
|
237
|
+
"concepts.upload.success.accepted.header":
|
|
238
|
+
"XLSX aceptado. Puede comprobar el estado en el siguiente enlace:",
|
|
205
239
|
"concepts.upload.success.content":
|
|
206
240
|
"Se han insertado correctamente {count} términos",
|
|
207
|
-
"concepts.upload.success.header":
|
|
241
|
+
"concepts.upload.success.header":
|
|
242
|
+
"Fichero subido correctamente. Creados: {count_created}, actualizados: {count_updated}",
|
|
243
|
+
"concepts.upload.success.header_with_errors":
|
|
244
|
+
"Fichero subido con errores. Creados: {count_created}, actualizados: {count_updated}, errores: {count_errors}",
|
|
208
245
|
"createDomain.error.external_id.blank": "Id Externo vacío",
|
|
209
246
|
"createDomain.error.external_id.unique":
|
|
210
247
|
"Ya existe un dominio con el mismo id externo",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CONCEPTS_BULK_UPLOAD_EVENTS } from "@truedat/core/routes";
|
|
1
2
|
import { uploadConcepts } from "../../routines";
|
|
2
3
|
import { bgMessage } from "..";
|
|
3
4
|
|
|
@@ -14,16 +15,18 @@ describe("reducers: bgMessage", () => {
|
|
|
14
15
|
expect(
|
|
15
16
|
bgMessage(fooState, {
|
|
16
17
|
type: uploadConcepts.SUCCESS,
|
|
17
|
-
payload: { data: { message: { length: 5 } } }
|
|
18
|
+
payload: { data: { message: { length: 5 } } },
|
|
18
19
|
})
|
|
19
20
|
).toEqual({
|
|
20
21
|
error: false,
|
|
21
|
-
header: "concepts.upload.success.header",
|
|
22
|
-
content: "concepts.upload.success.content",
|
|
22
|
+
header: "concepts.upload.success.accepted.header",
|
|
23
23
|
icon: "check",
|
|
24
|
-
color: "
|
|
24
|
+
color: "blue",
|
|
25
25
|
text: "",
|
|
26
|
-
|
|
26
|
+
anchor: {
|
|
27
|
+
reference: CONCEPTS_BULK_UPLOAD_EVENTS,
|
|
28
|
+
label: "sidemenu.concepts_upload_events",
|
|
29
|
+
},
|
|
27
30
|
});
|
|
28
31
|
});
|
|
29
32
|
|
|
@@ -33,10 +36,10 @@ describe("reducers: bgMessage", () => {
|
|
|
33
36
|
type: uploadConcepts.FAILURE,
|
|
34
37
|
payload: {
|
|
35
38
|
data: {
|
|
36
|
-
error: { error: "unprocessable_entity", field: "What a field!!" }
|
|
39
|
+
error: { error: "unprocessable_entity", field: "What a field!!" },
|
|
37
40
|
},
|
|
38
|
-
status: 422
|
|
39
|
-
}
|
|
41
|
+
status: 422,
|
|
42
|
+
},
|
|
40
43
|
})
|
|
41
44
|
).toEqual({
|
|
42
45
|
error: true,
|
|
@@ -44,7 +47,7 @@ describe("reducers: bgMessage", () => {
|
|
|
44
47
|
content: `concepts.upload.failed.unprocessable_entity`,
|
|
45
48
|
icon: "attention",
|
|
46
49
|
text: "",
|
|
47
|
-
fields: { error: "unprocessable_entity", field: "What a field!!" }
|
|
50
|
+
fields: { error: "unprocessable_entity", field: "What a field!!" },
|
|
48
51
|
});
|
|
49
52
|
});
|
|
50
53
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import _ from "lodash/fp";
|
|
2
2
|
import { dismissAlert } from "@truedat/core/routines";
|
|
3
|
+
import { CONCEPTS_BULK_UPLOAD_EVENTS } from "@truedat/core/routes";
|
|
3
4
|
import { uploadConcepts } from "../routines";
|
|
4
5
|
import { bulkUpdate } from "../concepts/routines";
|
|
5
6
|
|
|
@@ -12,24 +13,26 @@ const bgMessage = (state = initialState, { type, payload }) => {
|
|
|
12
13
|
case uploadConcepts.SUCCESS:
|
|
13
14
|
return {
|
|
14
15
|
error: false,
|
|
15
|
-
header: "concepts.upload.success.header",
|
|
16
|
-
content: "concepts.upload.success.content",
|
|
16
|
+
header: "concepts.upload.success.accepted.header",
|
|
17
17
|
icon: "check",
|
|
18
|
-
color: "
|
|
18
|
+
color: "blue",
|
|
19
19
|
text: "",
|
|
20
|
-
|
|
20
|
+
anchor: {
|
|
21
|
+
reference: CONCEPTS_BULK_UPLOAD_EVENTS,
|
|
22
|
+
label: "sidemenu.concepts_upload_events",
|
|
23
|
+
},
|
|
21
24
|
};
|
|
22
25
|
case uploadConcepts.FAILURE:
|
|
23
26
|
if (payload.status != 500 && !_.path("data.errors")(payload)) {
|
|
24
27
|
return {
|
|
25
28
|
error: true,
|
|
26
29
|
header: "concepts.upload.failed.header",
|
|
27
|
-
content: `concepts.upload.failed.${
|
|
28
|
-
payload
|
|
29
|
-
|
|
30
|
+
content: `concepts.upload.failed.${
|
|
31
|
+
_.path("data.error.error")(payload) || _.path("data.error")(payload)
|
|
32
|
+
}`,
|
|
30
33
|
icon: "attention",
|
|
31
34
|
text: "",
|
|
32
|
-
fields: _.path("data.error")(payload)
|
|
35
|
+
fields: _.path("data.error")(payload),
|
|
33
36
|
};
|
|
34
37
|
} else {
|
|
35
38
|
return null;
|
|
@@ -42,7 +45,7 @@ const bgMessage = (state = initialState, { type, payload }) => {
|
|
|
42
45
|
icon: "check",
|
|
43
46
|
color: "green",
|
|
44
47
|
text: "",
|
|
45
|
-
fields: { count: payload.data.message.length }
|
|
48
|
+
fields: { count: payload.data.message.length },
|
|
46
49
|
};
|
|
47
50
|
default:
|
|
48
51
|
return state;
|