@truedat/dd 4.30.2 → 4.30.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/package.json +6 -6
- package/src/api.js +2 -0
- package/src/components/GrantRequest.js +12 -1
- package/src/components/GrantRequestApprovalForm.js +127 -0
- package/src/components/GrantRequestApprovalPopup.js +43 -0
- package/src/components/GrantRequestApprovals.js +41 -0
- package/src/components/GrantRoutes.js +21 -5
- package/src/components/MyGrantRequests.js +2 -2
- package/src/components/StructureGrantCart.js +1 -4
- package/src/components/__tests__/GrantRequestApprovalForm.spec.js +87 -0
- package/src/components/__tests__/GrantRequestApprovalPopup.spec.js +16 -0
- package/src/components/__tests__/__snapshots__/GrantRequest.spec.js.snap +12 -1
- package/src/components/__tests__/__snapshots__/GrantRequestApprovalForm.spec.js.snap +101 -0
- package/src/components/__tests__/__snapshots__/GrantRequestApprovalPopup.spec.js.snap +37 -0
- package/src/components/__tests__/__snapshots__/GrantRoutes.spec.js.snap +4 -0
- package/src/messages/en.js +16 -5
- package/src/messages/es.js +18 -6
- package/src/reducers/__tests__/creatingGrantRequestApproval.spec.js +33 -0
- package/src/reducers/creatingGrantRequestApproval.js +16 -0
- package/src/reducers/index.js +2 -0
- package/src/reducers/structureRedirect.js +4 -0
- package/src/routines.js +4 -0
- package/src/sagas/__tests__/createGrantRequestApproval.spec.js +80 -0
- package/src/sagas/createGrantRequestApproval.js +31 -0
- package/src/sagas/index.js +3 -0
- package/src/selectors/__tests__/getGrantRequests.spec.js +3 -3
- package/src/selectors/getGrantRequests.js +1 -1
- package/src/selectors/getGrantRequestsColumns.js +12 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [4.30.6] 2021-10-15
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- [TD-3874] Allow rule creation specifying domain for shared concepts
|
|
8
|
+
|
|
9
|
+
## [4.30.5] 2021-10-15
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- [TD-4226] i18n for grant checkout button
|
|
14
|
+
|
|
15
|
+
## [4.30.3] 2021-10-07
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- [TD-4044] Support for approving grant requests
|
|
20
|
+
|
|
3
21
|
## [4.30.1] 2021-10-06
|
|
4
22
|
|
|
5
23
|
### Added
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/dd",
|
|
3
|
-
"version": "4.30.
|
|
3
|
+
"version": "4.30.6",
|
|
4
4
|
"description": "Truedat Web Data Dictionary",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"jsnext:main": "src/index.js",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"@testing-library/jest-dom": "^5.14.1",
|
|
35
35
|
"@testing-library/react": "^12.0.0",
|
|
36
36
|
"@testing-library/user-event": "^13.2.1",
|
|
37
|
-
"@truedat/test": "4.30.
|
|
37
|
+
"@truedat/test": "4.30.6",
|
|
38
38
|
"babel-jest": "^27.0.6",
|
|
39
39
|
"babel-plugin-dynamic-import-node": "^2.3.3",
|
|
40
40
|
"babel-plugin-lodash": "^3.3.4",
|
|
@@ -84,9 +84,9 @@
|
|
|
84
84
|
]
|
|
85
85
|
},
|
|
86
86
|
"dependencies": {
|
|
87
|
-
"@truedat/auth": "4.30.
|
|
88
|
-
"@truedat/core": "4.30.
|
|
89
|
-
"@truedat/df": "4.30.
|
|
87
|
+
"@truedat/auth": "4.30.6",
|
|
88
|
+
"@truedat/core": "4.30.6",
|
|
89
|
+
"@truedat/df": "4.30.6",
|
|
90
90
|
"lodash": "^4.17.21",
|
|
91
91
|
"path-to-regexp": "^1.7.0",
|
|
92
92
|
"prop-types": "^15.7.2",
|
|
@@ -107,5 +107,5 @@
|
|
|
107
107
|
"react-dom": ">= 16.8.6 < 17",
|
|
108
108
|
"semantic-ui-react": ">= 0.88.2 < 2.1"
|
|
109
109
|
},
|
|
110
|
-
"gitHead": "
|
|
110
|
+
"gitHead": "f23b3cf877ac9383f2e638ddcd685d173ac6142a"
|
|
111
111
|
}
|
package/src/api.js
CHANGED
|
@@ -7,6 +7,7 @@ const API_DATA_STRUCTURE_VERSION = "/api/data_structures/:id/versions/:version";
|
|
|
7
7
|
const API_GRANT_FILTERS_SEARCH = "/api/grant_filters/search";
|
|
8
8
|
const API_PROFILE_EXECUTION = "/api/profile_executions/:id";
|
|
9
9
|
const API_GRANT_REQUEST = "/api/grant_requests/:id";
|
|
10
|
+
const API_GRANT_REQUEST_APPROVALS = "/api/grant_requests/:id/approvals";
|
|
10
11
|
const API_GRANT_REQUEST_GROUP = "/api/grant_request_groups";
|
|
11
12
|
const API_GRANT_REQUESTS = "/api/grant_requests";
|
|
12
13
|
const API_GRANTS_CSV = "/api/grants/csv";
|
|
@@ -49,6 +50,7 @@ export {
|
|
|
49
50
|
API_PROFILE_EXECUTION,
|
|
50
51
|
API_GRANT_FILTERS_SEARCH,
|
|
51
52
|
API_GRANT_REQUEST,
|
|
53
|
+
API_GRANT_REQUEST_APPROVALS,
|
|
52
54
|
API_GRANT_REQUEST_GROUP,
|
|
53
55
|
API_GRANT_REQUESTS,
|
|
54
56
|
API_GRANTS_SEARCH,
|
|
@@ -8,6 +8,7 @@ import { useIntl } from "react-intl";
|
|
|
8
8
|
import { Header, Grid, Icon, Segment, List, Label } from "semantic-ui-react";
|
|
9
9
|
import { getGrantRequestTemplate } from "../selectors";
|
|
10
10
|
import GrantRequestCrumbs from "./GrantRequestCrumbs";
|
|
11
|
+
import GrantRequestApprovalPopup from "./GrantRequestApprovalPopup";
|
|
11
12
|
|
|
12
13
|
const DynamicFormViewer = React.lazy(() =>
|
|
13
14
|
import("@truedat/df/components/DynamicFormViewer")
|
|
@@ -44,7 +45,17 @@ export const GrantRequest = ({ grantRequest, template }) => {
|
|
|
44
45
|
</Header>
|
|
45
46
|
</Grid.Column>
|
|
46
47
|
<Grid.Column width={4} textAlign="right">
|
|
47
|
-
|
|
48
|
+
<GrantRequestApprovalPopup
|
|
49
|
+
content={formatMessage({ id: "grantRequest.actions.approve" })}
|
|
50
|
+
primary
|
|
51
|
+
grantRequest={grantRequest}
|
|
52
|
+
/>
|
|
53
|
+
<GrantRequestApprovalPopup
|
|
54
|
+
content={formatMessage({ id: "grantRequest.actions.reject" })}
|
|
55
|
+
secondary
|
|
56
|
+
isRejection
|
|
57
|
+
grantRequest={grantRequest}
|
|
58
|
+
/>
|
|
48
59
|
</Grid.Column>
|
|
49
60
|
</Grid>
|
|
50
61
|
<List size="big" relaxed="very">
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { useForm, Controller } from "react-hook-form";
|
|
4
|
+
import { Button, Container, Form, Header } from "semantic-ui-react";
|
|
5
|
+
import PropTypes from "prop-types";
|
|
6
|
+
import { useIntl } from "react-intl";
|
|
7
|
+
import { connect } from "react-redux";
|
|
8
|
+
import { createGrantRequestApproval } from "../routines";
|
|
9
|
+
|
|
10
|
+
export const GrantRequestApprovalForm = ({
|
|
11
|
+
grantRequest,
|
|
12
|
+
isRejection,
|
|
13
|
+
saving,
|
|
14
|
+
createGrantRequestApproval,
|
|
15
|
+
}) => {
|
|
16
|
+
const { formatMessage } = useIntl();
|
|
17
|
+
|
|
18
|
+
const { handleSubmit, control, formState } = useForm({
|
|
19
|
+
mode: "all",
|
|
20
|
+
defaultValues: {
|
|
21
|
+
role: null,
|
|
22
|
+
comments: "",
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
const { isDirty, isValid } = formState;
|
|
26
|
+
|
|
27
|
+
const dropdownOptions = _.flow(
|
|
28
|
+
_.get("pending_roles"),
|
|
29
|
+
_.map((role) => ({ key: role, value: role, text: role }))
|
|
30
|
+
)(grantRequest);
|
|
31
|
+
|
|
32
|
+
const onSubmit = (formValues) => {
|
|
33
|
+
const { id, domain_id } = grantRequest;
|
|
34
|
+
const approval = {
|
|
35
|
+
...formValues,
|
|
36
|
+
domain_id,
|
|
37
|
+
is_rejection: isRejection,
|
|
38
|
+
};
|
|
39
|
+
createGrantRequestApproval({
|
|
40
|
+
grantRequestId: id,
|
|
41
|
+
approval,
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
return (
|
|
45
|
+
<Container style={{ width: "400px" }}>
|
|
46
|
+
<Header
|
|
47
|
+
as="h2"
|
|
48
|
+
content={formatMessage({
|
|
49
|
+
id: isRejection
|
|
50
|
+
? "grantRequestApproval.form.reject"
|
|
51
|
+
: "grantRequestApproval.form.approve",
|
|
52
|
+
})}
|
|
53
|
+
/>
|
|
54
|
+
<Form onSubmit={handleSubmit(onSubmit)}>
|
|
55
|
+
<Controller
|
|
56
|
+
control={control}
|
|
57
|
+
name="role"
|
|
58
|
+
rules={{ validate: (v) => !_.isEmpty(v) }}
|
|
59
|
+
render={({ onBlur, onChange, value }, { invalid }) => (
|
|
60
|
+
<Form.Dropdown
|
|
61
|
+
placeholder={formatMessage({
|
|
62
|
+
id: "grantRequestApproval.form.role.placeholder",
|
|
63
|
+
})}
|
|
64
|
+
label={formatMessage({
|
|
65
|
+
id: "grantRequestApproval.form.role.label",
|
|
66
|
+
})}
|
|
67
|
+
error={invalid}
|
|
68
|
+
onBlur={onBlur}
|
|
69
|
+
value={value}
|
|
70
|
+
onChange={(_, { value }) => onChange(value)}
|
|
71
|
+
selection
|
|
72
|
+
required
|
|
73
|
+
options={dropdownOptions}
|
|
74
|
+
/>
|
|
75
|
+
)}
|
|
76
|
+
/>
|
|
77
|
+
<Controller
|
|
78
|
+
control={control}
|
|
79
|
+
name="comments"
|
|
80
|
+
render={({ onBlur, onChange, value }, { invalid }) => (
|
|
81
|
+
<Form.TextArea
|
|
82
|
+
data-testid="comments-id"
|
|
83
|
+
label={formatMessage({
|
|
84
|
+
id: "grantRequestApproval.form.comments.label",
|
|
85
|
+
})}
|
|
86
|
+
placeholder={formatMessage({
|
|
87
|
+
id: "grantRequestApproval.form.comments.placeholder",
|
|
88
|
+
})}
|
|
89
|
+
value={value}
|
|
90
|
+
onBlur={onBlur}
|
|
91
|
+
error={invalid}
|
|
92
|
+
onChange={(_, { value }) => onChange(value)}
|
|
93
|
+
/>
|
|
94
|
+
)}
|
|
95
|
+
/>
|
|
96
|
+
<div className="actions">
|
|
97
|
+
<Button
|
|
98
|
+
type="submit"
|
|
99
|
+
disabled={!isDirty || !isValid}
|
|
100
|
+
loading={saving}
|
|
101
|
+
primary
|
|
102
|
+
content={formatMessage({
|
|
103
|
+
id: isRejection
|
|
104
|
+
? "grantRequest.actions.reject"
|
|
105
|
+
: "grantRequest.actions.approve",
|
|
106
|
+
})}
|
|
107
|
+
/>
|
|
108
|
+
</div>
|
|
109
|
+
</Form>
|
|
110
|
+
</Container>
|
|
111
|
+
);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
GrantRequestApprovalForm.propTypes = {
|
|
115
|
+
grantRequest: PropTypes.object,
|
|
116
|
+
isRejection: PropTypes.bool,
|
|
117
|
+
createGrantRequestApproval: PropTypes.func,
|
|
118
|
+
saving: PropTypes.bool,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export const mapStateToProps = (state) => ({
|
|
122
|
+
saving: state?.creatingGrantRequestApproval,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
export default connect(mapStateToProps, { createGrantRequestApproval })(
|
|
126
|
+
GrantRequestApprovalForm
|
|
127
|
+
);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React, { useState } from "react";
|
|
3
|
+
import { Button, Popup } from "semantic-ui-react";
|
|
4
|
+
import PropTypes from "prop-types";
|
|
5
|
+
import GrantRequestApprovalForm from "./GrantRequestApprovalForm";
|
|
6
|
+
|
|
7
|
+
export const GrantRequestApprovalPopup = ({
|
|
8
|
+
isRejection,
|
|
9
|
+
grantRequest,
|
|
10
|
+
...buttonProps
|
|
11
|
+
}) => {
|
|
12
|
+
const [open, setOpen] = useState(false);
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<Popup
|
|
16
|
+
on="click"
|
|
17
|
+
basic
|
|
18
|
+
flowing
|
|
19
|
+
content={
|
|
20
|
+
<GrantRequestApprovalForm
|
|
21
|
+
isRejection={isRejection}
|
|
22
|
+
grantRequest={grantRequest}
|
|
23
|
+
/>
|
|
24
|
+
}
|
|
25
|
+
onOpen={() => setOpen(true)}
|
|
26
|
+
onClose={() => setOpen(false)}
|
|
27
|
+
open={open}
|
|
28
|
+
position="bottom right"
|
|
29
|
+
size="large"
|
|
30
|
+
positionFixed
|
|
31
|
+
trigger={
|
|
32
|
+
<Button className="button icon group-actions" {...buttonProps} />
|
|
33
|
+
}
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
GrantRequestApprovalPopup.propTypes = {
|
|
39
|
+
grantRequest: PropTypes.object,
|
|
40
|
+
isRejection: PropTypes.bool,
|
|
41
|
+
saving: PropTypes.bool,
|
|
42
|
+
};
|
|
43
|
+
export default GrantRequestApprovalPopup;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { connect } from "react-redux";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
import { useIntl } from "react-intl";
|
|
5
|
+
import { Header, Icon, Segment } from "semantic-ui-react";
|
|
6
|
+
import {
|
|
7
|
+
getGrantRequests,
|
|
8
|
+
getGrantRequestsApprovalColumns
|
|
9
|
+
} from "../selectors";
|
|
10
|
+
import GrantRequests from "./GrantRequests";
|
|
11
|
+
|
|
12
|
+
export const GrantRequestApprovals = ({ columns, grantRequests }) => {
|
|
13
|
+
const { formatMessage } = useIntl();
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<Segment>
|
|
17
|
+
<Header as="h2">
|
|
18
|
+
<Icon circular name="key" />
|
|
19
|
+
<Header.Content>
|
|
20
|
+
{formatMessage({ id: "grantRequests.header.approve" })}
|
|
21
|
+
<Header.Subheader>
|
|
22
|
+
{formatMessage({ id: "grantRequests.subheader.approve" })}
|
|
23
|
+
</Header.Subheader>
|
|
24
|
+
</Header.Content>
|
|
25
|
+
</Header>
|
|
26
|
+
<GrantRequests columns={columns} grantRequests={grantRequests} />
|
|
27
|
+
</Segment>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const mapStateToProps = state => ({
|
|
32
|
+
columns: getGrantRequestsApprovalColumns(state),
|
|
33
|
+
grantRequests: getGrantRequests(state)
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
GrantRequestApprovals.propTypes = {
|
|
37
|
+
columns: PropTypes.array,
|
|
38
|
+
grantRequests: PropTypes.array
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export default connect(mapStateToProps)(GrantRequestApprovals);
|
|
@@ -4,13 +4,15 @@ import PropTypes from "prop-types";
|
|
|
4
4
|
import { Route, Switch } from "react-router-dom";
|
|
5
5
|
import { connect } from "react-redux";
|
|
6
6
|
import {
|
|
7
|
+
GRANT_REQUEST_APPROVALS,
|
|
7
8
|
GRANT_REQUEST,
|
|
8
9
|
GRANT_REQUESTS,
|
|
9
10
|
GRANTS_REQUESTS_CHECKOUT,
|
|
10
11
|
GRANTS,
|
|
11
12
|
MY_GRANT_REQUESTS,
|
|
12
|
-
MY_GRANTS
|
|
13
|
+
MY_GRANTS
|
|
13
14
|
} from "@truedat/core/routes";
|
|
15
|
+
import GrantRequestApprovals from "./GrantRequestApprovals";
|
|
14
16
|
import StructureGrantCartCheckout from "./StructureGrantCartCheckout";
|
|
15
17
|
import GrantFiltersLoader from "./GrantFiltersLoader";
|
|
16
18
|
import GrantRequestsLoader from "./GrantRequestsLoader";
|
|
@@ -50,6 +52,20 @@ export const GrantRoutes = ({ grantRequestLoaded }) => (
|
|
|
50
52
|
</>
|
|
51
53
|
)}
|
|
52
54
|
/>
|
|
55
|
+
<Route
|
|
56
|
+
path={GRANT_REQUEST_APPROVALS}
|
|
57
|
+
render={() => (
|
|
58
|
+
<>
|
|
59
|
+
<GrantRequestsLoader
|
|
60
|
+
defaultPayload={{
|
|
61
|
+
action: "approve",
|
|
62
|
+
status: "pending"
|
|
63
|
+
}}
|
|
64
|
+
/>
|
|
65
|
+
<GrantRequestApprovals />
|
|
66
|
+
</>
|
|
67
|
+
)}
|
|
68
|
+
/>
|
|
53
69
|
<Route
|
|
54
70
|
path={MY_GRANT_REQUESTS}
|
|
55
71
|
render={() => (
|
|
@@ -57,7 +73,7 @@ export const GrantRoutes = ({ grantRequestLoaded }) => (
|
|
|
57
73
|
<GrantRequestsLoader
|
|
58
74
|
defaultPayload={{
|
|
59
75
|
user: "me",
|
|
60
|
-
status: "pending,approved,rejected,processing"
|
|
76
|
+
status: "pending,approved,rejected,processing"
|
|
61
77
|
}}
|
|
62
78
|
/>
|
|
63
79
|
<MyGrantRequests />
|
|
@@ -86,12 +102,12 @@ export const GrantRoutes = ({ grantRequestLoaded }) => (
|
|
|
86
102
|
</>
|
|
87
103
|
);
|
|
88
104
|
|
|
89
|
-
const mapStateToProps =
|
|
90
|
-
grantRequestLoaded: !_.isEmpty(state.grantRequest)
|
|
105
|
+
const mapStateToProps = state => ({
|
|
106
|
+
grantRequestLoaded: !_.isEmpty(state.grantRequest)
|
|
91
107
|
});
|
|
92
108
|
|
|
93
109
|
GrantRoutes.propTypes = {
|
|
94
|
-
grantRequestLoaded: PropTypes.bool
|
|
110
|
+
grantRequestLoaded: PropTypes.bool
|
|
95
111
|
};
|
|
96
112
|
|
|
97
113
|
export default connect(mapStateToProps)(GrantRoutes);
|
|
@@ -5,7 +5,7 @@ import PropTypes from "prop-types";
|
|
|
5
5
|
import { useIntl } from "react-intl";
|
|
6
6
|
import { Checkbox, Form, Header, Icon, Segment } from "semantic-ui-react";
|
|
7
7
|
import { useForm, Controller } from "react-hook-form";
|
|
8
|
-
import {
|
|
8
|
+
import { getGrantRequests, getMyGrantRequestsColumns } from "../selectors";
|
|
9
9
|
import { setGrantRequestsQuery } from "../routines";
|
|
10
10
|
import GrantRequests from "./GrantRequests";
|
|
11
11
|
|
|
@@ -78,7 +78,7 @@ export const MyGrantRequests = ({
|
|
|
78
78
|
|
|
79
79
|
const mapStateToProps = (state) => ({
|
|
80
80
|
columns: getMyGrantRequestsColumns(state),
|
|
81
|
-
grantRequests:
|
|
81
|
+
grantRequests: getGrantRequests(state),
|
|
82
82
|
});
|
|
83
83
|
|
|
84
84
|
MyGrantRequests.propTypes = {
|
|
@@ -20,7 +20,6 @@ import { GRANTS_REQUESTS_CHECKOUT, STRUCTURES } from "@truedat/core/routes";
|
|
|
20
20
|
import { FiltersGrid } from "@truedat/dq/components/ruleImplementationForm/FiltersGrid";
|
|
21
21
|
import { getRuleImplementationOperators } from "@truedat/dq/selectors/getRuleImplementationOperators";
|
|
22
22
|
import {
|
|
23
|
-
addGrantRequestToCart,
|
|
24
23
|
removeGrantRequestFromCart,
|
|
25
24
|
updateGrantRequestFilter,
|
|
26
25
|
} from "../routines";
|
|
@@ -127,7 +126,7 @@ export const StructureGrantCart = ({
|
|
|
127
126
|
<>
|
|
128
127
|
<Divider />
|
|
129
128
|
<Button as={Link} to={linkTo.GRANTS_REQUESTS_CHECKOUT()}>
|
|
130
|
-
|
|
129
|
+
{formatMessage({ id: `structure.grant.cart.checkout` })}
|
|
131
130
|
</Button>
|
|
132
131
|
</>
|
|
133
132
|
)}
|
|
@@ -139,7 +138,6 @@ StructureGrantCart.propTypes = {
|
|
|
139
138
|
structure: PropTypes.object,
|
|
140
139
|
grantRequestsCart: PropTypes.object,
|
|
141
140
|
typeOperators: PropTypes.array,
|
|
142
|
-
addGrantRequestToCart: PropTypes.func,
|
|
143
141
|
removeGrantRequestFromCart: PropTypes.func,
|
|
144
142
|
updateGrantRequestFilter: PropTypes.func,
|
|
145
143
|
};
|
|
@@ -156,7 +154,6 @@ const mapStateToProps = (state) => {
|
|
|
156
154
|
const mapDispatchToProps = (dispatch) =>
|
|
157
155
|
bindActionCreators(
|
|
158
156
|
{
|
|
159
|
-
addGrantRequestToCart,
|
|
160
157
|
removeGrantRequestFromCart,
|
|
161
158
|
updateGrantRequestFilter,
|
|
162
159
|
},
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { waitFor } from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import { render } from "@truedat/test/render";
|
|
5
|
+
import { GrantRequestApprovalForm } from "../GrantRequestApprovalForm";
|
|
6
|
+
|
|
7
|
+
describe("<GrantRequestApprovalForm />", () => {
|
|
8
|
+
const grantRequest = {
|
|
9
|
+
id: 3,
|
|
10
|
+
domain_id: 8,
|
|
11
|
+
pending_roles: ["role1", "role2"],
|
|
12
|
+
};
|
|
13
|
+
const createGrantRequestApproval = jest.fn();
|
|
14
|
+
const saving = false;
|
|
15
|
+
const props = {
|
|
16
|
+
grantRequest,
|
|
17
|
+
isRejection: false,
|
|
18
|
+
saving,
|
|
19
|
+
createGrantRequestApproval,
|
|
20
|
+
};
|
|
21
|
+
const renderOpts = {
|
|
22
|
+
messages: {
|
|
23
|
+
en: {
|
|
24
|
+
"grantRequestApproval.form.approve": "approve",
|
|
25
|
+
"grantRequestApproval.form.reject": "reject",
|
|
26
|
+
"grantRequestApproval.form.role.label": "role_label",
|
|
27
|
+
"grantRequestApproval.form.role.placeholder": "role_placeholder",
|
|
28
|
+
"grantRequestApproval.form.comments.label": "comments_label",
|
|
29
|
+
"grantRequestApproval.form.comments.placeholder":
|
|
30
|
+
"comments_placeholder",
|
|
31
|
+
"grantRequest.actions.approve": "submit",
|
|
32
|
+
"grantRequest.actions.reject": "submit",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
it("matches the latest snapshot", () => {
|
|
38
|
+
const { container } = render(
|
|
39
|
+
<GrantRequestApprovalForm {...props} />,
|
|
40
|
+
renderOpts
|
|
41
|
+
);
|
|
42
|
+
expect(container).toMatchSnapshot();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("enables submit button when we select a role", async () => {
|
|
46
|
+
const { findByText, queryByText } = render(
|
|
47
|
+
<GrantRequestApprovalForm {...props} />,
|
|
48
|
+
renderOpts
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
expect(queryByText(/submit/)).toBeDisabled();
|
|
52
|
+
userEvent.click(await findByText(/role1/));
|
|
53
|
+
await waitFor(() => {
|
|
54
|
+
expect(queryByText(/submit/)).not.toBeDisabled();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("invokes function when submit button clicked", async () => {
|
|
59
|
+
const { findByText, queryByText, queryByTestId } = render(
|
|
60
|
+
<GrantRequestApprovalForm {...props} />,
|
|
61
|
+
renderOpts
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
expect(queryByText(/submit/)).toBeDisabled();
|
|
65
|
+
userEvent.click(await findByText(/role1/));
|
|
66
|
+
await waitFor(() => {
|
|
67
|
+
expect(queryByText(/submit/)).not.toBeDisabled();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
userEvent.type(queryByTestId("comments-id"), "comment");
|
|
71
|
+
userEvent.click(await findByText(/submit/));
|
|
72
|
+
await waitFor(() => {
|
|
73
|
+
expect(createGrantRequestApproval.mock.calls.length).toBe(1);
|
|
74
|
+
});
|
|
75
|
+
expect(createGrantRequestApproval).toHaveBeenCalledWith(
|
|
76
|
+
expect.objectContaining({
|
|
77
|
+
grantRequestId: 3,
|
|
78
|
+
approval: {
|
|
79
|
+
domain_id: 8,
|
|
80
|
+
is_rejection: false,
|
|
81
|
+
role: "role1",
|
|
82
|
+
comments: "comment",
|
|
83
|
+
},
|
|
84
|
+
})
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { shallow } from "enzyme";
|
|
3
|
+
import GrantRequestApprovalPopup from "../GrantRequestApprovalPopup";
|
|
4
|
+
|
|
5
|
+
describe("<GrantRequestApprovalPopup />", () => {
|
|
6
|
+
const props = {
|
|
7
|
+
isRejection: false,
|
|
8
|
+
grantRequest: { id: 2 },
|
|
9
|
+
content: "button content",
|
|
10
|
+
primary: true,
|
|
11
|
+
};
|
|
12
|
+
it("matches the latest snapshot", () => {
|
|
13
|
+
const wrapper = shallow(<GrantRequestApprovalPopup {...props} />);
|
|
14
|
+
expect(wrapper).toMatchSnapshot();
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -47,7 +47,18 @@ exports[`<GrantRequest /> matches the latest snapshot 1`] = `
|
|
|
47
47
|
</div>
|
|
48
48
|
<div
|
|
49
49
|
class="right aligned four wide column"
|
|
50
|
-
|
|
50
|
+
>
|
|
51
|
+
<button
|
|
52
|
+
class="ui primary button button icon group-actions"
|
|
53
|
+
>
|
|
54
|
+
grantRequest.actions.approve
|
|
55
|
+
</button>
|
|
56
|
+
<button
|
|
57
|
+
class="ui secondary button button icon group-actions"
|
|
58
|
+
>
|
|
59
|
+
grantRequest.actions.reject
|
|
60
|
+
</button>
|
|
61
|
+
</div>
|
|
51
62
|
</div>
|
|
52
63
|
<div
|
|
53
64
|
class="ui big very relaxed list"
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<GrantRequestApprovalForm /> matches the latest snapshot 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<div
|
|
6
|
+
class="ui container"
|
|
7
|
+
style="width: 400px;"
|
|
8
|
+
>
|
|
9
|
+
<h2
|
|
10
|
+
class="ui header"
|
|
11
|
+
>
|
|
12
|
+
approve
|
|
13
|
+
</h2>
|
|
14
|
+
<form
|
|
15
|
+
class="ui form"
|
|
16
|
+
>
|
|
17
|
+
<div
|
|
18
|
+
class="required field"
|
|
19
|
+
>
|
|
20
|
+
<label>
|
|
21
|
+
role_label
|
|
22
|
+
</label>
|
|
23
|
+
<div
|
|
24
|
+
aria-expanded="false"
|
|
25
|
+
class="ui selection dropdown"
|
|
26
|
+
required=""
|
|
27
|
+
role="listbox"
|
|
28
|
+
tabindex="0"
|
|
29
|
+
>
|
|
30
|
+
<div
|
|
31
|
+
aria-atomic="true"
|
|
32
|
+
aria-live="polite"
|
|
33
|
+
class="divider default text"
|
|
34
|
+
role="alert"
|
|
35
|
+
>
|
|
36
|
+
role_placeholder
|
|
37
|
+
</div>
|
|
38
|
+
<i
|
|
39
|
+
aria-hidden="true"
|
|
40
|
+
class="dropdown icon"
|
|
41
|
+
/>
|
|
42
|
+
<div
|
|
43
|
+
class="menu transition"
|
|
44
|
+
>
|
|
45
|
+
<div
|
|
46
|
+
aria-checked="false"
|
|
47
|
+
aria-selected="true"
|
|
48
|
+
class="selected item"
|
|
49
|
+
role="option"
|
|
50
|
+
style="pointer-events: all;"
|
|
51
|
+
>
|
|
52
|
+
<span
|
|
53
|
+
class="text"
|
|
54
|
+
>
|
|
55
|
+
role1
|
|
56
|
+
</span>
|
|
57
|
+
</div>
|
|
58
|
+
<div
|
|
59
|
+
aria-checked="false"
|
|
60
|
+
aria-selected="false"
|
|
61
|
+
class="item"
|
|
62
|
+
role="option"
|
|
63
|
+
style="pointer-events: all;"
|
|
64
|
+
>
|
|
65
|
+
<span
|
|
66
|
+
class="text"
|
|
67
|
+
>
|
|
68
|
+
role2
|
|
69
|
+
</span>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
<div
|
|
75
|
+
class="field"
|
|
76
|
+
>
|
|
77
|
+
<label>
|
|
78
|
+
comments_label
|
|
79
|
+
</label>
|
|
80
|
+
<textarea
|
|
81
|
+
data-testid="comments-id"
|
|
82
|
+
placeholder="comments_placeholder"
|
|
83
|
+
rows="3"
|
|
84
|
+
/>
|
|
85
|
+
</div>
|
|
86
|
+
<div
|
|
87
|
+
class="actions"
|
|
88
|
+
>
|
|
89
|
+
<button
|
|
90
|
+
class="ui primary disabled button"
|
|
91
|
+
disabled=""
|
|
92
|
+
tabindex="-1"
|
|
93
|
+
type="submit"
|
|
94
|
+
>
|
|
95
|
+
submit
|
|
96
|
+
</button>
|
|
97
|
+
</div>
|
|
98
|
+
</form>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
`;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<GrantRequestApprovalPopup /> matches the latest snapshot 1`] = `
|
|
4
|
+
<Popup
|
|
5
|
+
basic={true}
|
|
6
|
+
content={
|
|
7
|
+
<Memo(Connect(GrantRequestApprovalForm))
|
|
8
|
+
grantRequest={
|
|
9
|
+
Object {
|
|
10
|
+
"id": 2,
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
isRejection={false}
|
|
14
|
+
/>
|
|
15
|
+
}
|
|
16
|
+
disabled={false}
|
|
17
|
+
eventsEnabled={true}
|
|
18
|
+
flowing={true}
|
|
19
|
+
on="click"
|
|
20
|
+
onClose={[Function]}
|
|
21
|
+
onOpen={[Function]}
|
|
22
|
+
open={false}
|
|
23
|
+
pinned={false}
|
|
24
|
+
popperModifiers={Array []}
|
|
25
|
+
position="bottom right"
|
|
26
|
+
positionFixed={true}
|
|
27
|
+
size="large"
|
|
28
|
+
trigger={
|
|
29
|
+
<Button
|
|
30
|
+
as="button"
|
|
31
|
+
className="button icon group-actions"
|
|
32
|
+
content="button content"
|
|
33
|
+
primary={true}
|
|
34
|
+
/>
|
|
35
|
+
}
|
|
36
|
+
/>
|
|
37
|
+
`;
|
package/src/messages/en.js
CHANGED
|
@@ -38,6 +38,8 @@ export default {
|
|
|
38
38
|
"grant.header.end_date": "Expires",
|
|
39
39
|
"grant.header.start_date": "Access granted",
|
|
40
40
|
"grant.updated_at": "Last changed",
|
|
41
|
+
"grantRequest.actions.approve": "Approve",
|
|
42
|
+
"grantRequest.actions.reject": "Reject",
|
|
41
43
|
"grantRequest.content.approved": "Approved",
|
|
42
44
|
"grantRequest.content.empty": "Empty",
|
|
43
45
|
"grantRequest.content.pending": "Pending",
|
|
@@ -45,18 +47,26 @@ export default {
|
|
|
45
47
|
"grantRequest.content.processing": "Processing",
|
|
46
48
|
"grantRequest.content.rejected": "Rejected",
|
|
47
49
|
"grantRequest.crumb.title": "Request",
|
|
48
|
-
"grantRequest.header": "Grant request",
|
|
49
|
-
"grantRequests.empty": "No grant requests found",
|
|
50
|
-
"grantRequests.header": "Grant requests",
|
|
51
50
|
"grantRequest.header.data_structure": "Data Structure",
|
|
52
51
|
"grantRequest.header.path": "Path",
|
|
52
|
+
"grantRequest.header.status": "Status",
|
|
53
53
|
"grantRequest.header.updated_at": "Updated",
|
|
54
54
|
"grantRequest.header.user": "User",
|
|
55
|
-
"grantRequest.header
|
|
55
|
+
"grantRequest.header": "Grant request",
|
|
56
|
+
"grantRequestApproval.form.approve": "Approve grant request",
|
|
57
|
+
"grantRequestApproval.form.comments.label": "Comments",
|
|
58
|
+
"grantRequestApproval.form.comments.placeholder": "Comments",
|
|
59
|
+
"grantRequestApproval.form.reject": "Reject grant request",
|
|
60
|
+
"grantRequestApproval.form.role.label": "Approval role",
|
|
61
|
+
"grantRequestApproval.form.role.placeholder": "Role",
|
|
62
|
+
"grantRequests.empty": "No grant requests found",
|
|
56
63
|
"grantRequests.filters": "Filters",
|
|
64
|
+
"grantRequests.header.approve": "Approve grant requests",
|
|
57
65
|
"grantRequests.header.me": "My grant requests",
|
|
58
|
-
"grantRequests.
|
|
66
|
+
"grantRequests.header": "Grant requests",
|
|
67
|
+
"grantRequests.subheader.approve": "Grant requests pending approval",
|
|
59
68
|
"grantRequests.subheader.me": "Grant requests requested by me",
|
|
69
|
+
"grantRequests.subheader": "Pending approval grant requests",
|
|
60
70
|
"grants.actions.download.popup": "Select a grant",
|
|
61
71
|
"grants.actions.download.tooltip": "Download to CSV",
|
|
62
72
|
"grants.header": "Grants",
|
|
@@ -163,6 +173,7 @@ export default {
|
|
|
163
173
|
"structure.grant.request_grant": "Request grant",
|
|
164
174
|
"structure.grant.requested_grant": "Pending request",
|
|
165
175
|
"structure.grant.cart": "Requested grants",
|
|
176
|
+
"structure.grant.cart.checkout": "Checkout",
|
|
166
177
|
"structure.grant.cart.template": "Request information",
|
|
167
178
|
"structure.grant.cart.template.placeholder": "Select request type",
|
|
168
179
|
"structure.grant.cart.filters": "Filters for structure",
|
package/src/messages/es.js
CHANGED
|
@@ -39,6 +39,8 @@ export default {
|
|
|
39
39
|
"grant.header.start_date": "Acceso desde",
|
|
40
40
|
"grant.header.user": "Cuenta",
|
|
41
41
|
"grant.updated_at": "Actualizada",
|
|
42
|
+
"grantRequest.actions.approve": "Aprobar",
|
|
43
|
+
"grantRequest.actions.reject": "Rechazar",
|
|
42
44
|
"grantRequest.content.approved": "Aprobada",
|
|
43
45
|
"grantRequest.content.empty": "Vacío",
|
|
44
46
|
"grantRequest.content.pending": "Pendiente",
|
|
@@ -46,18 +48,27 @@ export default {
|
|
|
46
48
|
"grantRequest.content.processing": "Procesando",
|
|
47
49
|
"grantRequest.content.rejected": "Rechazada",
|
|
48
50
|
"grantRequest.crumb.title": "Petición",
|
|
49
|
-
"grantRequest.header": "Petición de acceso",
|
|
50
|
-
"grantRequests.empty": "No se han encontrado peticiones de datos",
|
|
51
|
-
"grantRequests.header": "Peticiones de acceso",
|
|
52
51
|
"grantRequest.header.data_structure": "Estructura",
|
|
53
52
|
"grantRequest.header.path": "Ruta",
|
|
53
|
+
"grantRequest.header.status": "Estado",
|
|
54
54
|
"grantRequest.header.updated_at": "Actualizado",
|
|
55
55
|
"grantRequest.header.user": "Usuario",
|
|
56
|
-
"grantRequest.header
|
|
57
|
-
"
|
|
56
|
+
"grantRequest.header": "Petición de acceso",
|
|
57
|
+
"grantRequestApproval.form.approve": "Aprobar petición de acceso",
|
|
58
|
+
"grantRequestApproval.form.comments.label": "Comentarios",
|
|
59
|
+
"grantRequestApproval.form.comments.placeholder": "Comentarios",
|
|
60
|
+
"grantRequestApproval.form.reject": "Rechazar petición de acceso",
|
|
61
|
+
"grantRequestApproval.form.role.label": "Rol de aprobación",
|
|
62
|
+
"grantRequestApproval.form.role.placeholder": "Rol",
|
|
63
|
+
"grantRequests.empty": "No se han encontrado peticiones de datos",
|
|
58
64
|
"grantRequests.filters": "Filtros",
|
|
59
|
-
"grantRequests.
|
|
65
|
+
"grantRequests.header.approve": "Aprobar peticiones de acceso",
|
|
66
|
+
"grantRequests.header.me": "Mis peticiones de acceso",
|
|
67
|
+
"grantRequests.header": "Peticiones de acceso",
|
|
68
|
+
"grantRequests.subheader.approve":
|
|
69
|
+
"Peticiones de acceso pendientes de aprobación",
|
|
60
70
|
"grantRequests.subheader.me": "Peticiones de acceso demandadas por mí",
|
|
71
|
+
"grantRequests.subheader": "Peticiones de acceso pendientes de aprobación",
|
|
61
72
|
"grants.actions.download.popup": "Seleccione un acceso",
|
|
62
73
|
"grants.actions.download.tooltip": "Descargar a CSV",
|
|
63
74
|
"grants.header": "Accesos",
|
|
@@ -165,6 +176,7 @@ export default {
|
|
|
165
176
|
"structure.grant.request_grant": "Solicitar acceso",
|
|
166
177
|
"structure.grant.requested_grant": "Solicitud pendiente",
|
|
167
178
|
"structure.grant.cart": "Accesos a solicitar",
|
|
179
|
+
"structure.grant.cart.checkout": "Checkout",
|
|
168
180
|
"structure.grant.cart.template": "Información de solicitud",
|
|
169
181
|
"structure.grant.cart.template.placeholder": "Seleccione tipo de solicitud",
|
|
170
182
|
"structure.grant.cart.filters": "Filtros para la estructura",
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createGrantRequestApproval } from "../../routines";
|
|
2
|
+
import { creatingGrantRequestApproval } from "../creatingGrantRequestApproval";
|
|
3
|
+
|
|
4
|
+
const fooState = { foo: "bar" };
|
|
5
|
+
const initialState = false;
|
|
6
|
+
|
|
7
|
+
describe("reducers: creatingGrantRequestApproval", () => {
|
|
8
|
+
it("should provide the initial state", () => {
|
|
9
|
+
expect(creatingGrantRequestApproval(undefined, {})).toBe(initialState);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("should handle the createGrantRequestApproval.TRIGGER action", () => {
|
|
13
|
+
expect(
|
|
14
|
+
creatingGrantRequestApproval(fooState, {
|
|
15
|
+
type: createGrantRequestApproval.TRIGGER,
|
|
16
|
+
})
|
|
17
|
+
).toBe(true);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should handle the createGrantRequestApproval.FULFILL action", () => {
|
|
21
|
+
expect(
|
|
22
|
+
creatingGrantRequestApproval(fooState, {
|
|
23
|
+
type: createGrantRequestApproval.FULFILL,
|
|
24
|
+
})
|
|
25
|
+
).toBe(initialState);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("should ignore unknown actions", () => {
|
|
29
|
+
expect(creatingGrantRequestApproval(fooState, { type: "FOO" })).toBe(
|
|
30
|
+
fooState
|
|
31
|
+
);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createGrantRequestApproval } from "../routines";
|
|
2
|
+
|
|
3
|
+
const initialState = false;
|
|
4
|
+
|
|
5
|
+
const creatingGrantRequestApproval = (state = initialState, { type }) => {
|
|
6
|
+
switch (type) {
|
|
7
|
+
case createGrantRequestApproval.TRIGGER:
|
|
8
|
+
return true;
|
|
9
|
+
case createGrantRequestApproval.FULFILL:
|
|
10
|
+
return initialState;
|
|
11
|
+
default:
|
|
12
|
+
return state;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export { creatingGrantRequestApproval };
|
package/src/reducers/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { bulkUpdateStructuresLoading } from "./bulkUpdateStructuresLoading";
|
|
2
2
|
import { childrenRelations, parentRelations } from "./structureRelations";
|
|
3
|
+
import { creatingGrantRequestApproval } from "./creatingGrantRequestApproval";
|
|
3
4
|
import { csvGraphDownloading } from "./csvGraphDownloading";
|
|
4
5
|
import { ddMessage } from "./ddMessage";
|
|
5
6
|
import { deletingStructureTags } from "./deletingStructureTags";
|
|
@@ -91,6 +92,7 @@ import { grantRequestsQuery } from "./grantRequestsQuery";
|
|
|
91
92
|
export {
|
|
92
93
|
bulkUpdateStructuresLoading,
|
|
93
94
|
childrenRelations,
|
|
95
|
+
creatingGrantRequestApproval,
|
|
94
96
|
csvGraphDownloading,
|
|
95
97
|
ddMessage,
|
|
96
98
|
deletingStructureTags,
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
uploadStructures,
|
|
16
16
|
doStructureNoteAction,
|
|
17
17
|
checkoutGrantRequest,
|
|
18
|
+
createGrantRequestApproval,
|
|
18
19
|
} from "../routines";
|
|
19
20
|
|
|
20
21
|
export const initialState = "";
|
|
@@ -67,6 +68,9 @@ export const structureRedirect = (
|
|
|
67
68
|
const id = _.prop("data.id")(payload);
|
|
68
69
|
return linkTo.PROFILE_GROUP({ id });
|
|
69
70
|
}
|
|
71
|
+
case createGrantRequestApproval.SUCCESS: {
|
|
72
|
+
return linkTo.GRANT_REQUEST_APPROVALS();
|
|
73
|
+
}
|
|
70
74
|
default:
|
|
71
75
|
return state;
|
|
72
76
|
}
|
package/src/routines.js
CHANGED
|
@@ -156,3 +156,7 @@ export const removeGrantRequestsQuery = createRoutine(
|
|
|
156
156
|
"REMOVE_GRANT_REQUESTS_QUERY"
|
|
157
157
|
);
|
|
158
158
|
export const setGrantRequestsQuery = createRoutine("SET_GRANT_REQUESTS_QUERY");
|
|
159
|
+
|
|
160
|
+
export const createGrantRequestApproval = createRoutine(
|
|
161
|
+
"CREATE_GRANT_REQUEST_APPROVAL"
|
|
162
|
+
);
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { compile } from "path-to-regexp";
|
|
2
|
+
import { testSaga } from "redux-saga-test-plan";
|
|
3
|
+
import { apiJsonPost, JSON_OPTS } from "@truedat/core/services/api";
|
|
4
|
+
import {
|
|
5
|
+
createGrantRequestApprovalRequestSaga,
|
|
6
|
+
createGrantRequestApprovalSaga,
|
|
7
|
+
} from "../createGrantRequestApproval";
|
|
8
|
+
import { createGrantRequestApproval } from "../../routines";
|
|
9
|
+
import { API_GRANT_REQUEST_APPROVALS } from "../../api";
|
|
10
|
+
|
|
11
|
+
describe("sagas: createGrantRequestApprovalRequestSaga", () => {
|
|
12
|
+
it("should invoke createGrantRequestApprovalSaga on createGrantRequestApproval.TRIGGER", () => {
|
|
13
|
+
expect(() => {
|
|
14
|
+
testSaga(createGrantRequestApprovalRequestSaga)
|
|
15
|
+
.next()
|
|
16
|
+
.takeLatest(
|
|
17
|
+
createGrantRequestApproval.TRIGGER,
|
|
18
|
+
createGrantRequestApprovalSaga
|
|
19
|
+
)
|
|
20
|
+
.finish()
|
|
21
|
+
.isDone();
|
|
22
|
+
}).not.toThrow();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("should throw exception if an unhandled action is received", () => {
|
|
26
|
+
expect(() => {
|
|
27
|
+
testSaga(createGrantRequestApprovalRequestSaga)
|
|
28
|
+
.next()
|
|
29
|
+
.takeLatest("FOO", createGrantRequestApprovalRequestSaga);
|
|
30
|
+
}).toThrow();
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe("sagas: createGrantRequestApprovalSaga", () => {
|
|
35
|
+
const approval = {
|
|
36
|
+
body: "approval",
|
|
37
|
+
};
|
|
38
|
+
const payload = {
|
|
39
|
+
grantRequestId: 8,
|
|
40
|
+
approval,
|
|
41
|
+
};
|
|
42
|
+
const request_data = { approval };
|
|
43
|
+
|
|
44
|
+
const url = compile(API_GRANT_REQUEST_APPROVALS)({ id: 8 });
|
|
45
|
+
|
|
46
|
+
it("should put a success action when a response is returned", () => {
|
|
47
|
+
expect(() => {
|
|
48
|
+
testSaga(createGrantRequestApprovalSaga, { payload })
|
|
49
|
+
.next()
|
|
50
|
+
.put({ ...createGrantRequestApproval.request(request_data) })
|
|
51
|
+
.next()
|
|
52
|
+
.call(apiJsonPost, url, request_data, JSON_OPTS)
|
|
53
|
+
.next({ data: payload })
|
|
54
|
+
.put({ ...createGrantRequestApproval.success(payload) })
|
|
55
|
+
.next()
|
|
56
|
+
.put(createGrantRequestApproval.fulfill())
|
|
57
|
+
.next()
|
|
58
|
+
.isDone();
|
|
59
|
+
}).not.toThrow();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("should put a failure action when the call returns an error", () => {
|
|
63
|
+
const message = "Request failed";
|
|
64
|
+
const error = { message };
|
|
65
|
+
|
|
66
|
+
expect(() => {
|
|
67
|
+
testSaga(createGrantRequestApprovalSaga, { payload })
|
|
68
|
+
.next()
|
|
69
|
+
.put({ ...createGrantRequestApproval.request(request_data) })
|
|
70
|
+
.next()
|
|
71
|
+
.call(apiJsonPost, url, request_data, JSON_OPTS)
|
|
72
|
+
.throw(error)
|
|
73
|
+
.put(createGrantRequestApproval.failure(message))
|
|
74
|
+
.next()
|
|
75
|
+
.put(createGrantRequestApproval.fulfill())
|
|
76
|
+
.next()
|
|
77
|
+
.isDone();
|
|
78
|
+
}).not.toThrow();
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { compile } from "path-to-regexp";
|
|
2
|
+
import { call, put, takeLatest } from "redux-saga/effects";
|
|
3
|
+
import { apiJsonPost, JSON_OPTS } from "@truedat/core/services/api";
|
|
4
|
+
import { createGrantRequestApproval } from "../routines";
|
|
5
|
+
import { API_GRANT_REQUEST_APPROVALS } from "../api";
|
|
6
|
+
|
|
7
|
+
export function* createGrantRequestApprovalSaga({ payload }) {
|
|
8
|
+
try {
|
|
9
|
+
const { grantRequestId, approval } = payload;
|
|
10
|
+
const url = compile(API_GRANT_REQUEST_APPROVALS)({ id: grantRequestId });
|
|
11
|
+
yield put(createGrantRequestApproval.request({ approval }));
|
|
12
|
+
const { data } = yield call(apiJsonPost, url, { approval }, JSON_OPTS);
|
|
13
|
+
yield put(createGrantRequestApproval.success(data));
|
|
14
|
+
} catch (error) {
|
|
15
|
+
if (error.response) {
|
|
16
|
+
const { status, data } = error.response;
|
|
17
|
+
yield put(createGrantRequestApproval.failure({ status, data }));
|
|
18
|
+
} else {
|
|
19
|
+
yield put(createGrantRequestApproval.failure(error.message));
|
|
20
|
+
}
|
|
21
|
+
} finally {
|
|
22
|
+
yield put(createGrantRequestApproval.fulfill());
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function* createGrantRequestApprovalRequestSaga() {
|
|
27
|
+
yield takeLatest(
|
|
28
|
+
createGrantRequestApproval.TRIGGER,
|
|
29
|
+
createGrantRequestApprovalSaga
|
|
30
|
+
);
|
|
31
|
+
}
|
package/src/sagas/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { bulkUpdateStructuresRequestsSaga } from "./bulkUpdateStructures";
|
|
2
|
+
import { createGrantRequestApprovalRequestSaga } from "./createGrantRequestApproval";
|
|
2
3
|
import { createGraphRequestSaga } from "./createGraph";
|
|
3
4
|
import { createProfileGroupRequestSaga } from "./createProfileGroup";
|
|
4
5
|
import { createStructureGraphRequestSaga } from "./createStructureGraph";
|
|
@@ -46,6 +47,7 @@ import { operateGrantRequestToCartRequestSaga } from "./operateGrantRequestCart"
|
|
|
46
47
|
|
|
47
48
|
export {
|
|
48
49
|
bulkUpdateStructuresRequestsSaga,
|
|
50
|
+
createGrantRequestApprovalRequestSaga,
|
|
49
51
|
createGraphRequestSaga,
|
|
50
52
|
createProfileGroupRequestSaga,
|
|
51
53
|
createStructureGraphRequestSaga,
|
|
@@ -94,6 +96,7 @@ export {
|
|
|
94
96
|
|
|
95
97
|
export default [
|
|
96
98
|
bulkUpdateStructuresRequestsSaga(),
|
|
99
|
+
createGrantRequestApprovalRequestSaga(),
|
|
97
100
|
createGraphRequestSaga(),
|
|
98
101
|
createProfileGroupRequestSaga(),
|
|
99
102
|
createStructureGraphRequestSaga(),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getGrantRequests } from "..";
|
|
2
2
|
|
|
3
3
|
const grantRequests = [
|
|
4
4
|
{ updated_at: "2021-09-20 11:05:58", id: 1 },
|
|
@@ -6,12 +6,12 @@ const grantRequests = [
|
|
|
6
6
|
{ updated_at: "2021-10-04 07:25:01", id: 3 },
|
|
7
7
|
];
|
|
8
8
|
|
|
9
|
-
describe("selectors:
|
|
9
|
+
describe("selectors: getGrantRequests", () => {
|
|
10
10
|
const state = {
|
|
11
11
|
grantRequests,
|
|
12
12
|
};
|
|
13
13
|
it("get fields sorted by updated_at", () => {
|
|
14
|
-
expect(
|
|
14
|
+
expect(getGrantRequests(state)).toEqual([
|
|
15
15
|
{ updated_at: "2021-10-04 07:25:01", id: 3 },
|
|
16
16
|
{ updated_at: "2021-09-20 11:05:58", id: 1 },
|
|
17
17
|
{ id: 2 },
|
|
@@ -66,7 +66,7 @@ export const defaultGrantRequestsTableColumns = [
|
|
|
66
66
|
{
|
|
67
67
|
name: "user",
|
|
68
68
|
fieldSelector: (grantRequest) => ({
|
|
69
|
-
user: grantRequest?._embedded?.group?.user,
|
|
69
|
+
user: grantRequest?._embedded?.group?._embedded?.user,
|
|
70
70
|
}),
|
|
71
71
|
fieldDecorator: UserDecorator,
|
|
72
72
|
},
|
|
@@ -107,7 +107,17 @@ export const getMyGrantRequestsColumns = createSelector(
|
|
|
107
107
|
(grantsRequestsColumns) => {
|
|
108
108
|
return _.flow(
|
|
109
109
|
_.defaultTo(defaultGrantRequestsTableColumns),
|
|
110
|
-
_.reject(
|
|
110
|
+
_.reject({ name: "user" })
|
|
111
|
+
)(grantsRequestsColumns);
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
export const getGrantRequestsApprovalColumns = createSelector(
|
|
116
|
+
[_.prop("grantRequestsColumns")],
|
|
117
|
+
(grantsRequestsColumns) => {
|
|
118
|
+
return _.flow(
|
|
119
|
+
_.defaultTo(defaultGrantRequestsTableColumns),
|
|
120
|
+
_.reject({ name: "status" })
|
|
111
121
|
)(grantsRequestsColumns);
|
|
112
122
|
}
|
|
113
123
|
);
|