@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 +3 -3
- package/src/components/Implementations.js +33 -5
- package/src/components/ImplementationsHeader.js +56 -13
- package/src/components/ImplementationsRoutes.js +20 -12
- package/src/components/RuleImplementationsDownloadXlsx.js +2 -4
- package/src/components/__tests__/__snapshots__/ImplementationSearchResults.spec.js.snap +6 -2
- package/src/components/__tests__/__snapshots__/Implementations.spec.js.snap +3 -1
- package/src/components/__tests__/__snapshots__/RuleImplementationsActions.spec.js.snap +3 -1
- package/src/components/__tests__/__snapshots__/RuleImplementationsOptions.spec.js.snap +3 -1
- package/src/components/ImplementationUploadJobBreadcrumbs.js +0 -25
- package/src/components/ImplementationsUploadJob.js +0 -217
- package/src/components/ImplementationsUploadJobs.js +0 -128
- package/src/components/__tests__/ImplementationUploadJobBreadcrumbs.spec.js +0 -28
- package/src/components/__tests__/ImplementationsUploadJob.spec.js +0 -112
- package/src/components/__tests__/ImplementationsUploadJobs.spec.js +0 -60
- package/src/components/__tests__/__snapshots__/ImplementationUploadJobBreadcrumbs.spec.js.snap +0 -42
- package/src/components/__tests__/implementationsUploadJobParser.spec.js +0 -105
- package/src/components/implementationsUploadJobParser.js +0 -292
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/dq",
|
|
3
|
-
"version": "8.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.
|
|
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": "
|
|
89
|
+
"gitHead": "49d0d43e081d0f631f221d8f5992e390098b3630"
|
|
90
90
|
}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
import {
|
|
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={
|
|
57
|
+
<ImplementationFiltersLoader defaultFilters={filtersWithSubscope} />
|
|
30
58
|
<Segment>
|
|
31
59
|
<ImplementationsHeader />
|
|
32
60
|
<Segment attached="bottom">
|
|
33
|
-
<ImplementationSearchResults 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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
|
70
|
+
<FormattedMessage
|
|
71
|
+
id={header}
|
|
72
|
+
defaultMessage={header}
|
|
73
|
+
/>
|
|
34
74
|
<Header.Subheader>
|
|
35
|
-
<FormattedMessage
|
|
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
|
|
55
|
-
import
|
|
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={<
|
|
114
|
-
<Route path=":id" element={<
|
|
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
|
-
|
|
285
|
-
|
|
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
|
-
|
|
312
|
-
|
|
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
|
-
|
|
339
|
-
|
|
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
|
-
|
|
362
|
-
|
|
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>
|
|
@@ -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
|
-
}
|