@truedat/df 5.17.3 → 5.18.0
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 +4 -4
- package/src/templates/components/Template.js +64 -16
- package/src/templates/components/TemplateFilters.js +56 -0
- package/src/templates/components/TemplateRoutes.js +17 -11
- package/src/templates/components/Templates.js +45 -21
- package/src/templates/components/TemplatesContext.js +66 -0
- package/src/templates/components/TemplatesTable.js +113 -0
- package/src/templates/components/__tests__/TemplatesTable.spec.js +29 -0
- package/src/templates/components/__tests__/__snapshots__/Template.spec.js.snap +41 -16
- package/src/templates/components/__tests__/__snapshots__/TemplatesTable.spec.js.snap +3 -0
- package/src/templates/components/templateForm/FieldForm.js +2 -0
- package/src/templates/components/templateForm/TemplateForm.js +3 -8
- package/src/templates/components/templateForm/TemplateFormActions.js +21 -38
- package/src/templates/components/templateForm/__tests__/TemplateForm.spec.js +1 -1
- package/src/templates/components/templateForm/__tests__/TemplateFormActions.spec.js +0 -2
- package/src/templates/components/templateForm/__tests__/__snapshots__/TemplateForm.spec.js.snap +12 -12
- package/src/templates/components/templateForm/__tests__/__snapshots__/TemplateFormActions.spec.js.snap +34 -109
- package/src/templates/components/TemplateCards.js +0 -57
- package/src/templates/components/TemplateTabs.js +0 -40
- package/src/templates/components/__tests__/TemplateCards.spec.js +0 -34
- package/src/templates/components/__tests__/__snapshots__/TemplateCards.spec.js.snap +0 -62
- package/src/templates/components/scopes.js +0 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/df",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.18.0",
|
|
4
4
|
"description": "Truedat Web Data Quality Module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"jsnext:main": "src/index.js",
|
|
@@ -87,8 +87,8 @@
|
|
|
87
87
|
},
|
|
88
88
|
"dependencies": {
|
|
89
89
|
"@apollo/client": "^3.7.1",
|
|
90
|
-
"@truedat/auth": "5.
|
|
91
|
-
"@truedat/core": "5.
|
|
90
|
+
"@truedat/auth": "5.18.0",
|
|
91
|
+
"@truedat/core": "5.18.0",
|
|
92
92
|
"decode-uri-component": "^0.2.2",
|
|
93
93
|
"path-to-regexp": "^1.7.0",
|
|
94
94
|
"prop-types": "^15.8.1",
|
|
@@ -109,5 +109,5 @@
|
|
|
109
109
|
"react-dom": ">= 16.8.6 < 17",
|
|
110
110
|
"semantic-ui-react": ">= 2.0.3 < 2.2"
|
|
111
111
|
},
|
|
112
|
-
"gitHead": "
|
|
112
|
+
"gitHead": "2cd5914eafeae16400d4ccccfabc00cd2c84c80f"
|
|
113
113
|
}
|
|
@@ -1,30 +1,72 @@
|
|
|
1
1
|
import _ from "lodash/fp";
|
|
2
2
|
import React from "react";
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
|
-
import {
|
|
4
|
+
import { FormattedMessage } from "react-intl";
|
|
5
|
+
import {
|
|
6
|
+
Button,
|
|
7
|
+
Header,
|
|
8
|
+
Icon,
|
|
9
|
+
Segment,
|
|
10
|
+
Grid,
|
|
11
|
+
Container,
|
|
12
|
+
} from "semantic-ui-react";
|
|
5
13
|
import { connect } from "react-redux";
|
|
14
|
+
import { ConfirmModal } from "@truedat/core/components";
|
|
6
15
|
import { deleteTemplate, updateTemplate } from "../routines";
|
|
7
16
|
import TemplateCrumbs from "./TemplateCrumbs";
|
|
8
17
|
import TemplateForm from "./templateForm/TemplateForm";
|
|
9
18
|
|
|
10
|
-
export const Template = ({
|
|
19
|
+
export const Template = ({
|
|
20
|
+
template,
|
|
21
|
+
updateTemplate,
|
|
22
|
+
deleteTemplate,
|
|
23
|
+
templateDeleting,
|
|
24
|
+
templateSaving,
|
|
25
|
+
}) =>
|
|
11
26
|
_.isUndefined(template.id) ? null : (
|
|
12
27
|
<>
|
|
13
28
|
<TemplateCrumbs name={template?.label} />
|
|
14
29
|
<Segment>
|
|
15
|
-
<
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
30
|
+
<Grid>
|
|
31
|
+
<Grid.Column width={8}>
|
|
32
|
+
<Header as="h2">
|
|
33
|
+
<Icon name="file code outline" circular />
|
|
34
|
+
<Header.Content>
|
|
35
|
+
{template?.label}
|
|
36
|
+
<Header.Subheader>{template?.name}</Header.Subheader>
|
|
37
|
+
</Header.Content>
|
|
38
|
+
</Header>
|
|
39
|
+
</Grid.Column>
|
|
40
|
+
<Grid.Column width={8}>
|
|
41
|
+
<Container textAlign="right">
|
|
42
|
+
{template.id && (
|
|
43
|
+
<ConfirmModal
|
|
44
|
+
icon="trash"
|
|
45
|
+
trigger={
|
|
46
|
+
<Button
|
|
47
|
+
negative
|
|
48
|
+
icon="trash"
|
|
49
|
+
loading={templateDeleting}
|
|
50
|
+
disabled={templateSaving || templateDeleting}
|
|
51
|
+
/>
|
|
52
|
+
}
|
|
53
|
+
header={
|
|
54
|
+
<FormattedMessage id="template.actions.delete.confirmation.header" />
|
|
55
|
+
}
|
|
56
|
+
content={
|
|
57
|
+
<FormattedMessage
|
|
58
|
+
id="template.actions.delete.confirmation.content"
|
|
59
|
+
values={{ name: <i>{template.name}</i> }}
|
|
60
|
+
/>
|
|
61
|
+
}
|
|
62
|
+
onConfirm={() => deleteTemplate(template)}
|
|
63
|
+
/>
|
|
64
|
+
)}
|
|
65
|
+
</Container>
|
|
66
|
+
</Grid.Column>
|
|
67
|
+
</Grid>
|
|
68
|
+
|
|
69
|
+
<TemplateForm template={template} onSubmit={updateTemplate} editMode />
|
|
28
70
|
</Segment>
|
|
29
71
|
</>
|
|
30
72
|
);
|
|
@@ -33,9 +75,15 @@ Template.propTypes = {
|
|
|
33
75
|
deleteTemplate: PropTypes.func,
|
|
34
76
|
template: PropTypes.object,
|
|
35
77
|
updateTemplate: PropTypes.func,
|
|
78
|
+
templateDeleting: PropTypes.bool,
|
|
79
|
+
templateSaving: PropTypes.bool,
|
|
36
80
|
};
|
|
37
81
|
|
|
38
|
-
const mapStateToProps = ({ template }) => ({
|
|
82
|
+
const mapStateToProps = ({ template, templateDeleting, templateSaving }) => ({
|
|
83
|
+
template,
|
|
84
|
+
templateDeleting,
|
|
85
|
+
templateSaving,
|
|
86
|
+
});
|
|
39
87
|
|
|
40
88
|
export default connect(mapStateToProps, { updateTemplate, deleteTemplate })(
|
|
41
89
|
Template
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Input, Dropdown } from "semantic-ui-react";
|
|
4
|
+
import { useIntl, FormattedMessage } from "react-intl";
|
|
5
|
+
import { lowerDeburrTrim } from "@truedat/core/services/sort";
|
|
6
|
+
import { useTemplatesContext } from "./TemplatesContext";
|
|
7
|
+
|
|
8
|
+
export default function TemplateFilters() {
|
|
9
|
+
const { formatMessage } = useIntl();
|
|
10
|
+
const { query, setQuery, scope, scopes, setScope } = useTemplatesContext();
|
|
11
|
+
return (
|
|
12
|
+
<Input
|
|
13
|
+
value={query}
|
|
14
|
+
onChange={(_e, data) => setQuery(lowerDeburrTrim(data.value))}
|
|
15
|
+
icon={{ name: "search", link: true }}
|
|
16
|
+
iconPosition="left"
|
|
17
|
+
action={
|
|
18
|
+
<Dropdown
|
|
19
|
+
button
|
|
20
|
+
className="icon"
|
|
21
|
+
floating
|
|
22
|
+
icon="filter"
|
|
23
|
+
labeled
|
|
24
|
+
scrolling
|
|
25
|
+
text={formatMessage({
|
|
26
|
+
id: scope ? `template.scope.${scope}` : "templates.all_scopes",
|
|
27
|
+
})}
|
|
28
|
+
upward={false}
|
|
29
|
+
>
|
|
30
|
+
<Dropdown.Menu>
|
|
31
|
+
<Dropdown.Item onClick={() => setScope()}>
|
|
32
|
+
<em>
|
|
33
|
+
<FormattedMessage id="templates.all_scopes" />
|
|
34
|
+
</em>
|
|
35
|
+
</Dropdown.Item>
|
|
36
|
+
{_.flow(
|
|
37
|
+
_.defaultTo([]),
|
|
38
|
+
_.map((scope) => (
|
|
39
|
+
<Dropdown.Item
|
|
40
|
+
key={scope}
|
|
41
|
+
text={formatMessage({
|
|
42
|
+
id: `template.scope.${scope}`,
|
|
43
|
+
})}
|
|
44
|
+
onClick={() => setScope(scope)}
|
|
45
|
+
/>
|
|
46
|
+
))
|
|
47
|
+
)(scopes)}
|
|
48
|
+
</Dropdown.Menu>
|
|
49
|
+
</Dropdown>
|
|
50
|
+
}
|
|
51
|
+
placeholder={formatMessage({
|
|
52
|
+
id: "templates.search.placeholder",
|
|
53
|
+
})}
|
|
54
|
+
/>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -2,17 +2,13 @@ import React from "react";
|
|
|
2
2
|
import { Route, Switch } from "react-router-dom";
|
|
3
3
|
import { Unauthorized } from "@truedat/core/components";
|
|
4
4
|
import { useAuthorized } from "@truedat/core/hooks";
|
|
5
|
-
import {
|
|
6
|
-
TEMPLATE,
|
|
7
|
-
TEMPLATES,
|
|
8
|
-
TEMPLATES_NEW,
|
|
9
|
-
TEMPLATE_SCOPE,
|
|
10
|
-
} from "@truedat/core/routes";
|
|
5
|
+
import { TEMPLATE, TEMPLATES, TEMPLATES_NEW } from "@truedat/core/routes";
|
|
11
6
|
import NewTemplate from "./NewTemplate";
|
|
12
7
|
import Templates from "./Templates";
|
|
13
8
|
import Template from "./Template";
|
|
14
9
|
import TemplatesLoader from "./TemplatesLoader";
|
|
15
10
|
import TemplateLoader from "./TemplateLoader";
|
|
11
|
+
import TemplatesContextProvider from "./TemplatesContext";
|
|
16
12
|
|
|
17
13
|
export const TemplateRoutes = () => {
|
|
18
14
|
const authorized = useAuthorized();
|
|
@@ -21,11 +17,21 @@ export const TemplateRoutes = () => {
|
|
|
21
17
|
path={TEMPLATES}
|
|
22
18
|
render={() =>
|
|
23
19
|
authorized ? (
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
<TemplatesContextProvider
|
|
21
|
+
initialSortColumn="updated_at"
|
|
22
|
+
initialSortDirection="descending"
|
|
23
|
+
>
|
|
28
24
|
<Switch>
|
|
25
|
+
<Route
|
|
26
|
+
path={TEMPLATES}
|
|
27
|
+
render={() => (
|
|
28
|
+
<>
|
|
29
|
+
<TemplatesLoader />
|
|
30
|
+
<Templates />
|
|
31
|
+
</>
|
|
32
|
+
)}
|
|
33
|
+
exact
|
|
34
|
+
/>
|
|
29
35
|
<Route
|
|
30
36
|
path={TEMPLATES_NEW}
|
|
31
37
|
render={() => <NewTemplate />}
|
|
@@ -41,7 +47,7 @@ export const TemplateRoutes = () => {
|
|
|
41
47
|
)}
|
|
42
48
|
/>
|
|
43
49
|
</Switch>
|
|
44
|
-
|
|
50
|
+
</TemplatesContextProvider>
|
|
45
51
|
) : (
|
|
46
52
|
<Unauthorized />
|
|
47
53
|
)
|
|
@@ -1,25 +1,49 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { Header, Icon, Segment } from "semantic-ui-react";
|
|
3
2
|
import { FormattedMessage } from "react-intl";
|
|
4
|
-
import
|
|
5
|
-
|
|
3
|
+
import {
|
|
4
|
+
Header,
|
|
5
|
+
Icon,
|
|
6
|
+
Segment,
|
|
7
|
+
Grid,
|
|
8
|
+
Container,
|
|
9
|
+
Button,
|
|
10
|
+
} from "semantic-ui-react";
|
|
11
|
+
import { Link } from "react-router-dom";
|
|
12
|
+
import { TEMPLATES_NEW } from "@truedat/core/routes";
|
|
13
|
+
import TemplateFilters from "./TemplateFilters";
|
|
14
|
+
import TemplatesTable from "./TemplatesTable";
|
|
6
15
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
<
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
<
|
|
13
|
-
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
export default function Templates() {
|
|
17
|
+
return (
|
|
18
|
+
<Segment>
|
|
19
|
+
<Header as="h2">
|
|
20
|
+
<Icon name="file code outline" circular />
|
|
21
|
+
<Header.Content>
|
|
22
|
+
<FormattedMessage id="templates.header" />
|
|
23
|
+
<Header.Subheader>
|
|
24
|
+
<FormattedMessage id="templates.subheader" />
|
|
25
|
+
</Header.Subheader>
|
|
26
|
+
</Header.Content>
|
|
27
|
+
</Header>
|
|
28
|
+
|
|
29
|
+
<Grid>
|
|
30
|
+
<Grid.Column width={8}>
|
|
31
|
+
<TemplateFilters />
|
|
32
|
+
</Grid.Column>
|
|
24
33
|
|
|
25
|
-
|
|
34
|
+
<Grid.Column width={8}>
|
|
35
|
+
<Container textAlign="right">
|
|
36
|
+
<Button
|
|
37
|
+
primary
|
|
38
|
+
as={Link}
|
|
39
|
+
to={TEMPLATES_NEW}
|
|
40
|
+
content={<FormattedMessage id="templates.actions.create" />}
|
|
41
|
+
/>
|
|
42
|
+
</Container>
|
|
43
|
+
</Grid.Column>
|
|
44
|
+
</Grid>
|
|
45
|
+
|
|
46
|
+
<TemplatesTable />
|
|
47
|
+
</Segment>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React, { useState, useContext, createContext } from "react";
|
|
3
|
+
import { useSelector } from "react-redux";
|
|
4
|
+
import { matchSorter } from "match-sorter";
|
|
5
|
+
|
|
6
|
+
export const TemplatesContext = createContext();
|
|
7
|
+
export const useTemplatesContext = () => useContext(TemplatesContext);
|
|
8
|
+
|
|
9
|
+
const HIDDEN_SCOPES = ["cx", "ca"];
|
|
10
|
+
const isHiddenScope = (scope) => _.includes(scope)(HIDDEN_SCOPES);
|
|
11
|
+
|
|
12
|
+
export default function TemplatesContextProvider(props) {
|
|
13
|
+
const children = _.prop("children")(props);
|
|
14
|
+
const initialSortColumn = _.prop("initialSortColumn")(props);
|
|
15
|
+
const initialSortDirection = _.prop("initialSortDirection")(props);
|
|
16
|
+
|
|
17
|
+
const { templates, templatesLoading: loading } = useSelector(
|
|
18
|
+
_.pick(["templates", "templatesLoading"])
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const [query, setQuery] = useState("");
|
|
22
|
+
const [scope, setScope] = useState();
|
|
23
|
+
|
|
24
|
+
const [sortColumn, setSortColumn] = useState(initialSortColumn);
|
|
25
|
+
const [sortDirection, setSortDirection] = useState(initialSortDirection);
|
|
26
|
+
|
|
27
|
+
const parsedTemplates = _.flow(
|
|
28
|
+
_.reject(({ scope }) => isHiddenScope(scope)),
|
|
29
|
+
_.filter((template) => !scope || scope == template.scope),
|
|
30
|
+
(items) =>
|
|
31
|
+
matchSorter(items, query, {
|
|
32
|
+
keys: ["name", "label"],
|
|
33
|
+
threshold: matchSorter.rankings.CONTAINS,
|
|
34
|
+
}),
|
|
35
|
+
_.orderBy([sortColumn], [sortDirection == "ascending" ? "asc" : "desc"])
|
|
36
|
+
)(templates);
|
|
37
|
+
|
|
38
|
+
const scopes = _.flow(
|
|
39
|
+
_.map("scope"),
|
|
40
|
+
_.uniq,
|
|
41
|
+
_.reject(isHiddenScope)
|
|
42
|
+
)(templates);
|
|
43
|
+
|
|
44
|
+
const context = {
|
|
45
|
+
loading,
|
|
46
|
+
templates: parsedTemplates,
|
|
47
|
+
|
|
48
|
+
query,
|
|
49
|
+
setQuery,
|
|
50
|
+
|
|
51
|
+
scope,
|
|
52
|
+
setScope,
|
|
53
|
+
scopes,
|
|
54
|
+
|
|
55
|
+
sortColumn,
|
|
56
|
+
sortDirection,
|
|
57
|
+
setSortColumn,
|
|
58
|
+
setSortDirection,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<TemplatesContext.Provider value={context}>
|
|
63
|
+
{children}
|
|
64
|
+
</TemplatesContext.Provider>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { FormattedMessage } from "react-intl";
|
|
4
|
+
import { Link } from "react-router-dom";
|
|
5
|
+
import { Table, Header, Icon } from "semantic-ui-react";
|
|
6
|
+
import { linkTo } from "@truedat/core/routes";
|
|
7
|
+
import { sortColumn as sortHandler } from "@truedat/core/services/sort";
|
|
8
|
+
import { columnDecorator } from "@truedat/core/services";
|
|
9
|
+
import {
|
|
10
|
+
TranslateDecorator,
|
|
11
|
+
DateDecorator,
|
|
12
|
+
} from "@truedat/core/services/columnDecorators";
|
|
13
|
+
|
|
14
|
+
import { useTemplatesContext } from "./TemplatesContext";
|
|
15
|
+
|
|
16
|
+
export default function TemplatesTable() {
|
|
17
|
+
const {
|
|
18
|
+
templates,
|
|
19
|
+
sortColumn,
|
|
20
|
+
sortDirection,
|
|
21
|
+
setSortColumn,
|
|
22
|
+
setSortDirection,
|
|
23
|
+
} = useTemplatesContext();
|
|
24
|
+
|
|
25
|
+
const columns = [
|
|
26
|
+
{
|
|
27
|
+
name: "name",
|
|
28
|
+
sort: { name: "name" },
|
|
29
|
+
fieldSelector: _.pick(["id", "name"]),
|
|
30
|
+
fieldDecorator: ({ id, name }) => (
|
|
31
|
+
<Link to={linkTo.TEMPLATE({ templateId: id })}>{name}</Link>
|
|
32
|
+
),
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "label",
|
|
36
|
+
sort: { name: "label" },
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "scope",
|
|
40
|
+
sort: { name: "scope" },
|
|
41
|
+
fieldSelector: ({ scope }) => ({ id: `template.scope.${scope}` }),
|
|
42
|
+
fieldDecorator: TranslateDecorator,
|
|
43
|
+
width: 2,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: "updated_at",
|
|
47
|
+
sort: { name: "updated_at" },
|
|
48
|
+
fieldSelector: ({ updated_at }) => ({
|
|
49
|
+
date: updated_at,
|
|
50
|
+
}),
|
|
51
|
+
width: 2,
|
|
52
|
+
fieldDecorator: DateDecorator,
|
|
53
|
+
textAlign: "center",
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
return _.isEmpty(templates) ? (
|
|
58
|
+
<Header as="h4">
|
|
59
|
+
<Icon name="search" />
|
|
60
|
+
<Header.Content>
|
|
61
|
+
<FormattedMessage id="templates.search.results.empty" />
|
|
62
|
+
</Header.Content>
|
|
63
|
+
</Header>
|
|
64
|
+
) : (
|
|
65
|
+
<Table sortable>
|
|
66
|
+
<Table.Header>
|
|
67
|
+
<Table.Row>
|
|
68
|
+
{columns.map((column, key) => (
|
|
69
|
+
<Table.HeaderCell
|
|
70
|
+
key={key}
|
|
71
|
+
width={column.width}
|
|
72
|
+
content={
|
|
73
|
+
<FormattedMessage
|
|
74
|
+
id={`template.form.${column.header || column.name}`}
|
|
75
|
+
defaultMessage={column.name}
|
|
76
|
+
/>
|
|
77
|
+
}
|
|
78
|
+
sorted={
|
|
79
|
+
_.path("sort.name")(column) === sortColumn
|
|
80
|
+
? sortDirection
|
|
81
|
+
: null
|
|
82
|
+
}
|
|
83
|
+
className={_.path("sort.name")(column) ? "" : "disabled"}
|
|
84
|
+
onClick={() =>
|
|
85
|
+
sortHandler(
|
|
86
|
+
column,
|
|
87
|
+
() => {},
|
|
88
|
+
setSortDirection,
|
|
89
|
+
setSortColumn,
|
|
90
|
+
sortDirection,
|
|
91
|
+
sortColumn
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
/>
|
|
95
|
+
))}
|
|
96
|
+
</Table.Row>
|
|
97
|
+
</Table.Header>
|
|
98
|
+
<Table.Body>
|
|
99
|
+
{templates.map((t, i) => (
|
|
100
|
+
<Table.Row key={i}>
|
|
101
|
+
{columns.map((column, i) => (
|
|
102
|
+
<Table.Cell
|
|
103
|
+
key={i}
|
|
104
|
+
textAlign={column.textAlign}
|
|
105
|
+
content={columnDecorator(column)(t)}
|
|
106
|
+
/>
|
|
107
|
+
))}
|
|
108
|
+
</Table.Row>
|
|
109
|
+
))}
|
|
110
|
+
</Table.Body>
|
|
111
|
+
</Table>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { shallowWithIntl } from "@truedat/test/intl-stub";
|
|
3
|
+
import TemplatesTable from "../TemplatesTable";
|
|
4
|
+
import { TemplatesContext } from "../TemplatesContext";
|
|
5
|
+
|
|
6
|
+
describe("<TemplatesTable />", () => {
|
|
7
|
+
const templates = [
|
|
8
|
+
{
|
|
9
|
+
scope: "bg",
|
|
10
|
+
name: "testbg",
|
|
11
|
+
label: "testbg",
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
scope: "dq",
|
|
15
|
+
name: "testdq",
|
|
16
|
+
label: "testdq",
|
|
17
|
+
},
|
|
18
|
+
];
|
|
19
|
+
const context = { templates };
|
|
20
|
+
|
|
21
|
+
it("matches the latest snapshot", () => {
|
|
22
|
+
const wrapper = shallowWithIntl(
|
|
23
|
+
<TemplatesContext.Provider values={context}>
|
|
24
|
+
<TemplatesTable />
|
|
25
|
+
</TemplatesContext.Provider>
|
|
26
|
+
);
|
|
27
|
+
expect(wrapper).toMatchSnapshot();
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -24,24 +24,48 @@ exports[`<Template /> matches the latest snapshot 1`] = `
|
|
|
24
24
|
<div
|
|
25
25
|
class="ui segment"
|
|
26
26
|
>
|
|
27
|
-
<
|
|
28
|
-
class="ui
|
|
27
|
+
<div
|
|
28
|
+
class="ui grid"
|
|
29
29
|
>
|
|
30
|
-
<i
|
|
31
|
-
aria-hidden="true"
|
|
32
|
-
class="file code outline circular icon"
|
|
33
|
-
/>
|
|
34
30
|
<div
|
|
35
|
-
class="
|
|
31
|
+
class="eight wide column"
|
|
32
|
+
>
|
|
33
|
+
<h2
|
|
34
|
+
class="ui header"
|
|
35
|
+
>
|
|
36
|
+
<i
|
|
37
|
+
aria-hidden="true"
|
|
38
|
+
class="file code outline circular icon"
|
|
39
|
+
/>
|
|
40
|
+
<div
|
|
41
|
+
class="content"
|
|
42
|
+
>
|
|
43
|
+
My Template
|
|
44
|
+
<div
|
|
45
|
+
class="sub header"
|
|
46
|
+
>
|
|
47
|
+
template1
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
</h2>
|
|
51
|
+
</div>
|
|
52
|
+
<div
|
|
53
|
+
class="eight wide column"
|
|
36
54
|
>
|
|
37
|
-
My Template
|
|
38
55
|
<div
|
|
39
|
-
class="
|
|
56
|
+
class="ui right aligned container"
|
|
40
57
|
>
|
|
41
|
-
|
|
58
|
+
<button
|
|
59
|
+
class="ui icon negative button"
|
|
60
|
+
>
|
|
61
|
+
<i
|
|
62
|
+
aria-hidden="true"
|
|
63
|
+
class="trash icon"
|
|
64
|
+
/>
|
|
65
|
+
</button>
|
|
42
66
|
</div>
|
|
43
67
|
</div>
|
|
44
|
-
</
|
|
68
|
+
</div>
|
|
45
69
|
<form
|
|
46
70
|
class="ui form"
|
|
47
71
|
>
|
|
@@ -68,6 +92,7 @@ exports[`<Template /> matches the latest snapshot 1`] = `
|
|
|
68
92
|
>
|
|
69
93
|
<input
|
|
70
94
|
name="name"
|
|
95
|
+
readonly=""
|
|
71
96
|
required=""
|
|
72
97
|
type="text"
|
|
73
98
|
value="template1"
|
|
@@ -297,17 +322,17 @@ exports[`<Template /> matches the latest snapshot 1`] = `
|
|
|
297
322
|
class="ui divider"
|
|
298
323
|
/>
|
|
299
324
|
<div
|
|
300
|
-
class="
|
|
325
|
+
class="ui right aligned container"
|
|
301
326
|
>
|
|
302
327
|
<button
|
|
303
|
-
class="ui
|
|
328
|
+
class="ui primary button"
|
|
304
329
|
>
|
|
305
|
-
|
|
330
|
+
Save
|
|
306
331
|
</button>
|
|
307
332
|
<button
|
|
308
|
-
class="ui
|
|
333
|
+
class="ui button"
|
|
309
334
|
>
|
|
310
|
-
|
|
335
|
+
Cancel
|
|
311
336
|
</button>
|
|
312
337
|
</div>
|
|
313
338
|
</form>
|
|
@@ -28,7 +28,7 @@ const scopeOptions = (formatMessage) =>
|
|
|
28
28
|
"quality_control",
|
|
29
29
|
]);
|
|
30
30
|
|
|
31
|
-
export const TemplateForm = ({ loading, template, onSubmit
|
|
31
|
+
export const TemplateForm = ({ loading, template, onSubmit }) => {
|
|
32
32
|
const { formatMessage } = useIntl();
|
|
33
33
|
const [activeGroup, setActiveGroup] = useState(0);
|
|
34
34
|
const [editedTemplate, setEditedTemplate] = useState({
|
|
@@ -94,7 +94,6 @@ export const TemplateForm = ({ loading, template, onSubmit, onDelete }) => {
|
|
|
94
94
|
)(editedTemplate);
|
|
95
95
|
onSubmit({ template: { ...editedTemplate, content: formattedContent } });
|
|
96
96
|
};
|
|
97
|
-
const handleDelete = () => onDelete({ id: template.id });
|
|
98
97
|
|
|
99
98
|
const validatedContent = parseContentValidation(editedTemplate.content);
|
|
100
99
|
|
|
@@ -113,6 +112,7 @@ export const TemplateForm = ({ loading, template, onSubmit, onDelete }) => {
|
|
|
113
112
|
value={editedTemplate.name || ""}
|
|
114
113
|
required
|
|
115
114
|
onChange={handleChange}
|
|
115
|
+
readOnly={!_.isEmpty(template)}
|
|
116
116
|
/>
|
|
117
117
|
<Form.Input
|
|
118
118
|
name="label"
|
|
@@ -173,11 +173,7 @@ export const TemplateForm = ({ loading, template, onSubmit, onDelete }) => {
|
|
|
173
173
|
</Grid.Column>
|
|
174
174
|
</Grid>
|
|
175
175
|
<Divider />
|
|
176
|
-
<TemplateFormActions
|
|
177
|
-
template={template}
|
|
178
|
-
onSave={handleSubmit}
|
|
179
|
-
onDelete={handleDelete}
|
|
180
|
-
/>
|
|
176
|
+
<TemplateFormActions template={template} onSave={handleSubmit} />
|
|
181
177
|
</Form>
|
|
182
178
|
);
|
|
183
179
|
};
|
|
@@ -186,7 +182,6 @@ TemplateForm.propTypes = {
|
|
|
186
182
|
loading: PropTypes.bool.isRequired,
|
|
187
183
|
template: PropTypes.object.isRequired,
|
|
188
184
|
onSubmit: PropTypes.func.isRequired,
|
|
189
|
-
onDelete: PropTypes.func,
|
|
190
185
|
};
|
|
191
186
|
|
|
192
187
|
const mapStateToProps = ({ templateDeleting, templateSaving }) => ({
|
|
@@ -1,56 +1,39 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import PropTypes from "prop-types";
|
|
3
3
|
import { connect } from "react-redux";
|
|
4
|
-
import {
|
|
4
|
+
import { useHistory } from "react-router-dom";
|
|
5
|
+
import { Button, Container } from "semantic-ui-react";
|
|
6
|
+
import { TEMPLATES } from "@truedat/core/routes";
|
|
5
7
|
import { FormattedMessage } from "react-intl";
|
|
6
|
-
import { ConfirmModal } from "@truedat/core/components";
|
|
7
8
|
|
|
8
9
|
export const TemplateFormActions = ({
|
|
9
|
-
template: { name, id },
|
|
10
10
|
templateSaving,
|
|
11
11
|
templateDeleting,
|
|
12
|
-
onDelete,
|
|
13
12
|
onSave,
|
|
14
|
-
}) =>
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
/>
|
|
26
|
-
}
|
|
27
|
-
header={
|
|
28
|
-
<FormattedMessage id="template.actions.delete.confirmation.header" />
|
|
29
|
-
}
|
|
30
|
-
content={
|
|
31
|
-
<FormattedMessage
|
|
32
|
-
id="template.actions.delete.confirmation.content"
|
|
33
|
-
values={{ name: <i>{name}</i> }}
|
|
34
|
-
/>
|
|
35
|
-
}
|
|
36
|
-
onConfirm={onDelete}
|
|
13
|
+
}) => {
|
|
14
|
+
const history = useHistory();
|
|
15
|
+
const onCancel = () => history.push(TEMPLATES);
|
|
16
|
+
return (
|
|
17
|
+
<Container textAlign="right">
|
|
18
|
+
<Button
|
|
19
|
+
primary
|
|
20
|
+
content={<FormattedMessage id="actions.save" />}
|
|
21
|
+
onClick={onSave}
|
|
22
|
+
loading={templateSaving}
|
|
23
|
+
disabled={templateSaving || templateDeleting}
|
|
37
24
|
/>
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
/>
|
|
46
|
-
</div>
|
|
47
|
-
);
|
|
25
|
+
<Button
|
|
26
|
+
content={<FormattedMessage id="actions.cancel" />}
|
|
27
|
+
onClick={onCancel}
|
|
28
|
+
/>
|
|
29
|
+
</Container>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
48
32
|
|
|
49
33
|
TemplateFormActions.propTypes = {
|
|
50
34
|
template: PropTypes.object.isRequired,
|
|
51
35
|
templateDeleting: PropTypes.bool.isRequired,
|
|
52
36
|
templateSaving: PropTypes.bool.isRequired,
|
|
53
|
-
onDelete: PropTypes.func.isRequired,
|
|
54
37
|
onSave: PropTypes.func.isRequired,
|
|
55
38
|
};
|
|
56
39
|
|
|
@@ -10,7 +10,7 @@ const template = {
|
|
|
10
10
|
};
|
|
11
11
|
const messages = {
|
|
12
12
|
en: {
|
|
13
|
-
"actions.
|
|
13
|
+
"actions.cancel": "actions.cancel",
|
|
14
14
|
"actions.save": "actions.save",
|
|
15
15
|
"template.form.fieldGroups": "template.form.fieldGroups",
|
|
16
16
|
"template.form.header": "template.form.header",
|
|
@@ -4,7 +4,6 @@ import { TemplateFormActions } from "../TemplateFormActions";
|
|
|
4
4
|
|
|
5
5
|
describe("<TemplateFormActions />", () => {
|
|
6
6
|
const onSave = jest.fn();
|
|
7
|
-
const onDelete = jest.fn();
|
|
8
7
|
const template = {
|
|
9
8
|
id: 1,
|
|
10
9
|
name: "Name",
|
|
@@ -15,7 +14,6 @@ describe("<TemplateFormActions />", () => {
|
|
|
15
14
|
template,
|
|
16
15
|
templateSaving,
|
|
17
16
|
templateDeleting,
|
|
18
|
-
onDelete,
|
|
19
17
|
onSave,
|
|
20
18
|
};
|
|
21
19
|
|
package/src/templates/components/templateForm/__tests__/__snapshots__/TemplateForm.spec.js.snap
CHANGED
|
@@ -28,6 +28,7 @@ exports[`<TemplateForm /> matches the latest snapshot (loading) 1`] = `
|
|
|
28
28
|
>
|
|
29
29
|
<input
|
|
30
30
|
name="name"
|
|
31
|
+
readonly=""
|
|
31
32
|
required=""
|
|
32
33
|
type="text"
|
|
33
34
|
value="Name"
|
|
@@ -255,21 +256,19 @@ exports[`<TemplateForm /> matches the latest snapshot (loading) 1`] = `
|
|
|
255
256
|
class="ui divider"
|
|
256
257
|
/>
|
|
257
258
|
<div
|
|
258
|
-
class="
|
|
259
|
+
class="ui right aligned container"
|
|
259
260
|
>
|
|
260
261
|
<button
|
|
261
|
-
class="ui
|
|
262
|
+
class="ui loading primary disabled button"
|
|
262
263
|
disabled=""
|
|
263
264
|
tabindex="-1"
|
|
264
265
|
>
|
|
265
|
-
actions.
|
|
266
|
+
actions.save
|
|
266
267
|
</button>
|
|
267
268
|
<button
|
|
268
|
-
class="ui
|
|
269
|
-
disabled=""
|
|
270
|
-
tabindex="-1"
|
|
269
|
+
class="ui button"
|
|
271
270
|
>
|
|
272
|
-
actions.
|
|
271
|
+
actions.cancel
|
|
273
272
|
</button>
|
|
274
273
|
</div>
|
|
275
274
|
</form>
|
|
@@ -304,6 +303,7 @@ exports[`<TemplateForm /> matches the latest snapshot 1`] = `
|
|
|
304
303
|
>
|
|
305
304
|
<input
|
|
306
305
|
name="name"
|
|
306
|
+
readonly=""
|
|
307
307
|
required=""
|
|
308
308
|
type="text"
|
|
309
309
|
value="Name"
|
|
@@ -531,17 +531,17 @@ exports[`<TemplateForm /> matches the latest snapshot 1`] = `
|
|
|
531
531
|
class="ui divider"
|
|
532
532
|
/>
|
|
533
533
|
<div
|
|
534
|
-
class="
|
|
534
|
+
class="ui right aligned container"
|
|
535
535
|
>
|
|
536
536
|
<button
|
|
537
|
-
class="ui
|
|
537
|
+
class="ui primary button"
|
|
538
538
|
>
|
|
539
|
-
actions.
|
|
539
|
+
actions.save
|
|
540
540
|
</button>
|
|
541
541
|
<button
|
|
542
|
-
class="ui
|
|
542
|
+
class="ui button"
|
|
543
543
|
>
|
|
544
|
-
actions.
|
|
544
|
+
actions.cancel
|
|
545
545
|
</button>
|
|
546
546
|
</div>
|
|
547
547
|
</form>
|
|
@@ -1,43 +1,9 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
2
|
|
|
3
3
|
exports[`<TemplateFormActions /> matches the latest snapshot (deleting) 1`] = `
|
|
4
|
-
<
|
|
5
|
-
|
|
4
|
+
<Container
|
|
5
|
+
textAlign="right"
|
|
6
6
|
>
|
|
7
|
-
<ConfirmModal
|
|
8
|
-
content={
|
|
9
|
-
<Memo(MemoizedFormattedMessage)
|
|
10
|
-
id="template.actions.delete.confirmation.content"
|
|
11
|
-
values={
|
|
12
|
-
{
|
|
13
|
-
"name": <i>
|
|
14
|
-
Name
|
|
15
|
-
</i>,
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
/>
|
|
19
|
-
}
|
|
20
|
-
header={
|
|
21
|
-
<Memo(MemoizedFormattedMessage)
|
|
22
|
-
id="template.actions.delete.confirmation.header"
|
|
23
|
-
/>
|
|
24
|
-
}
|
|
25
|
-
icon="trash"
|
|
26
|
-
onConfirm={[MockFunction]}
|
|
27
|
-
trigger={
|
|
28
|
-
<Button
|
|
29
|
-
as="button"
|
|
30
|
-
content={
|
|
31
|
-
<Memo(MemoizedFormattedMessage)
|
|
32
|
-
id="actions.delete"
|
|
33
|
-
/>
|
|
34
|
-
}
|
|
35
|
-
disabled={true}
|
|
36
|
-
loading={true}
|
|
37
|
-
negative={true}
|
|
38
|
-
/>
|
|
39
|
-
}
|
|
40
|
-
/>
|
|
41
7
|
<Button
|
|
42
8
|
as="button"
|
|
43
9
|
content={
|
|
@@ -50,47 +16,22 @@ exports[`<TemplateFormActions /> matches the latest snapshot (deleting) 1`] = `
|
|
|
50
16
|
onClick={[MockFunction]}
|
|
51
17
|
primary={true}
|
|
52
18
|
/>
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
exports[`<TemplateFormActions /> matches the latest snapshot (saving) 1`] = `
|
|
57
|
-
<div
|
|
58
|
-
className="actions"
|
|
59
|
-
>
|
|
60
|
-
<ConfirmModal
|
|
19
|
+
<Button
|
|
20
|
+
as="button"
|
|
61
21
|
content={
|
|
62
22
|
<Memo(MemoizedFormattedMessage)
|
|
63
|
-
id="
|
|
64
|
-
values={
|
|
65
|
-
{
|
|
66
|
-
"name": <i>
|
|
67
|
-
Name
|
|
68
|
-
</i>,
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
/>
|
|
72
|
-
}
|
|
73
|
-
header={
|
|
74
|
-
<Memo(MemoizedFormattedMessage)
|
|
75
|
-
id="template.actions.delete.confirmation.header"
|
|
76
|
-
/>
|
|
77
|
-
}
|
|
78
|
-
icon="trash"
|
|
79
|
-
onConfirm={[MockFunction]}
|
|
80
|
-
trigger={
|
|
81
|
-
<Button
|
|
82
|
-
as="button"
|
|
83
|
-
content={
|
|
84
|
-
<Memo(MemoizedFormattedMessage)
|
|
85
|
-
id="actions.delete"
|
|
86
|
-
/>
|
|
87
|
-
}
|
|
88
|
-
disabled={true}
|
|
89
|
-
loading={false}
|
|
90
|
-
negative={true}
|
|
23
|
+
id="actions.cancel"
|
|
91
24
|
/>
|
|
92
25
|
}
|
|
26
|
+
onClick={[Function]}
|
|
93
27
|
/>
|
|
28
|
+
</Container>
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
exports[`<TemplateFormActions /> matches the latest snapshot (saving) 1`] = `
|
|
32
|
+
<Container
|
|
33
|
+
textAlign="right"
|
|
34
|
+
>
|
|
94
35
|
<Button
|
|
95
36
|
as="button"
|
|
96
37
|
content={
|
|
@@ -103,47 +44,22 @@ exports[`<TemplateFormActions /> matches the latest snapshot (saving) 1`] = `
|
|
|
103
44
|
onClick={[MockFunction]}
|
|
104
45
|
primary={true}
|
|
105
46
|
/>
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
exports[`<TemplateFormActions /> matches the latest snapshot 1`] = `
|
|
110
|
-
<div
|
|
111
|
-
className="actions"
|
|
112
|
-
>
|
|
113
|
-
<ConfirmModal
|
|
47
|
+
<Button
|
|
48
|
+
as="button"
|
|
114
49
|
content={
|
|
115
50
|
<Memo(MemoizedFormattedMessage)
|
|
116
|
-
id="
|
|
117
|
-
values={
|
|
118
|
-
{
|
|
119
|
-
"name": <i>
|
|
120
|
-
Name
|
|
121
|
-
</i>,
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
/>
|
|
125
|
-
}
|
|
126
|
-
header={
|
|
127
|
-
<Memo(MemoizedFormattedMessage)
|
|
128
|
-
id="template.actions.delete.confirmation.header"
|
|
129
|
-
/>
|
|
130
|
-
}
|
|
131
|
-
icon="trash"
|
|
132
|
-
onConfirm={[MockFunction]}
|
|
133
|
-
trigger={
|
|
134
|
-
<Button
|
|
135
|
-
as="button"
|
|
136
|
-
content={
|
|
137
|
-
<Memo(MemoizedFormattedMessage)
|
|
138
|
-
id="actions.delete"
|
|
139
|
-
/>
|
|
140
|
-
}
|
|
141
|
-
disabled={false}
|
|
142
|
-
loading={false}
|
|
143
|
-
negative={true}
|
|
51
|
+
id="actions.cancel"
|
|
144
52
|
/>
|
|
145
53
|
}
|
|
54
|
+
onClick={[Function]}
|
|
146
55
|
/>
|
|
56
|
+
</Container>
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
exports[`<TemplateFormActions /> matches the latest snapshot 1`] = `
|
|
60
|
+
<Container
|
|
61
|
+
textAlign="right"
|
|
62
|
+
>
|
|
147
63
|
<Button
|
|
148
64
|
as="button"
|
|
149
65
|
content={
|
|
@@ -156,5 +72,14 @@ exports[`<TemplateFormActions /> matches the latest snapshot 1`] = `
|
|
|
156
72
|
onClick={[MockFunction]}
|
|
157
73
|
primary={true}
|
|
158
74
|
/>
|
|
159
|
-
|
|
75
|
+
<Button
|
|
76
|
+
as="button"
|
|
77
|
+
content={
|
|
78
|
+
<Memo(MemoizedFormattedMessage)
|
|
79
|
+
id="actions.cancel"
|
|
80
|
+
/>
|
|
81
|
+
}
|
|
82
|
+
onClick={[Function]}
|
|
83
|
+
/>
|
|
84
|
+
</Container>
|
|
160
85
|
`;
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import _ from "lodash/fp";
|
|
2
|
-
import React from "react";
|
|
3
|
-
import PropTypes from "prop-types";
|
|
4
|
-
import queryString from "query-string";
|
|
5
|
-
import { FormattedMessage } from "react-intl";
|
|
6
|
-
import { connect } from "react-redux";
|
|
7
|
-
import { Link, useLocation } from "react-router-dom";
|
|
8
|
-
import { Icon, Card } from "semantic-ui-react";
|
|
9
|
-
import { TEMPLATES_NEW } from "@truedat/core/routes";
|
|
10
|
-
import TemplateCard from "./TemplateCard";
|
|
11
|
-
import { filterScopeWithTemplates, validScope } from "./scopes";
|
|
12
|
-
|
|
13
|
-
export const TemplateCards = ({ templates }) => {
|
|
14
|
-
const { search } = useLocation();
|
|
15
|
-
const { scope } = queryString.parse(search);
|
|
16
|
-
const defaultScope = _.flow(
|
|
17
|
-
_.map(_.prop("scope")),
|
|
18
|
-
_.filter(validScope),
|
|
19
|
-
_.head
|
|
20
|
-
)(templates);
|
|
21
|
-
|
|
22
|
-
const selectedScope =
|
|
23
|
-
filterScopeWithTemplates(scope, templates) || defaultScope;
|
|
24
|
-
const visibleTemplates = _.filter(_.propEq("scope", selectedScope))(
|
|
25
|
-
templates
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<Card.Group>
|
|
30
|
-
<Card link as={Link} to={TEMPLATES_NEW} className="create template">
|
|
31
|
-
<Card.Content textAlign="center">
|
|
32
|
-
<Card.Header>
|
|
33
|
-
<FormattedMessage id="templates.actions.create" />
|
|
34
|
-
</Card.Header>
|
|
35
|
-
<Card.Description>
|
|
36
|
-
<Icon.Group floated="left" size="big">
|
|
37
|
-
<Icon name="file outline" />
|
|
38
|
-
<Icon corner name="add" />
|
|
39
|
-
</Icon.Group>
|
|
40
|
-
</Card.Description>
|
|
41
|
-
</Card.Content>
|
|
42
|
-
</Card>
|
|
43
|
-
|
|
44
|
-
{visibleTemplates.map((template, i) => (
|
|
45
|
-
<TemplateCard key={i} template={template} />
|
|
46
|
-
))}
|
|
47
|
-
</Card.Group>
|
|
48
|
-
);
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
TemplateCards.propTypes = {
|
|
52
|
-
templates: PropTypes.array,
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const mapStateToProps = ({ templates }) => ({ templates });
|
|
56
|
-
|
|
57
|
-
export default connect(mapStateToProps)(TemplateCards);
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import _ from "lodash/fp";
|
|
2
|
-
import React from "react";
|
|
3
|
-
import PropTypes from "prop-types";
|
|
4
|
-
import { connect } from "react-redux";
|
|
5
|
-
import { Link, useLocation } from "react-router-dom";
|
|
6
|
-
import { Menu } from "semantic-ui-react";
|
|
7
|
-
import { FormattedMessage } from "react-intl";
|
|
8
|
-
import queryString from "query-string";
|
|
9
|
-
import { linkTo } from "@truedat/core/routes";
|
|
10
|
-
import { filterScope } from "./scopes";
|
|
11
|
-
|
|
12
|
-
const TemplateTabs = ({ scopes }) => {
|
|
13
|
-
const { search } = useLocation();
|
|
14
|
-
const { scope: selectedScope } = queryString.parse(search);
|
|
15
|
-
const activeTab = _.max([0, _.findIndex(s => s == selectedScope)(scopes)]);
|
|
16
|
-
return (
|
|
17
|
-
<Menu attached="top" secondary pointing tabular>
|
|
18
|
-
{scopes.map((scope, i) => (
|
|
19
|
-
<Menu.Item
|
|
20
|
-
key={i}
|
|
21
|
-
active={i === activeTab}
|
|
22
|
-
as={Link}
|
|
23
|
-
to={linkTo.TEMPLATE_SCOPE({ scope })}
|
|
24
|
-
>
|
|
25
|
-
<FormattedMessage id={`templates.tabs.${scope}`} />
|
|
26
|
-
</Menu.Item>
|
|
27
|
-
))}
|
|
28
|
-
</Menu>
|
|
29
|
-
);
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
TemplateTabs.propTypes = {
|
|
33
|
-
scopes: PropTypes.array
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const mapStateToProps = ({ templates }) => ({
|
|
37
|
-
scopes: _.flow(_.map("scope"), _.uniq, _.filter(filterScope))(templates)
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
export default connect(mapStateToProps)(TemplateTabs);
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { shallowWithIntl } from "@truedat/test/intl-stub";
|
|
3
|
-
import { TemplateCards } from "../TemplateCards";
|
|
4
|
-
|
|
5
|
-
jest.mock("react-router-dom", () => ({
|
|
6
|
-
...jest.requireActual("react-router-dom"),
|
|
7
|
-
useLocation: () => ({
|
|
8
|
-
hash: "",
|
|
9
|
-
key: "v5q3jz",
|
|
10
|
-
pathname: "/templates",
|
|
11
|
-
search: "?scope=bg"
|
|
12
|
-
})
|
|
13
|
-
}));
|
|
14
|
-
|
|
15
|
-
describe("<TemplateCards />", () => {
|
|
16
|
-
const templates = [
|
|
17
|
-
{
|
|
18
|
-
scope: "bg",
|
|
19
|
-
name: "testbg",
|
|
20
|
-
label: "testbg"
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
scope: "dq",
|
|
24
|
-
name: "testdq",
|
|
25
|
-
label: "testdq"
|
|
26
|
-
}
|
|
27
|
-
];
|
|
28
|
-
const props = { templates };
|
|
29
|
-
|
|
30
|
-
it("matches the latest snapshot", () => {
|
|
31
|
-
const wrapper = shallowWithIntl(<TemplateCards {...props} />);
|
|
32
|
-
expect(wrapper).toMatchSnapshot();
|
|
33
|
-
});
|
|
34
|
-
});
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
-
|
|
3
|
-
exports[`<TemplateCards /> matches the latest snapshot 1`] = `
|
|
4
|
-
<CardGroup>
|
|
5
|
-
<Card
|
|
6
|
-
as={
|
|
7
|
-
{
|
|
8
|
-
"$$typeof": Symbol(react.forward_ref),
|
|
9
|
-
"displayName": "Link",
|
|
10
|
-
"propTypes": {
|
|
11
|
-
"innerRef": [Function],
|
|
12
|
-
"onClick": [Function],
|
|
13
|
-
"replace": [Function],
|
|
14
|
-
"target": [Function],
|
|
15
|
-
"to": [Function],
|
|
16
|
-
},
|
|
17
|
-
"render": [Function],
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
className="create template"
|
|
21
|
-
link={true}
|
|
22
|
-
to="/templates/new"
|
|
23
|
-
>
|
|
24
|
-
<CardContent
|
|
25
|
-
textAlign="center"
|
|
26
|
-
>
|
|
27
|
-
<CardHeader>
|
|
28
|
-
<MemoizedFormattedMessage
|
|
29
|
-
id="templates.actions.create"
|
|
30
|
-
/>
|
|
31
|
-
</CardHeader>
|
|
32
|
-
<CardDescription>
|
|
33
|
-
<IconGroup
|
|
34
|
-
as="i"
|
|
35
|
-
floated="left"
|
|
36
|
-
size="big"
|
|
37
|
-
>
|
|
38
|
-
<Icon
|
|
39
|
-
as="i"
|
|
40
|
-
name="file outline"
|
|
41
|
-
/>
|
|
42
|
-
<Icon
|
|
43
|
-
as="i"
|
|
44
|
-
corner={true}
|
|
45
|
-
name="add"
|
|
46
|
-
/>
|
|
47
|
-
</IconGroup>
|
|
48
|
-
</CardDescription>
|
|
49
|
-
</CardContent>
|
|
50
|
-
</Card>
|
|
51
|
-
<TemplateCard
|
|
52
|
-
key="0"
|
|
53
|
-
template={
|
|
54
|
-
{
|
|
55
|
-
"label": "testbg",
|
|
56
|
-
"name": "testbg",
|
|
57
|
-
"scope": "bg",
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
/>
|
|
61
|
-
</CardGroup>
|
|
62
|
-
`;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import _ from "lodash/fp";
|
|
2
|
-
|
|
3
|
-
export const validScope = (scope) => _.negate(_.includes(scope))(["cx", "ca"]);
|
|
4
|
-
export const filterScope = (scope) => (validScope(scope) ? scope : null);
|
|
5
|
-
export const filterScopeWithTemplates = (scope, templates) =>
|
|
6
|
-
validScope(scope) && _.flow(_.map("scope"), _.includes(scope))(templates)
|
|
7
|
-
? scope
|
|
8
|
-
: null;
|