@truedat/bg 8.13.4 → 8.13.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/bg",
3
- "version": "8.13.4",
3
+ "version": "8.13.5",
4
4
  "description": "Truedat Web Business Glossary",
5
5
  "sideEffects": [
6
6
  "**/*.css",
@@ -57,7 +57,7 @@
57
57
  "@testing-library/jest-dom": "^6.6.3",
58
58
  "@testing-library/react": "^16.3.0",
59
59
  "@testing-library/user-event": "^14.6.1",
60
- "@truedat/test": "8.13.4",
60
+ "@truedat/test": "8.13.5",
61
61
  "identity-obj-proxy": "^3.0.0",
62
62
  "jest": "^29.7.0",
63
63
  "redux-saga-test-plan": "^4.0.6"
@@ -90,5 +90,5 @@
90
90
  "semantic-ui-react": "^3.0.0-beta.2",
91
91
  "swr": "^2.3.3"
92
92
  },
93
- "gitHead": "4ec142f1c856aa669b232b12250c800a19ab9ace"
93
+ "gitHead": "9b87cbb7143f20b305b84077c1fcf004e5b27726"
94
94
  }
@@ -3,12 +3,11 @@ const API_BUSINESS_CONCEPT_VERSION =
3
3
  const API_BUSINESS_CONCEPT_VERSIONS_ACTIONS =
4
4
  "/api/business_concept_versions/actions";
5
5
  const API_BUSINESS_CONCEPT_VERSIONS_CSV = "/api/business_concept_versions/csv";
6
- const API_BUSINESS_CONCEPT_VERSIONS_XLSX =
7
- "/api/business_concept_versions/xlsx";
6
+ const API_BUSINESS_CONCEPTS_XLSX_DOWNLOAD =
7
+ "/api/business_concepts/xlsx/download";
8
8
  const API_BUSINESS_CONCEPT_VERSIONS_SEARCH =
9
9
  "/api/business_concept_versions/search";
10
- const API_BUSINESS_CONCEPT_VERSIONS_UPLOAD =
11
- "/api/business_concept_versions/upload";
10
+ const API_BUSINESS_CONCEPTS_XLSX_UPLOAD = "/api/business_concepts/xlsx/upload";
12
11
  const API_CONCEPT_ARCHIVE = "/api/business_concepts/:id/versions";
13
12
  const API_CONCEPT_LINKS_DOWNLOAD =
14
13
  "/api/business_concept_versions/links/download";
@@ -17,8 +16,6 @@ const API_CONCEPT_FILTERS = "/api/business_concept_filters/search";
17
16
  const API_CONCEPT_SHARED_DOMAINS = "/api/business_concepts/:id/shared_domains";
18
17
  const API_BUSINESS_CONCEPT_BULK_UPDATE =
19
18
  "/api/business_concept_versions/bulk_update";
20
- const API_BUSINESS_CONCEPT_BULK_UPLOAD =
21
- "/api/business_concepts/bulk_upload_event";
22
19
  const API_BUSINESS_CONCEPT_SET_CONFIDENTIAL =
23
20
  "/api/business_concept_versions/:id/set_confidential";
24
21
  const API_CONCEPT_SUGGESTIONS = "/api/business_concept_versions/suggestions";
@@ -28,15 +25,14 @@ export {
28
25
  API_BUSINESS_CONCEPT_VERSION,
29
26
  API_BUSINESS_CONCEPT_VERSIONS_ACTIONS,
30
27
  API_BUSINESS_CONCEPT_VERSIONS_CSV,
31
- API_BUSINESS_CONCEPT_VERSIONS_XLSX,
28
+ API_BUSINESS_CONCEPTS_XLSX_DOWNLOAD,
32
29
  API_BUSINESS_CONCEPT_VERSIONS_SEARCH,
33
- API_BUSINESS_CONCEPT_VERSIONS_UPLOAD,
30
+ API_BUSINESS_CONCEPTS_XLSX_UPLOAD,
34
31
  API_CONCEPT_ARCHIVE,
35
32
  API_CONCEPT_LINKS_DOWNLOAD,
36
33
  API_CONCEPT_LINKS_UPLOAD,
37
34
  API_CONCEPT_FILTERS,
38
35
  API_CONCEPT_SHARED_DOMAINS,
39
36
  API_BUSINESS_CONCEPT_BULK_UPDATE,
40
- API_BUSINESS_CONCEPT_BULK_UPLOAD,
41
37
  API_BUSINESS_CONCEPT_SET_CONFIDENTIAL,
42
38
  };
@@ -10,7 +10,8 @@ import {
10
10
  CONCEPT_LINKS_MANAGEMENT,
11
11
  CONCEPT_VERSION,
12
12
  CONCEPTS_BULK_UPDATE,
13
- CONCEPTS_BULK_UPLOAD_EVENTS,
13
+ CONCEPTS_UPLOAD_JOBS,
14
+ CONCEPTS_UPLOAD_JOB,
14
15
  CONCEPTS_NEW,
15
16
  CONCEPTS_PENDING,
16
17
  CONCEPTS_DEPRECATED,
@@ -23,6 +24,8 @@ import {
23
24
  import { useIntl } from "react-intl";
24
25
  import { getSidemenuGlossarySubscopes } from "@truedat/core/selectors/getSidemenuGlossarySubscopes";
25
26
  import { SearchContextProvider } from "@truedat/core/search/SearchContext";
27
+ import UploadJobs from "@truedat/core/components/UploadJobs";
28
+ import UploadJob from "@truedat/core/components/UploadJob";
26
29
  import { useLinksSearch, useLinksFilters } from "../relations/hooks/useLinks";
27
30
  import ConceptLinksApprovals from "../relations/components/ConceptLinksApprovals";
28
31
  import ConceptLinksApprovalResults from "../relations/components/ConceptLinksApprovalResults";
@@ -36,7 +39,6 @@ import Concepts from "./Concepts";
36
39
  import ConceptsBulkUpdate from "./ConceptsBulkUpdate";
37
40
 
38
41
  import ConceptSubscriptionLoader from "./ConceptSubscriptionLoader";
39
- import ConceptsUploadEvents from "./ConceptsUploadEvents";
40
42
 
41
43
  const RelationTagsLoader = React.lazy(
42
44
  () => import("@truedat/lm/components/RelationTagsLoader"),
@@ -165,16 +167,12 @@ const ConceptRoutes = ({
165
167
  </>
166
168
  }
167
169
  />
168
- <Route
169
- // CONCEPTS_BULK_UPLOAD_EVENTS = "/concepts/management/bulk_upload_event";
170
- path="management"
171
- >
172
- <Route
173
- // CONCEPTS_BULK_UPLOAD_EVENTS = "/concepts/management/bulk_upload_event";
174
- path="bulk_upload_event"
175
- element={<ConceptsUploadEvents />}
176
- />
170
+ <Route path="uploadJobs">
171
+ <Route index element={<UploadJobs scope="concepts" />} />
172
+ <Route path=":id" element={<UploadJob scope="concepts" />} />
173
+ </Route>
177
174
 
175
+ <Route path="management">
178
176
  <Route
179
177
  path="approvals/results"
180
178
  element={<ConceptLinksApprovalResults />}
@@ -4,7 +4,7 @@ import { FormattedMessage, useIntl } from "react-intl";
4
4
  import UploadModal from "@truedat/core/components/UploadModal";
5
5
  import { useWebContext } from "@truedat/core/webContext";
6
6
  import { useConceptsUpload } from "../hooks/useConcepts";
7
- import { getUploadAlertMessage } from "../selectors/messages";
7
+ import { getConceptsUploadAlertMessage } from "../selectors/messages";
8
8
 
9
9
  export const ConceptsUploadButton = ({ canAutoPublish }) => {
10
10
  const { formatMessage, locale } = useIntl();
@@ -21,13 +21,16 @@ export const ConceptsUploadButton = ({ canAutoPublish }) => {
21
21
  data.append("lang", locale);
22
22
  triggerUpload(data)
23
23
  .then(({ status, data }) => {
24
- const alertMessage = getUploadAlertMessage({ status, data });
24
+ const alertMessage = getConceptsUploadAlertMessage({ status, data });
25
25
  setAlertMessage(alertMessage);
26
26
  })
27
27
  .catch((error) => {
28
28
  if (error.response) {
29
29
  const { status, data } = error.response;
30
- const alertMessage = getUploadAlertMessage({ status, data });
30
+ const alertMessage = getConceptsUploadAlertMessage({
31
+ status,
32
+ data,
33
+ });
31
34
  setAlertMessage(alertMessage);
32
35
  } else {
33
36
  setAlertMessage({ error: true, content: error.message });
@@ -60,13 +63,19 @@ export const ConceptsUploadButton = ({ canAutoPublish }) => {
60
63
  handleSubmit={(data) => {
61
64
  triggerUpload(data)
62
65
  .then(({ status, data }) => {
63
- const alertMessage = getUploadAlertMessage({ status, data });
66
+ const alertMessage = getConceptsUploadAlertMessage({
67
+ status,
68
+ data,
69
+ });
64
70
  setAlertMessage(alertMessage);
65
71
  })
66
72
  .catch((error) => {
67
73
  if (error.response) {
68
74
  const { status, data } = error.response;
69
- const alertMessage = getUploadAlertMessage({ status, data });
75
+ const alertMessage = getConceptsUploadAlertMessage({
76
+ status,
77
+ data,
78
+ });
70
79
  setAlertMessage(alertMessage);
71
80
  } else {
72
81
  setAlertMessage(error.message);
@@ -7,7 +7,7 @@ import {
7
7
  CONCEPTS_DEPRECATED,
8
8
  CONCEPTS_NEW,
9
9
  CONCEPTS_BULK_UPDATE,
10
- CONCEPTS_BULK_UPLOAD_EVENTS,
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("../ConceptsUploadEvents", () => () => (
39
- <div>ConceptsUploadEvents</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 bulk upload events route", async () => {
112
+ it("renders correctly with upload jobs route", async () => {
110
113
  const rendered = render(<ConceptRoutes />, {
111
- routes: [CONCEPTS_BULK_UPLOAD_EVENTS],
114
+ routes: [CONCEPTS_UPLOAD_JOBS],
112
115
  });
113
116
  await waitForLoad(rendered);
114
117
  expect(rendered.container).toMatchSnapshot();
@@ -14,14 +14,6 @@ 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>
@@ -139,6 +131,14 @@ exports[`<ConceptRoutes /> renders correctly with subscope route 1`] = `
139
131
  </div>
140
132
  `;
141
133
 
134
+ exports[`<ConceptRoutes /> renders correctly with upload jobs route 1`] = `
135
+ <div>
136
+ <div>
137
+ UploadJobs
138
+ </div>
139
+ </div>
140
+ `;
141
+
142
142
  exports[`<ConceptRoutes /> renders unauthorized component when not authorized 1`] = `
143
143
  <div>
144
144
  <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 { apiJson, apiJsonPost, JSON_OPTS } from "@truedat/core/services/api";
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
- API_BUSINESS_CONCEPT_VERSIONS_UPLOAD,
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(API_BUSINESS_CONCEPT_VERSIONS_UPLOAD, (url, { arg }) =>
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 { API_BUSINESS_CONCEPT_VERSIONS_XLSX } from "../../api";
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, API_BUSINESS_CONCEPT_VERSIONS_XLSX, body, {
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, API_BUSINESS_CONCEPT_VERSIONS_XLSX, body, {
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 { API_BUSINESS_CONCEPT_VERSIONS_XLSX } from "../api";
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 = API_BUSINESS_CONCEPT_VERSIONS_XLSX;
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 { CONCEPTS_BULK_UPLOAD_EVENTS } from "@truedat/core/routes";
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: CONCEPTS_BULK_UPLOAD_EVENTS,
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);
@@ -1,100 +0,0 @@
1
- import userEvent from "@testing-library/user-event";
2
- import { render, waitForLoad } from "@truedat/test/render";
3
- import ConceptsUploadEventsTable from "../ConceptsUploadEventsTable";
4
-
5
- const events = [
6
- {
7
- file_hash: "99E400B91D164F8F0A39F400CA323C8F",
8
- inserted_at: "2022-04-23T15:53:24.638484Z",
9
- message: null,
10
- response: {
11
- errors: [],
12
- created: [4825521, 4825561, 4825593],
13
- updated: [4825522, 4825562, 4825594],
14
- },
15
- status: "COMPLETED",
16
- task_reference: "0.3225053307.1609564164.100549",
17
- user_id: 467,
18
- },
19
- {
20
- file_hash: "47D90FDF1AD967BD7DBBDAE28664278E",
21
- inserted_at: "2022-04-23T15:14:48.770275Z",
22
- message: null,
23
- response: {
24
- errors: [
25
- {
26
- error_type: "test_field_error",
27
- body: {
28
- message: "concepts.upload.failed.invalid_field_value",
29
- context: {
30
- field: "gdpr",
31
- type: "grpd_template",
32
- row: 4,
33
- error: "Missing value",
34
- },
35
- },
36
- },
37
- {
38
- error_type: "test_forbidden_update",
39
- body: {
40
- message: "concepts.upload.failed.forbidden_update",
41
- context: {
42
- type: "test_template",
43
- row: 2,
44
- domain: "forbidden_domain",
45
- },
46
- },
47
- },
48
- {
49
- error_type: "test_name_not_available",
50
- body: {
51
- message: "concepts.upload.failed.name_not_available",
52
- context: {
53
- type: "test_template",
54
- row: 7,
55
- name: "test_name_not_available",
56
- },
57
- },
58
- },
59
- ],
60
- created: [],
61
- updated: [],
62
- },
63
- status: "COMPLETED",
64
- task_reference: "0.3225053307.1609564162.101463",
65
- user_id: 467,
66
- },
67
- ];
68
-
69
- jest.mock("../../hooks/useUploadEvents.js", () => {
70
- const originalModule = jest.requireActual("../../hooks/useUploadEvents.js");
71
-
72
- return {
73
- __esModule: true,
74
- ...originalModule,
75
- useUploadEvents: jest.fn(() => ({
76
- data: {
77
- data: events,
78
- },
79
- loading: false,
80
- })),
81
- };
82
- });
83
-
84
- describe("<ConceptsUploadEventsTable />", () => {
85
- it("matches the latest snapshot", async () => {
86
- const rendered = render(<ConceptsUploadEventsTable />);
87
- await waitForLoad(rendered);
88
- expect(rendered.container).toMatchSnapshot();
89
- });
90
-
91
- it("toggles row horizontal view when row is clicked", async () => {
92
- const user = userEvent.setup({ delay: null });
93
- const rendered = render(<ConceptsUploadEventsTable />);
94
- await waitForLoad(rendered);
95
- const dataRow = rendered.getAllByRole("row")[1];
96
- expect(dataRow).toHaveClass("contracted");
97
- await user.click(dataRow);
98
- expect(dataRow).toHaveClass("expanded");
99
- });
100
- });
@@ -1,115 +0,0 @@
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
- inserted_at
28
- </th>
29
- </tr>
30
- </thead>
31
- <tbody
32
- class=""
33
- >
34
- <tr
35
- class="contracted"
36
- >
37
- <td
38
- class=""
39
- >
40
- concepts.upload.status.completed
41
- </td>
42
- <td
43
- class=""
44
- >
45
- <p>
46
- concepts.upload.success.header
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
- concepts.upload.status.completed
69
- </td>
70
- <td
71
- class=""
72
- >
73
- <p>
74
- concepts.upload.success.header_with_errors
75
- </p>
76
- <ul
77
- class="bulk-upload-error"
78
- >
79
- <li>
80
- concepts.upload.failed.invalid_field_value
81
- </li>
82
- <li>
83
- concepts.upload.failed.forbidden_update
84
- </li>
85
- <li>
86
- concepts.upload.failed.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
- "concepts.upload.failed.invalid_field_value","test_field_error","concepts.upload.failed.invalid_field_value","gdpr","grpd_template","4","Missing value","",""
94
- "concepts.upload.failed.forbidden_update","test_forbidden_update","concepts.upload.failed.forbidden_update","","test_template","2","","forbidden_domain",""
95
- "concepts.upload.failed.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
- alert.export.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
- `;
@@ -1,11 +0,0 @@
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
- };
@@ -1,20 +0,0 @@
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
- });
@@ -1,66 +0,0 @@
1
- import _ from "lodash/fp";
2
- import { FormattedMessage } from "react-intl";
3
- import Moment from "react-moment";
4
- import { AlertExporter } from "@truedat/core/components/AlertExporter";
5
-
6
- const bulkUpdateErrors = (response) => {
7
- return _.isEmpty(response) ? (
8
- ""
9
- ) : (
10
- <>
11
- <p>{response.translatedHeader}</p>
12
- <ul className="bulk-upload-error">
13
- {_.map.convert({ cap: false })((translatedError, key) => {
14
- return <li key={`error_${key}`}>{translatedError}</li>;
15
- })(response.translatedErrors)}
16
- </ul>
17
- {_.isEmpty(response.errors) ? null : (
18
- <AlertExporter
19
- className="alert-exporter-in-table"
20
- onClick={(e) => e.stopPropagation()}
21
- {...{
22
- list: response.translatedErrors,
23
- messages: response.errors,
24
- }}
25
- />
26
- )}
27
- </>
28
- );
29
- };
30
-
31
- const dateDecorator = (date) =>
32
- date && <Moment locale="es" date={date} format="YYYY-MM-DD HH:mm" />;
33
-
34
- const statusDecorator = (status) => (
35
- <FormattedMessage id={`concepts.upload.status.${_.toLower(status)}`} />
36
- );
37
-
38
- export const defaultConceptsUploadEventColumns = [
39
- {
40
- name: "filename",
41
- width: 2,
42
- },
43
- {
44
- name: "status",
45
- fieldDecorator: statusDecorator,
46
- width: 2,
47
- },
48
- {
49
- name: "response",
50
- fieldDecorator: bulkUpdateErrors,
51
- width: 2,
52
- },
53
- {
54
- name: "message",
55
- width: 2,
56
- },
57
- {
58
- name: "inserted_at",
59
- fieldDecorator: dateDecorator,
60
- textAlign: "center",
61
- width: 2,
62
- },
63
- ];
64
-
65
- export const getConceptUploadEventColumns = (state) =>
66
- state.conceptsUploadEventColumns ?? defaultConceptsUploadEventColumns;
@@ -1,24 +0,0 @@
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
- }