@truedat/bg 8.13.4 → 8.13.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/package.json +3 -3
- package/src/concepts/api.js +5 -9
- package/src/concepts/components/ConceptEdit.js +23 -1
- package/src/concepts/components/ConceptForm.js +28 -1
- package/src/concepts/components/ConceptRoutes.js +10 -13
- package/src/concepts/components/ConceptsUploadButton.js +14 -5
- package/src/concepts/components/__tests__/ConceptEdit.dateRestrictions.spec.js +84 -0
- package/src/concepts/components/__tests__/ConceptEdit.spec.js +29 -0
- package/src/concepts/components/__tests__/ConceptEditLoader.spec.js +159 -0
- package/src/concepts/components/__tests__/ConceptForm.spec.js +97 -1
- package/src/concepts/components/__tests__/ConceptRoutes.spec.js +11 -6
- package/src/concepts/components/__tests__/__snapshots__/ConceptRoutes.spec.js.snap +8 -11
- package/src/concepts/hooks/useConcepts.js +9 -4
- package/src/concepts/sagas/__tests__/downloadConcepts.spec.js +3 -3
- package/src/concepts/sagas/downloadConcepts.js +2 -2
- package/src/concepts/selectors/__tests__/messages.spec.js +63 -0
- package/src/concepts/selectors/index.js +0 -4
- package/src/concepts/selectors/messages.js +20 -3
- package/src/concepts/components/ConceptsUploadEvents.js +0 -31
- package/src/concepts/components/ConceptsUploadEventsTable.js +0 -183
- package/src/concepts/components/__tests__/ConceptsUploadEventsTable.spec.js +0 -100
- package/src/concepts/components/__tests__/__snapshots__/ConceptsUploadEventsTable.spec.js.snap +0 -115
- package/src/concepts/hooks/useUploadEvents.js +0 -11
- package/src/concepts/selectors/__tests__/getConceptUploadEventColumns.spec.js +0 -20
- package/src/concepts/selectors/getConceptUploadEventColumns.js +0 -66
- package/src/concepts/styles/ConceptsUploadEventColumns.less +0 -24
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { waitFor } from "@testing-library/react";
|
|
2
2
|
import { render, waitForLoad } from "@truedat/test/render";
|
|
3
3
|
import { domainsMock, multipleTemplatesMock } from "@truedat/test/mocks";
|
|
4
|
-
import ConceptForm from "../ConceptForm";
|
|
4
|
+
import ConceptForm, { conceptHasDateRestrictionErrors } from "../ConceptForm";
|
|
5
5
|
|
|
6
6
|
jest.mock("@truedat/ai/hooks/useSuggestions", () => {
|
|
7
7
|
const originalModule = jest.requireActual("@truedat/ai/hooks/useSuggestions");
|
|
@@ -76,7 +76,103 @@ const props = {
|
|
|
76
76
|
const templateVariables = { scope: "bg", domainIds: [1] };
|
|
77
77
|
const domainVariables = { action: "createBusinessConcept" };
|
|
78
78
|
|
|
79
|
+
const dateTemplate = {
|
|
80
|
+
content: [
|
|
81
|
+
{
|
|
82
|
+
name: "dates",
|
|
83
|
+
fields: [
|
|
84
|
+
{
|
|
85
|
+
name: "fecha",
|
|
86
|
+
type: "date",
|
|
87
|
+
restrictions: { resolved: { min: null, max: "2026-10-15" } },
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const savedConcept = {
|
|
95
|
+
en: { content: { fecha: { value: "2026-10-16", origin: "user" } } },
|
|
96
|
+
};
|
|
97
|
+
|
|
79
98
|
describe("<ConceptForm />", () => {
|
|
99
|
+
it("allows an unchanged existing date outside the latest restriction", () => {
|
|
100
|
+
expect(
|
|
101
|
+
conceptHasDateRestrictionErrors(
|
|
102
|
+
dateTemplate,
|
|
103
|
+
{
|
|
104
|
+
en: {
|
|
105
|
+
name: "Updated name",
|
|
106
|
+
content: { fecha: { value: "2026-10-16", origin: "user" } },
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
savedConcept,
|
|
110
|
+
),
|
|
111
|
+
).toBe(false);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("rejects a modified date outside the latest restriction", () => {
|
|
115
|
+
expect(
|
|
116
|
+
conceptHasDateRestrictionErrors(
|
|
117
|
+
dateTemplate,
|
|
118
|
+
{ en: { content: { fecha: { value: "2026-10-17", origin: "user" } } } },
|
|
119
|
+
savedConcept,
|
|
120
|
+
),
|
|
121
|
+
).toBe(true);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("does not exempt a new language using another language's saved date", () => {
|
|
125
|
+
expect(
|
|
126
|
+
conceptHasDateRestrictionErrors(
|
|
127
|
+
dateTemplate,
|
|
128
|
+
{
|
|
129
|
+
...savedConcept,
|
|
130
|
+
es: { content: { fecha: { value: "2026-10-16", origin: "user" } } },
|
|
131
|
+
},
|
|
132
|
+
savedConcept,
|
|
133
|
+
),
|
|
134
|
+
).toBe(true);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("detects an invalid date so Save can be disabled", () => {
|
|
138
|
+
const template = {
|
|
139
|
+
content: [
|
|
140
|
+
{
|
|
141
|
+
name: "dates",
|
|
142
|
+
fields: [
|
|
143
|
+
{
|
|
144
|
+
name: "fecha",
|
|
145
|
+
type: "date",
|
|
146
|
+
restrictions: {
|
|
147
|
+
resolved: { min: null, max: "2026-10-15" },
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
expect(
|
|
156
|
+
conceptHasDateRestrictionErrors(template, {
|
|
157
|
+
en: {
|
|
158
|
+
content: {
|
|
159
|
+
fecha: { value: "2026-10-16", origin: "user" },
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
}),
|
|
163
|
+
).toBe(true);
|
|
164
|
+
|
|
165
|
+
expect(
|
|
166
|
+
conceptHasDateRestrictionErrors(template, {
|
|
167
|
+
en: {
|
|
168
|
+
content: {
|
|
169
|
+
fecha: { value: "2026-10-15", origin: "user" },
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
}),
|
|
173
|
+
).toBe(false);
|
|
174
|
+
});
|
|
175
|
+
|
|
80
176
|
describe("with multiple templates", () => {
|
|
81
177
|
const renderOpts = {
|
|
82
178
|
mocks: [
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
CONCEPTS_DEPRECATED,
|
|
8
8
|
CONCEPTS_NEW,
|
|
9
9
|
CONCEPTS_BULK_UPDATE,
|
|
10
|
-
|
|
10
|
+
CONCEPTS_UPLOAD_JOBS,
|
|
11
11
|
CONCEPT_LINKS_MANAGEMENT,
|
|
12
12
|
CONCEPT_VERSION,
|
|
13
13
|
CONCEPT_EDIT,
|
|
@@ -35,8 +35,11 @@ jest.mock("../ConceptsBulkUpdate", () => () => <div>ConceptsBulkUpdate</div>);
|
|
|
35
35
|
jest.mock("../ConceptSubscriptionLoader", () => () => (
|
|
36
36
|
<div>ConceptSubscriptionLoader</div>
|
|
37
37
|
));
|
|
38
|
-
jest.mock("
|
|
39
|
-
<div>
|
|
38
|
+
jest.mock("@truedat/core/components/UploadJobs", () => () => (
|
|
39
|
+
<div>UploadJobs</div>
|
|
40
|
+
));
|
|
41
|
+
jest.mock("@truedat/core/components/UploadJob", () => () => (
|
|
42
|
+
<div>UploadJob</div>
|
|
40
43
|
));
|
|
41
44
|
jest.mock("../../relations/components/ConceptLinksApprovals", () => () => (
|
|
42
45
|
<div>ConceptLinksApprovals</div>
|
|
@@ -106,9 +109,9 @@ describe("<ConceptRoutes />", () => {
|
|
|
106
109
|
expect(rendered.container).toMatchSnapshot();
|
|
107
110
|
});
|
|
108
111
|
|
|
109
|
-
it("renders correctly with
|
|
112
|
+
it("renders correctly with upload jobs route", async () => {
|
|
110
113
|
const rendered = render(<ConceptRoutes />, {
|
|
111
|
-
routes: [
|
|
114
|
+
routes: [CONCEPTS_UPLOAD_JOBS],
|
|
112
115
|
});
|
|
113
116
|
await waitForLoad(rendered);
|
|
114
117
|
expect(rendered.container).toMatchSnapshot();
|
|
@@ -149,10 +152,12 @@ describe("<ConceptRoutes />", () => {
|
|
|
149
152
|
state: {
|
|
150
153
|
concept: { business_concept_id: "123" },
|
|
151
154
|
templatesLoading: false,
|
|
152
|
-
templates:
|
|
155
|
+
templates: [],
|
|
153
156
|
},
|
|
154
157
|
});
|
|
155
158
|
await waitForLoad(rendered);
|
|
159
|
+
expect(rendered.getByText("ConceptEdit")).toBeInTheDocument();
|
|
160
|
+
expect(rendered.queryByText("TemplatesLoader")).not.toBeInTheDocument();
|
|
156
161
|
expect(rendered.container).toMatchSnapshot();
|
|
157
162
|
});
|
|
158
163
|
|
|
@@ -14,22 +14,11 @@ exports[`<ConceptRoutes /> renders correctly with bulk update route 1`] = `
|
|
|
14
14
|
</div>
|
|
15
15
|
`;
|
|
16
16
|
|
|
17
|
-
exports[`<ConceptRoutes /> renders correctly with bulk upload events route 1`] = `
|
|
18
|
-
<div>
|
|
19
|
-
<div>
|
|
20
|
-
ConceptsUploadEvents
|
|
21
|
-
</div>
|
|
22
|
-
</div>
|
|
23
|
-
`;
|
|
24
|
-
|
|
25
17
|
exports[`<ConceptRoutes /> renders correctly with concept edit route 1`] = `
|
|
26
18
|
<div>
|
|
27
19
|
<div>
|
|
28
20
|
ConceptCrumbs
|
|
29
21
|
</div>
|
|
30
|
-
<div>
|
|
31
|
-
TemplatesLoader
|
|
32
|
-
</div>
|
|
33
22
|
<div>
|
|
34
23
|
ConceptLoader
|
|
35
24
|
</div>
|
|
@@ -139,6 +128,14 @@ exports[`<ConceptRoutes /> renders correctly with subscope route 1`] = `
|
|
|
139
128
|
</div>
|
|
140
129
|
`;
|
|
141
130
|
|
|
131
|
+
exports[`<ConceptRoutes /> renders correctly with upload jobs route 1`] = `
|
|
132
|
+
<div>
|
|
133
|
+
<div>
|
|
134
|
+
UploadJobs
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
`;
|
|
138
|
+
|
|
142
139
|
exports[`<ConceptRoutes /> renders unauthorized component when not authorized 1`] = `
|
|
143
140
|
<div>
|
|
144
141
|
<div
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import useSWR from "swr";
|
|
2
2
|
import useSWRMutations from "swr/mutation";
|
|
3
3
|
import FileSaver from "file-saver";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
apiJson,
|
|
6
|
+
apiJsonPost,
|
|
7
|
+
JSON_OPTS,
|
|
8
|
+
UPLOAD_JSON_OPTS,
|
|
9
|
+
} from "@truedat/core/services/api";
|
|
5
10
|
|
|
6
11
|
import {
|
|
7
12
|
API_CONCEPT_SUGGESTIONS,
|
|
@@ -9,7 +14,7 @@ import {
|
|
|
9
14
|
API_CONCEPT_LINKS_DOWNLOAD,
|
|
10
15
|
API_BUSINESS_CONCEPT_VERSIONS_SEARCH,
|
|
11
16
|
API_BUSINESS_CONCEPT_VERSIONS_ACTIONS,
|
|
12
|
-
|
|
17
|
+
API_BUSINESS_CONCEPTS_XLSX_UPLOAD,
|
|
13
18
|
API_CONCEPT_LINKS_UPLOAD,
|
|
14
19
|
} from "../api";
|
|
15
20
|
|
|
@@ -58,8 +63,8 @@ export const useConceptLinksDownload = () => {
|
|
|
58
63
|
};
|
|
59
64
|
|
|
60
65
|
export const useConceptsUpload = () => {
|
|
61
|
-
return useSWRMutations(
|
|
62
|
-
apiJsonPost(url, arg),
|
|
66
|
+
return useSWRMutations(API_BUSINESS_CONCEPTS_XLSX_UPLOAD, (url, { arg }) =>
|
|
67
|
+
apiJsonPost(url, arg, UPLOAD_JSON_OPTS),
|
|
63
68
|
);
|
|
64
69
|
};
|
|
65
70
|
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
saveFile,
|
|
7
7
|
} from "../downloadConcepts";
|
|
8
8
|
import { downloadConcepts } from "../../routines";
|
|
9
|
-
import {
|
|
9
|
+
import { API_BUSINESS_CONCEPTS_XLSX_DOWNLOAD } from "../../api";
|
|
10
10
|
|
|
11
11
|
describe("sagas: downloadConceptsRequestSaga", () => {
|
|
12
12
|
it("should invoke downloadConceptsSaga on downloadConcepts.TRIGGER", () => {
|
|
@@ -52,7 +52,7 @@ describe("sagas: downloadConceptsSaga", () => {
|
|
|
52
52
|
.next()
|
|
53
53
|
.put(downloadConcepts.request(body))
|
|
54
54
|
.next()
|
|
55
|
-
.call(apiJsonPost,
|
|
55
|
+
.call(apiJsonPost, API_BUSINESS_CONCEPTS_XLSX_DOWNLOAD, body, {
|
|
56
56
|
...JSON_OPTS,
|
|
57
57
|
responseType: "blob",
|
|
58
58
|
})
|
|
@@ -76,7 +76,7 @@ describe("sagas: downloadConceptsSaga", () => {
|
|
|
76
76
|
.next()
|
|
77
77
|
.put(downloadConcepts.request(body))
|
|
78
78
|
.next()
|
|
79
|
-
.call(apiJsonPost,
|
|
79
|
+
.call(apiJsonPost, API_BUSINESS_CONCEPTS_XLSX_DOWNLOAD, body, {
|
|
80
80
|
...JSON_OPTS,
|
|
81
81
|
responseType: "blob",
|
|
82
82
|
})
|
|
@@ -4,7 +4,7 @@ import { call, put, takeLatest } from "redux-saga/effects";
|
|
|
4
4
|
import { apiJsonPost, JSON_OPTS } from "@truedat/core/services/api";
|
|
5
5
|
import { CONCEPT_VERSION } from "@truedat/core/routes";
|
|
6
6
|
import { downloadConcepts } from "../routines";
|
|
7
|
-
import {
|
|
7
|
+
import { API_BUSINESS_CONCEPTS_XLSX_DOWNLOAD } from "../api";
|
|
8
8
|
|
|
9
9
|
export function saveFile({ data, headers }) {
|
|
10
10
|
const contentDisposition = headers["content-disposition"];
|
|
@@ -25,7 +25,7 @@ export function* downloadConceptsSaga({ payload }) {
|
|
|
25
25
|
lang,
|
|
26
26
|
...searchParams,
|
|
27
27
|
};
|
|
28
|
-
const url =
|
|
28
|
+
const url = API_BUSINESS_CONCEPTS_XLSX_DOWNLOAD;
|
|
29
29
|
yield put(downloadConcepts.request(body));
|
|
30
30
|
const { data, headers } = yield call(apiJsonPost, url, body, {
|
|
31
31
|
...JSON_OPTS,
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getConceptsUploadAlertMessage,
|
|
3
|
+
getUploadAlertMessage,
|
|
4
|
+
} from "../messages";
|
|
5
|
+
|
|
6
|
+
describe("getUploadAlertMessage", () => {
|
|
7
|
+
it("returns a success alert for status 200", () => {
|
|
8
|
+
expect(getUploadAlertMessage({ status: 200, data: {} })).toMatchObject({
|
|
9
|
+
error: false,
|
|
10
|
+
header: "concepts.upload.success.accepted.header",
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("returns a success alert for status 202", () => {
|
|
15
|
+
expect(getUploadAlertMessage({ status: 202, data: {} })).toMatchObject({
|
|
16
|
+
error: false,
|
|
17
|
+
header: "concepts.upload.success.accepted.header",
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("returns a failure alert for other statuses", () => {
|
|
22
|
+
expect(getUploadAlertMessage({ status: 422, data: {} })).toMatchObject({
|
|
23
|
+
error: true,
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe("getConceptsUploadAlertMessage", () => {
|
|
29
|
+
it("links to the created job detail on success with job_id", () => {
|
|
30
|
+
expect(
|
|
31
|
+
getConceptsUploadAlertMessage({
|
|
32
|
+
status: 200,
|
|
33
|
+
data: { job_id: 123 },
|
|
34
|
+
}),
|
|
35
|
+
).toMatchObject({
|
|
36
|
+
error: false,
|
|
37
|
+
anchor: {
|
|
38
|
+
reference: "/concepts/uploadJobs/123",
|
|
39
|
+
label: "uploadJob.concepts.header",
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("keeps the upload jobs list anchor when there is no job_id", () => {
|
|
45
|
+
expect(
|
|
46
|
+
getConceptsUploadAlertMessage({ status: 202, data: {} }),
|
|
47
|
+
).toMatchObject({
|
|
48
|
+
error: false,
|
|
49
|
+
anchor: {
|
|
50
|
+
reference: "/concepts/uploadJobs",
|
|
51
|
+
label: "sidemenu.concepts_upload_events",
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("returns a failure alert untouched when the upload fails", () => {
|
|
57
|
+
expect(
|
|
58
|
+
getConceptsUploadAlertMessage({ status: 422, data: {} }),
|
|
59
|
+
).toMatchObject({
|
|
60
|
+
error: true,
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
export { defaultConceptColumns, getConceptColumns } from "./getConceptColumns";
|
|
2
2
|
export { getConceptDomainPath } from "./getConceptDomainPath";
|
|
3
|
-
export {
|
|
4
|
-
getConceptUploadEventColumns,
|
|
5
|
-
defaultConceptsUploadEventColumns,
|
|
6
|
-
} from "./getConceptUploadEventColumns";
|
|
7
3
|
export { getParsedEvents } from "./getParsedEvents";
|
|
8
4
|
export { getTemplateFields } from "./getTemplateFields";
|
|
9
5
|
export { getTemplateGroups } from "./getTemplateGroups";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import _ from "lodash/fp";
|
|
2
|
-
import {
|
|
2
|
+
import { CONCEPTS_UPLOAD_JOBS, linkTo } from "@truedat/core/routes";
|
|
3
3
|
|
|
4
4
|
export const getUploadAlertMessage = ({ status, data }) => {
|
|
5
|
-
if (status === 202) {
|
|
5
|
+
if (status === 200 || status === 202) {
|
|
6
6
|
return {
|
|
7
7
|
error: false,
|
|
8
8
|
header: "concepts.upload.success.accepted.header",
|
|
@@ -10,7 +10,7 @@ export const getUploadAlertMessage = ({ status, data }) => {
|
|
|
10
10
|
color: "blue",
|
|
11
11
|
text: "",
|
|
12
12
|
anchor: {
|
|
13
|
-
reference:
|
|
13
|
+
reference: CONCEPTS_UPLOAD_JOBS,
|
|
14
14
|
label: "sidemenu.concepts_upload_events",
|
|
15
15
|
},
|
|
16
16
|
};
|
|
@@ -44,3 +44,20 @@ export const getUploadAlertMessage = ({ status, data }) => {
|
|
|
44
44
|
return null;
|
|
45
45
|
}
|
|
46
46
|
};
|
|
47
|
+
|
|
48
|
+
export const getConceptsUploadAlertMessage = ({ status, data }) => {
|
|
49
|
+
const alertMessage = getUploadAlertMessage({ status, data });
|
|
50
|
+
const jobId = _.prop("job_id")(data);
|
|
51
|
+
|
|
52
|
+
if (alertMessage && !alertMessage.error && jobId != null) {
|
|
53
|
+
return {
|
|
54
|
+
...alertMessage,
|
|
55
|
+
anchor: {
|
|
56
|
+
reference: linkTo.CONCEPTS_UPLOAD_JOB({ id: jobId }),
|
|
57
|
+
label: "uploadJob.concepts.header",
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return alertMessage;
|
|
63
|
+
};
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { Header, Icon, Segment } from "semantic-ui-react";
|
|
2
|
-
import { FormattedMessage, useIntl } from "react-intl";
|
|
3
|
-
import ConceptsUploadEventsTable from "./ConceptsUploadEventsTable";
|
|
4
|
-
|
|
5
|
-
export const ConceptsUploadEvents = () => {
|
|
6
|
-
const { formatMessage } = useIntl();
|
|
7
|
-
return (
|
|
8
|
-
<Segment>
|
|
9
|
-
<Header as="h2">
|
|
10
|
-
<Icon
|
|
11
|
-
circular
|
|
12
|
-
name={formatMessage({
|
|
13
|
-
id: "conceptsUpload.header.icon",
|
|
14
|
-
defaultMessage: "cogs",
|
|
15
|
-
})}
|
|
16
|
-
/>
|
|
17
|
-
<Header.Content>
|
|
18
|
-
<FormattedMessage id="concepts.upload.header" />
|
|
19
|
-
<Header.Subheader>
|
|
20
|
-
<FormattedMessage id="concepts.upload.subheader" />
|
|
21
|
-
</Header.Subheader>
|
|
22
|
-
</Header.Content>
|
|
23
|
-
</Header>
|
|
24
|
-
<Segment attached="bottom">
|
|
25
|
-
<ConceptsUploadEventsTable />
|
|
26
|
-
</Segment>
|
|
27
|
-
</Segment>
|
|
28
|
-
);
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
export default ConceptsUploadEvents;
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
import { useState } from "react";
|
|
2
|
-
import PropTypes from "prop-types";
|
|
3
|
-
import { connect } from "react-redux";
|
|
4
|
-
import _ from "lodash/fp";
|
|
5
|
-
import { FormattedMessage } from "react-intl";
|
|
6
|
-
import { Table } from "semantic-ui-react";
|
|
7
|
-
import { useIntl } from "react-intl";
|
|
8
|
-
import { columnDecorator } from "@truedat/core/services";
|
|
9
|
-
import { useUploadEvents } from "../hooks/useUploadEvents";
|
|
10
|
-
import { getConceptUploadEventColumns } from "../selectors/getConceptUploadEventColumns";
|
|
11
|
-
import "../styles/ConceptsUploadEventColumns.less";
|
|
12
|
-
|
|
13
|
-
const getColumnsWithData = (conceptsResults, columns) =>
|
|
14
|
-
_.filter((column) =>
|
|
15
|
-
_.any(_.flow(columnDecorator(column), _.negate(_.isEmpty)))(
|
|
16
|
-
conceptsResults,
|
|
17
|
-
),
|
|
18
|
-
)(columns);
|
|
19
|
-
|
|
20
|
-
const messagesImp = (data) => {
|
|
21
|
-
const errors = data?.errors ?? [];
|
|
22
|
-
return _.map((error) => {
|
|
23
|
-
if (!error || !error.body)
|
|
24
|
-
return {
|
|
25
|
-
id: "unknown_error",
|
|
26
|
-
context: {},
|
|
27
|
-
defaultMessage: "concepts.upload.failed.success.errors",
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
return {
|
|
31
|
-
id: error.body.message ?? "unknown_error",
|
|
32
|
-
context: { ...error.body.context } ?? {},
|
|
33
|
-
defaultMessage: "concepts.upload.failed.success.errors",
|
|
34
|
-
};
|
|
35
|
-
}, errors);
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const ConceptsUploadEventsTable = ({ getColumns }) => {
|
|
39
|
-
const { formatMessage } = useIntl();
|
|
40
|
-
const [eventExpandedIndex, setEventExpandedIndex] = useState(null);
|
|
41
|
-
const { data, loading } = useUploadEvents();
|
|
42
|
-
|
|
43
|
-
const events =
|
|
44
|
-
data && !loading
|
|
45
|
-
? _.reduce(
|
|
46
|
-
(acc, event) => {
|
|
47
|
-
const eventKey = `${event.task_reference}_${event.status}`;
|
|
48
|
-
|
|
49
|
-
const node = event.node ?? "";
|
|
50
|
-
const hasErrors =
|
|
51
|
-
event.response?.errors && !_.isEmpty(event.response.errors);
|
|
52
|
-
|
|
53
|
-
const messageId = _.startsWith("lm@", node)
|
|
54
|
-
? hasErrors
|
|
55
|
-
? "concepts.upload.success.header_with_errors_without_update"
|
|
56
|
-
: "concepts.upload.success.header"
|
|
57
|
-
: hasErrors
|
|
58
|
-
? "concepts.upload.success.header_with_errors"
|
|
59
|
-
: "concepts.upload.success.header";
|
|
60
|
-
|
|
61
|
-
const translatedHeader = event.response
|
|
62
|
-
? formatMessage(
|
|
63
|
-
{ id: messageId },
|
|
64
|
-
{
|
|
65
|
-
count_created: event.response.created?.length ?? 0,
|
|
66
|
-
count_updated: event.response.updated?.length ?? 0,
|
|
67
|
-
count_errors: event.response.errors?.length ?? 0,
|
|
68
|
-
},
|
|
69
|
-
)
|
|
70
|
-
: "";
|
|
71
|
-
|
|
72
|
-
const translatedErrors = _.map((error) =>
|
|
73
|
-
formatMessage(
|
|
74
|
-
{ id: error.id ?? "unknown_error" },
|
|
75
|
-
error.context ?? {},
|
|
76
|
-
),
|
|
77
|
-
)(messagesImp(event.response));
|
|
78
|
-
|
|
79
|
-
// eslint-disable-next-line fp/no-mutation
|
|
80
|
-
acc[eventKey] = {
|
|
81
|
-
...event,
|
|
82
|
-
response: {
|
|
83
|
-
...event.response,
|
|
84
|
-
translatedHeader,
|
|
85
|
-
translatedErrors,
|
|
86
|
-
},
|
|
87
|
-
expanded: false,
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
return acc;
|
|
91
|
-
},
|
|
92
|
-
{},
|
|
93
|
-
data.data,
|
|
94
|
-
)
|
|
95
|
-
: [];
|
|
96
|
-
|
|
97
|
-
const columns = getColumnsWithData(events, getColumns());
|
|
98
|
-
|
|
99
|
-
const toggleExpanded = (eventKey) =>
|
|
100
|
-
eventExpandedIndex === eventKey
|
|
101
|
-
? setEventExpandedIndex(null)
|
|
102
|
-
: setEventExpandedIndex(eventKey);
|
|
103
|
-
|
|
104
|
-
return (
|
|
105
|
-
<>
|
|
106
|
-
{!_.isEmpty(events) && (
|
|
107
|
-
<Table sortable selectable>
|
|
108
|
-
<Table.Header>
|
|
109
|
-
<Table.Row>
|
|
110
|
-
{columns.map((column, i) => (
|
|
111
|
-
<Table.HeaderCell
|
|
112
|
-
key={i}
|
|
113
|
-
width={column.width}
|
|
114
|
-
content={formatMessage({
|
|
115
|
-
id: `concepts.upload.props.${column.name}`,
|
|
116
|
-
defaultMessage: column.name,
|
|
117
|
-
})}
|
|
118
|
-
/>
|
|
119
|
-
))}
|
|
120
|
-
</Table.Row>
|
|
121
|
-
</Table.Header>
|
|
122
|
-
<Table.Body>
|
|
123
|
-
{_.map(
|
|
124
|
-
(eventKey) => (
|
|
125
|
-
<Table.Row
|
|
126
|
-
className={
|
|
127
|
-
eventKey === eventExpandedIndex ? "expanded" : "contracted"
|
|
128
|
-
}
|
|
129
|
-
key={eventKey}
|
|
130
|
-
onClick={() => toggleExpanded(eventKey)}
|
|
131
|
-
>
|
|
132
|
-
{eventKey !== eventExpandedIndex ? (
|
|
133
|
-
columns &&
|
|
134
|
-
columns.map((column, key) => (
|
|
135
|
-
<Table.Cell
|
|
136
|
-
key={key}
|
|
137
|
-
textAlign={column.textAlign}
|
|
138
|
-
content={columnDecorator(column)({
|
|
139
|
-
...events[eventKey],
|
|
140
|
-
formatMessage,
|
|
141
|
-
})}
|
|
142
|
-
/>
|
|
143
|
-
))
|
|
144
|
-
) : (
|
|
145
|
-
<Table.Cell colSpan={columns.length}>
|
|
146
|
-
{events[eventKey].file_hash ? (
|
|
147
|
-
<>
|
|
148
|
-
<FormattedMessage
|
|
149
|
-
id={`concepts.upload.props.file_hash`}
|
|
150
|
-
/>
|
|
151
|
-
: {events[eventKey].file_hash}
|
|
152
|
-
</>
|
|
153
|
-
) : null}
|
|
154
|
-
{columns.map((column, key) => (
|
|
155
|
-
<div key={key}>
|
|
156
|
-
{columnDecorator(column)({
|
|
157
|
-
...events[eventKey],
|
|
158
|
-
formatMessage,
|
|
159
|
-
})}
|
|
160
|
-
</div>
|
|
161
|
-
))}
|
|
162
|
-
</Table.Cell>
|
|
163
|
-
)}
|
|
164
|
-
</Table.Row>
|
|
165
|
-
),
|
|
166
|
-
Object.keys(events),
|
|
167
|
-
)}
|
|
168
|
-
</Table.Body>
|
|
169
|
-
</Table>
|
|
170
|
-
)}
|
|
171
|
-
</>
|
|
172
|
-
);
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
ConceptsUploadEventsTable.propTypes = {
|
|
176
|
-
getColumns: PropTypes.func,
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
const mapStateToProps = (state) => ({
|
|
180
|
-
getColumns: () => getConceptUploadEventColumns(state),
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
export default connect(mapStateToProps)(ConceptsUploadEventsTable);
|