@truedat/dq 8.1.1 → 8.1.4

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/dq",
3
- "version": "8.1.1",
3
+ "version": "8.1.4",
4
4
  "description": "Truedat Web Data Quality Module",
5
5
  "sideEffects": false,
6
6
  "module": "src/index.js",
@@ -53,7 +53,7 @@
53
53
  "@testing-library/jest-dom": "^6.6.3",
54
54
  "@testing-library/react": "^16.3.0",
55
55
  "@testing-library/user-event": "^14.6.1",
56
- "@truedat/test": "8.1.1",
56
+ "@truedat/test": "8.1.4",
57
57
  "identity-obj-proxy": "^3.0.0",
58
58
  "jest": "^29.7.0",
59
59
  "redux-saga-test-plan": "^4.0.6"
@@ -86,5 +86,5 @@
86
86
  "semantic-ui-react": "^3.0.0-beta.2",
87
87
  "swr": "^2.3.3"
88
88
  },
89
- "gitHead": "26ff4767ae2a60605d4958208ca73d91cfe7f81a"
89
+ "gitHead": "49d0d43e081d0f631f221d8f5992e390098b3630"
90
90
  }
@@ -1,7 +1,14 @@
1
- import { lazy } from "react";
1
+ import { useActiveRoute } from "@truedat/core/hooks";
2
+ import { useParams } from "react-router";
3
+ import { useSelector } from "react-redux";
2
4
  import PropTypes from "prop-types";
3
5
  import { Segment } from "semantic-ui-react";
4
6
  import { SearchContextProvider } from "@truedat/core/search/SearchContext";
7
+ import { getRiSubscopes } from "@truedat/core/selectors";
8
+ import {
9
+ IMPLEMENTATIONS,
10
+ IMPLEMENTATIONS_BY_SUBSCOPE
11
+ } from "@truedat/core/routes";
5
12
  import {
6
13
  useRuleImplementationFilters,
7
14
  useRuleImplementationSearch,
@@ -12,6 +19,27 @@ import ImplementationFiltersLoader from "./ImplementationFiltersLoader";
12
19
  import ImplementationSearchResults from "./ImplementationSearchResults";
13
20
 
14
21
  export const Implementations = ({ defaultFilters }) => {
22
+ const { subscope } = useParams();
23
+ const isImplementations=useActiveRoute(IMPLEMENTATIONS)
24
+ const isImplementationsBySubscope=useActiveRoute(IMPLEMENTATIONS_BY_SUBSCOPE)
25
+ const riSubscopes = useSelector(getRiSubscopes);
26
+
27
+ let filtersWithSubscope = { ...defaultFilters };
28
+
29
+ if (isImplementations && riSubscopes && riSubscopes.length > 0){
30
+ const mustNotFilters = {};
31
+ mustNotFilters[`mustnot.template.subscope`] = riSubscopes;
32
+
33
+ filtersWithSubscope = {
34
+ ...defaultFilters,
35
+ ...mustNotFilters
36
+ };
37
+ }
38
+ else if (isImplementationsBySubscope){
39
+ filtersWithSubscope = { ...defaultFilters, "template.subscope": [subscope] };
40
+ }
41
+
42
+
15
43
  const searchProps = {
16
44
  initialSortColumn: "implementation_key.raw",
17
45
  initialSortDirection: "ascending",
@@ -20,17 +48,17 @@ export const Implementations = ({ defaultFilters }) => {
20
48
  pageSize: 20,
21
49
  userFiltersType: "user_search_filters",
22
50
  userFilterScope: "rule_implementation",
23
- defaultFilters,
51
+ defaultFilters: filtersWithSubscope,
24
52
  };
25
53
 
26
54
  return (
27
55
  <>
28
56
  <SearchContextProvider {...searchProps}>
29
- <ImplementationFiltersLoader defaultFilters={defaultFilters} />
57
+ <ImplementationFiltersLoader defaultFilters={filtersWithSubscope} />
30
58
  <Segment>
31
59
  <ImplementationsHeader />
32
60
  <Segment attached="bottom">
33
- <ImplementationSearchResults defaultFilters={defaultFilters} />
61
+ <ImplementationSearchResults defaultFilters={filtersWithSubscope} />
34
62
  </Segment>
35
63
  </Segment>
36
64
  </SearchContextProvider>
@@ -42,4 +70,4 @@ Implementations.propTypes = {
42
70
  defaultFilters: PropTypes.object,
43
71
  };
44
72
 
45
- export default Implementations;
73
+ export default Implementations;
@@ -1,25 +1,62 @@
1
1
  import { Header, Icon } from "semantic-ui-react";
2
2
  import { FormattedMessage, useIntl } from "react-intl";
3
3
  import { useActiveRoute } from "@truedat/core/hooks";
4
+ import { useLocation } from "react-router";
4
5
  import {
5
6
  IMPLEMENTATIONS_PENDING,
6
7
  IMPLEMENTATIONS_DEPRECATED,
8
+ IMPLEMENTATIONS_BY_SUBSCOPE,
7
9
  } from "@truedat/core/routes";
10
+ import { useMemo } from "react";
8
11
 
9
12
  export const ImplementationSearchResultsHeader = () => {
10
13
  const { formatMessage } = useIntl();
14
+ const location = useLocation();
11
15
  const pending = useActiveRoute(IMPLEMENTATIONS_PENDING);
12
16
  const deprecated = useActiveRoute(IMPLEMENTATIONS_DEPRECATED);
13
- const header = pending
14
- ? "implementations.header.manage"
15
- : deprecated
16
- ? "implementations.header.deprecated"
17
- : "implementations.header";
18
- const subheader = pending
19
- ? "implementations.subheader.manage"
20
- : deprecated
21
- ? "implementations.subheader.deprecated"
22
- : "implementations.subheader";
17
+
18
+ const { header, subheader } = useMemo(() => {
19
+ // Check if current URL is a subscope URL
20
+ const pathParts = location.pathname.split('/');
21
+ const isSubscopeUrl = pathParts.includes('subscope') && pathParts.length > 2;
22
+
23
+ if (isSubscopeUrl) {
24
+ const subscopeIndex = pathParts.indexOf('subscope');
25
+ const subscopeName = pathParts[subscopeIndex + 1];
26
+
27
+ if (subscopeName) {
28
+ const decodedName = decodeURIComponent(subscopeName);
29
+ const normalizedSubscopeName = decodedName
30
+ .replace(/\s+/g, '_')
31
+ .replace(/[^\w_]/g, '');
32
+
33
+ return {
34
+ header: `implementations.${normalizedSubscopeName}.main.header`,
35
+ subheader: `implementations.${normalizedSubscopeName}.main.subheader`
36
+ };
37
+ }
38
+ }
39
+
40
+ if (pending) {
41
+ return {
42
+ header: "implementations.header.manage",
43
+ subheader: "implementations.subheader.manage"
44
+ };
45
+ }
46
+
47
+ if (deprecated) {
48
+ return {
49
+ header: "implementations.header.deprecated",
50
+ subheader: "implementations.subheader.deprecated"
51
+ };
52
+ }
53
+
54
+ return {
55
+ header: "implementations.header",
56
+ subheader: "implementations.subheader"
57
+ };
58
+ }, [location.pathname, pending, deprecated]);
59
+
23
60
  return (
24
61
  <Header as="h2">
25
62
  <Icon
@@ -30,13 +67,19 @@ export const ImplementationSearchResultsHeader = () => {
30
67
  })}
31
68
  />
32
69
  <Header.Content>
33
- <FormattedMessage id={header} />
70
+ <FormattedMessage
71
+ id={header}
72
+ defaultMessage={header}
73
+ />
34
74
  <Header.Subheader>
35
- <FormattedMessage id={subheader} />
75
+ <FormattedMessage
76
+ id={subheader}
77
+ defaultMessage={subheader}
78
+ />
36
79
  </Header.Subheader>
37
80
  </Header.Content>
38
81
  </Header>
39
82
  );
40
83
  };
41
84
 
42
- export default ImplementationSearchResultsHeader;
85
+ export default ImplementationSearchResultsHeader;
@@ -26,6 +26,7 @@ import {
26
26
  IMPLEMENTATION_STRUCTURES,
27
27
  IMPLEMENTATION,
28
28
  IMPLEMENTATIONS,
29
+ IMPLEMENTATIONS_BY_SUBSCOPE,
29
30
  IMPLEMENTATIONS_DEPRECATED,
30
31
  IMPLEMENTATIONS_PENDING,
31
32
  } from "@truedat/core/routes";
@@ -51,8 +52,8 @@ import RuleResultDetails from "./RuleResultDetails";
51
52
  import RuleResultRemediationLoader from "./RuleResultRemediationLoader";
52
53
  import RuleResultsRoutes from "./RuleResultsRoutes";
53
54
  import RuleSubscriptionLoader from "./RuleSubscriptionLoader";
54
- import ImplementationsUploadJobs from "./ImplementationsUploadJobs";
55
- import ImplementationsUploadJob from "./ImplementationsUploadJob";
55
+ import UploadJobs from "@truedat/core/components/UploadJobs";
56
+ import UploadJob from "@truedat/core/components/UploadJob";
56
57
 
57
58
  const TemplatesLoader = React.lazy(
58
59
  () => import("@truedat/core/components/TemplatesLoader")
@@ -110,8 +111,8 @@ export const ImplementationsRoutes = ({
110
111
  // IMPLEMENTATIONS_UPLOAD_JOBS = "/implementations/uploadJobs";
111
112
  path="/uploadJobs"
112
113
  >
113
- <Route index element={<ImplementationsUploadJobs />} />
114
- <Route path=":id" element={<ImplementationsUploadJob />} />
114
+ <Route index element={<UploadJobs scope="implementations" />} />
115
+ <Route path=":id" element={<UploadJob scope="implementations" />} />
115
116
  </Route>
116
117
 
117
118
  <Route
@@ -125,6 +126,13 @@ export const ImplementationsRoutes = ({
125
126
  />
126
127
  }
127
128
  />
129
+ <Route
130
+ // IMPLEMENTATIONS_BY_SUBSCOPE = "/implementations/subscope/:subscope";
131
+ path="/subscope/:subscope"
132
+ element={
133
+ <Implementations defaultFilters={{ status: ["published"] }} />
134
+ }
135
+ />
128
136
  <Route
129
137
  // IMPLEMENTATIONS_DEPRECATED = "/implementations/deprecated";
130
138
  path="deprecated"
@@ -281,8 +289,8 @@ export const ImplementationsRoutes = ({
281
289
  />
282
290
  ) : null}
283
291
  {!structuresAliasesLoading &&
284
- ruleImplementationLoaded &&
285
- implementationStructuresLoaded ? (
292
+ ruleImplementationLoaded &&
293
+ implementationStructuresLoaded ? (
286
294
  isRuleImplemenationBasic ? (
287
295
  <NewBasicRuleImplementation edition clone />
288
296
  ) : (
@@ -308,8 +316,8 @@ export const ImplementationsRoutes = ({
308
316
  />
309
317
  ) : null}
310
318
  {!structuresAliasesLoading &&
311
- ruleImplementationLoaded &&
312
- implementationStructuresLoaded ? (
319
+ ruleImplementationLoaded &&
320
+ implementationStructuresLoaded ? (
313
321
  isRuleImplemenationBasic ? (
314
322
  <NewBasicRuleImplementation edition />
315
323
  ) : (
@@ -335,8 +343,8 @@ export const ImplementationsRoutes = ({
335
343
  />
336
344
  ) : null}
337
345
  {!structuresAliasesLoading &&
338
- ruleImplementationLoaded &&
339
- implementationStructuresLoaded ? (
346
+ ruleImplementationLoaded &&
347
+ implementationStructuresLoaded ? (
340
348
  <NewRuleImplementation edition implementationType="default" />
341
349
  ) : null}
342
350
  </>
@@ -358,8 +366,8 @@ export const ImplementationsRoutes = ({
358
366
  />
359
367
  ) : null}
360
368
  {!structuresAliasesLoading &&
361
- ruleImplementationLoaded &&
362
- implementationStructuresLoaded ? (
369
+ ruleImplementationLoaded &&
370
+ implementationStructuresLoaded ? (
363
371
  <NewRuleImplementation edition implementationType="raw" />
364
372
  ) : null}
365
373
  </>
@@ -25,14 +25,12 @@ export const RuleImplementationsDownloadXlsx = () => {
25
25
  <Dropdown.Item
26
26
  icon="download"
27
27
  content={
28
- <>
29
- <span>
28
+ <span>
30
29
  {formatMessage({
31
30
  id: "implementations.actions.downloadXlsx.tooltip",
32
31
  })}
33
32
  </span>
34
- </>
35
- }
33
+ }
36
34
  onClick={() =>
37
35
  triggerDownload({
38
36
  ...searchParams,
@@ -54,7 +54,9 @@ exports[`<ImplementationSearchResults /> matches the latest snapshot 1`] = `
54
54
  aria-hidden="true"
55
55
  class="download icon"
56
56
  />
57
- <span>
57
+ <span
58
+ class="text"
59
+ >
58
60
  implementations.actions.downloadXlsx.tooltip
59
61
  </span>
60
62
  </div>
@@ -205,7 +207,9 @@ exports[`<ImplementationSearchResults /> renders executions on when executionEna
205
207
  aria-hidden="true"
206
208
  class="download icon"
207
209
  />
208
- <span>
210
+ <span
211
+ class="text"
212
+ >
209
213
  implementations.actions.downloadXlsx.tooltip
210
214
  </span>
211
215
  </div>
@@ -56,7 +56,9 @@ exports[`<Implementations /> matches the latest snapshot 1`] = `
56
56
  aria-hidden="true"
57
57
  class="download icon"
58
58
  />
59
- <span>
59
+ <span
60
+ class="text"
61
+ >
60
62
  implementations.actions.downloadXlsx.tooltip
61
63
  </span>
62
64
  </div>
@@ -66,7 +66,9 @@ exports[`<RuleImplementationsActions /> matches the latest snapshot 1`] = `
66
66
  aria-hidden="true"
67
67
  class="download icon"
68
68
  />
69
- <span>
69
+ <span
70
+ class="text"
71
+ >
70
72
  implementations.actions.downloadXlsx.tooltip
71
73
  </span>
72
74
  </div>
@@ -25,7 +25,9 @@ exports[`<RuleImplementationsOptions /> matches the latest snapshot 1`] = `
25
25
  aria-hidden="true"
26
26
  class="download icon"
27
27
  />
28
- <span>
28
+ <span
29
+ class="text"
30
+ >
29
31
  implementations.actions.downloadXlsx.tooltip
30
32
  </span>
31
33
  </div>
@@ -1,25 +0,0 @@
1
- import PropTypes from "prop-types";
2
- import { Breadcrumb } from "semantic-ui-react";
3
- import { Link } from "react-router";
4
- import { FormattedMessage } from "react-intl";
5
- import { IMPLEMENTATIONS_UPLOAD_JOBS } from "@truedat/core/routes";
6
-
7
- export const ImplementationUploadJobBreadcrumbs = ({ filename }) => (
8
- <Breadcrumb>
9
- <Breadcrumb.Section as={Link} to={IMPLEMENTATIONS_UPLOAD_JOBS}>
10
- <FormattedMessage id="sidemenu.implementations_upload_jobs" />
11
- </Breadcrumb.Section>
12
- {filename ? (
13
- <>
14
- <Breadcrumb.Divider icon="right angle" />
15
- <Breadcrumb.Section active>{filename}</Breadcrumb.Section>
16
- </>
17
- ) : null}
18
- </Breadcrumb>
19
- );
20
-
21
- ImplementationUploadJobBreadcrumbs.propTypes = {
22
- filename: PropTypes.string,
23
- };
24
-
25
- export default ImplementationUploadJobBreadcrumbs;
@@ -1,217 +0,0 @@
1
- import _ from "lodash/fp";
2
- import { useState } from "react";
3
- import {
4
- Header,
5
- Icon,
6
- Segment,
7
- Dimmer,
8
- Loader,
9
- Table,
10
- Label,
11
- Button,
12
- } from "semantic-ui-react";
13
- import { FormattedMessage, useIntl } from "react-intl";
14
- import { useParams } from "react-router";
15
- import { DateTime } from "@truedat/core/components";
16
- import { useImplementationsUploadJob } from "../hooks/useImplementations";
17
- import { StatusPill, ResponseCell } from "./implementationsUploadJobParser";
18
- import ImplementationUploadJobBreadcrumbs from "./ImplementationUploadJobBreadcrumbs";
19
-
20
- export default function ImplementationsUploadJob() {
21
- const { id } = useParams();
22
- const { data, loading } = useImplementationsUploadJob(id);
23
- const job = data?.data;
24
-
25
- const { formatMessage } = useIntl();
26
- const [expandedGroups, setExpandedGroups] = useState({});
27
-
28
- // Group events by status
29
- const groupedEvents = job?.events ? _.groupBy("status", job.events) : {};
30
-
31
- const toggleGroup = (status) => {
32
- setExpandedGroups((prev) => ({
33
- ...prev,
34
- [status]: !prev[status],
35
- }));
36
- };
37
-
38
- const renderEventGroup = (status, events) => {
39
- const isExpanded = expandedGroups[status];
40
- const shouldCollapse = status === "INFO" || status === "ERROR";
41
-
42
- if (!shouldCollapse) {
43
- // Render non-collapsible events normally
44
- return events.map((event, idx) => (
45
- <Table.Row key={`${status}-${idx}`}>
46
- <Table.Cell>
47
- <StatusPill status={event.status} />
48
- </Table.Cell>
49
- <Table.Cell>
50
- <DateTime value={event.inserted_at} />
51
- </Table.Cell>
52
- <Table.Cell>
53
- <ResponseCell {...event} />
54
- </Table.Cell>
55
- </Table.Row>
56
- ));
57
- }
58
-
59
- // Render collapsible groups for INFO and ERROR
60
- const groupRows = [];
61
-
62
- // Add group header row
63
- groupRows.push(
64
- <Table.Row
65
- key={`${status}-header`}
66
- style={{ backgroundColor: "#f8f9fa" }}
67
- >
68
- <Table.Cell>
69
- <Button
70
- basic
71
- size="mini"
72
- icon={isExpanded ? "chevron down" : "chevron right"}
73
- content={` ${formatMessage({ id: `implementations.bulkUpload.event.status.${status}` })} (${events.length})`}
74
- onClick={() => toggleGroup(status)}
75
- style={{ padding: "0.5em" }}
76
- />
77
- </Table.Cell>
78
- <Table.Cell>
79
- <DateTime value={events[0]?.inserted_at} />
80
- </Table.Cell>
81
- <Table.Cell></Table.Cell>
82
- </Table.Row>
83
- );
84
-
85
- // Add expanded events if group is expanded
86
- if (isExpanded) {
87
- events.forEach((event, idx) => {
88
- groupRows.push(
89
- <Table.Row key={`${status}-${idx}`} style={{ paddingLeft: "2em" }}>
90
- <Table.Cell style={{ paddingLeft: "2em" }}>
91
- <StatusPill status={event.status} />
92
- </Table.Cell>
93
- <Table.Cell>
94
- <DateTime value={event.inserted_at} />
95
- </Table.Cell>
96
- <Table.Cell>
97
- <ResponseCell {...event} />
98
- </Table.Cell>
99
- </Table.Row>
100
- );
101
- });
102
- }
103
-
104
- return groupRows;
105
- };
106
-
107
- return (
108
- <>
109
- <ImplementationUploadJobBreadcrumbs filename={job?.filename} />
110
- <Segment>
111
- <Header as="h2">
112
- <Icon
113
- circular
114
- name={formatMessage({
115
- id: "implementations.bulkUpload.job.header.icon",
116
- defaultMessage: "cogs",
117
- })}
118
- />
119
- <Header.Content>
120
- <FormattedMessage id="implementations.bulkUpload.job.header" />
121
- </Header.Content>
122
- </Header>
123
- <Dimmer.Dimmable dimmed={loading}>
124
- <Segment attached="bottom">
125
- {job ? (
126
- <>
127
- <div
128
- style={{
129
- display: "flex",
130
- alignItems: "center",
131
- gap: "1.5em",
132
- marginBottom: "1.5em",
133
- flexWrap: "wrap",
134
- }}
135
- >
136
- <div style={{ display: "flex", flexDirection: "column" }}>
137
- <span style={{ fontWeight: 600, fontSize: "1.3em" }}>
138
- {job.filename}
139
- </span>
140
- <span style={{ color: "gray", fontSize: "0.7em" }}>
141
- {job.hash}
142
- </span>
143
- </div>
144
- <span>
145
- <Label basic size="small" style={{ marginRight: 4 }}>
146
- <FormattedMessage
147
- id="implementations.bulkUpload.job.table.status"
148
- defaultMessage="Status"
149
- />
150
- </Label>
151
- <StatusPill status={job.latest_status} />
152
- </span>
153
- <span>
154
- <Label basic size="small" style={{ marginRight: 4 }}>
155
- <FormattedMessage
156
- id="implementations.bulkUpload.job.table.inserted_at"
157
- defaultMessage="Inserted At"
158
- />
159
- </Label>
160
- <DateTime value={job.latest_event_at} />
161
- </span>
162
- </div>
163
- <Table>
164
- <Table.Header>
165
- <Table.Row>
166
- <Table.HeaderCell>
167
- <FormattedMessage
168
- id="implementations.bulkUpload.job.table.status"
169
- defaultMessage="Status"
170
- />
171
- </Table.HeaderCell>
172
- <Table.HeaderCell>
173
- <FormattedMessage
174
- id="implementations.bulkUpload.job.table.inserted_at"
175
- defaultMessage="Inserted At"
176
- />
177
- </Table.HeaderCell>
178
- <Table.HeaderCell>
179
- <FormattedMessage
180
- id="implementations.bulkUpload.job.table.detail"
181
- defaultMessage="Detail"
182
- />
183
- </Table.HeaderCell>
184
- </Table.Row>
185
- </Table.Header>
186
- <Table.Body>
187
- {_.isArray(job.events) && !_.isEmpty(job.events) ? (
188
- Object.entries(groupedEvents)
189
- .map(([status, events]) =>
190
- renderEventGroup(status, events)
191
- )
192
- .flat()
193
- ) : (
194
- <Table.Row>
195
- <Table.Cell colSpan={3}>
196
- <FormattedMessage
197
- id="implementations.bulkUpload.job.table.no_events"
198
- defaultMessage="No events found."
199
- />
200
- </Table.Cell>
201
- </Table.Row>
202
- )}
203
- </Table.Body>
204
- </Table>
205
- </>
206
- ) : null}
207
- {loading ? (
208
- <Dimmer active inverted>
209
- <Loader />
210
- </Dimmer>
211
- ) : null}
212
- </Segment>
213
- </Dimmer.Dimmable>
214
- </Segment>
215
- </>
216
- );
217
- }