@truedat/dd 5.19.0 → 5.20.1

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.
Files changed (43) hide show
  1. package/package.json +5 -5
  2. package/src/api/queries.js +19 -1
  3. package/src/components/Grant.js +9 -27
  4. package/src/components/GrantRemoval.js +26 -34
  5. package/src/components/GrantRemovalDirectButton.js +89 -0
  6. package/src/components/GrantRemovalWorkflow.js +107 -0
  7. package/src/components/GrantRemovalWorkflowDropdown.js +144 -0
  8. package/src/components/GrantRequest.js +2 -1
  9. package/src/components/GrantRequestCancel.js +5 -2
  10. package/src/components/GrantRequestHeader.js +13 -7
  11. package/src/components/GrantRequestRow.js +8 -1
  12. package/src/components/GrantRequests.js +8 -3
  13. package/src/components/GrantRequestsTable.js +1 -1
  14. package/src/components/StructureGrantDropdown.js +7 -40
  15. package/src/components/StructureGrantSummaryButton.js +2 -2
  16. package/src/components/StructureGrants.js +21 -17
  17. package/src/components/__tests__/Grant.spec.js +11 -7
  18. package/src/components/__tests__/GrantRemoval.spec.js +19 -54
  19. package/src/components/__tests__/GrantRemovalDirectButton.spec.js +66 -0
  20. package/src/components/__tests__/GrantRemovalWorkflow.spec.js +157 -0
  21. package/src/components/__tests__/GrantRequest.spec.js +2 -0
  22. package/src/components/__tests__/GrantRequestHeader.spec.js +2 -5
  23. package/src/components/__tests__/StructureGrantDropdown.spec.js +1 -16
  24. package/src/components/__tests__/StructureGrantSummaryButton.spec.js +3 -2
  25. package/src/components/__tests__/StructureGrants.spec.js +5 -1
  26. package/src/components/__tests__/__snapshots__/Grant.spec.js.snap +5 -4
  27. package/src/components/__tests__/__snapshots__/GrantRequest.spec.js.snap +3 -3
  28. package/src/components/__tests__/__snapshots__/GrantRequestHeader.spec.js.snap +4 -4
  29. package/src/components/__tests__/__snapshots__/GrantRequestsSearchResults.spec.js.snap +1 -1
  30. package/src/components/__tests__/__snapshots__/StructureGrantDropdown.spec.js.snap +0 -52
  31. package/src/components/__tests__/__snapshots__/StructureGrantListButton.spec.js.snap +0 -14
  32. package/src/components/__tests__/__snapshots__/StructureGrantSummaryButton.spec.js.snap +0 -14
  33. package/src/components/__tests__/__snapshots__/StructureGrants.spec.js.snap +4 -5
  34. package/src/hooks/useGrantRequest.js +10 -1
  35. package/src/messages/en.js +23 -8
  36. package/src/messages/es.js +26 -9
  37. package/src/reducers/structure.js +1 -0
  38. package/src/sagas/__tests__/createGrantRequestStatus.spec.js +1 -1
  39. package/src/sagas/createGrantRequestStatus.js +24 -8
  40. package/src/selectors/getGrantRequestsColumns.js +32 -5
  41. package/src/selectors/getGrantRequestsSearchColumns.js +20 -14
  42. package/src/selectors/utils/decorators.js +68 -0
  43. package/src/components/__tests__/__snapshots__/GrantRemoval.spec.js.snap +0 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/dd",
3
- "version": "5.19.0",
3
+ "version": "5.20.1",
4
4
  "description": "Truedat Web Data Dictionary",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -88,9 +88,9 @@
88
88
  },
89
89
  "dependencies": {
90
90
  "@apollo/client": "^3.7.1",
91
- "@truedat/auth": "5.19.0",
92
- "@truedat/core": "5.19.0",
93
- "@truedat/df": "5.19.0",
91
+ "@truedat/auth": "5.20.1",
92
+ "@truedat/core": "5.20.1",
93
+ "@truedat/df": "5.20.1",
94
94
  "lodash": "^4.17.21",
95
95
  "moment": "^2.29.4",
96
96
  "path-to-regexp": "^1.7.0",
@@ -115,5 +115,5 @@
115
115
  "react-dom": ">= 16.8.6 < 17",
116
116
  "semantic-ui-react": ">= 2.0.3 < 2.2"
117
117
  },
118
- "gitHead": "df3fcd6655456c3468259d79762800c3c417147d"
118
+ "gitHead": "a7197e02a1b1a88c375fe044dee65c7e96ccd618"
119
119
  }
@@ -94,7 +94,7 @@ export const REFERENCE_DATASET_QUERY = gql`
94
94
  }
95
95
  `;
96
96
 
97
- export const LATEST_GRANT_REQUEST_QUERY = gql`
97
+ export const LATEST_GRANT_REQUEST_BY_DS_QUERY = gql`
98
98
  query latestGrantRequest($id: ID!) {
99
99
  latestGrantRequest(data_structure_id: $id) {
100
100
  id
@@ -103,6 +103,24 @@ export const LATEST_GRANT_REQUEST_QUERY = gql`
103
103
  id
104
104
  }
105
105
  }
106
+ requestType
107
+ status {
108
+ status
109
+ }
110
+ }
111
+ }
112
+ `;
113
+
114
+ export const LATEST_GRANT_REQUEST_BY_GRANT_QUERY = gql`
115
+ query latestGrantRequest($id: ID!, $requestType: RequestTypeEnum!) {
116
+ latestGrantRequest(grant_id: $id, requestType: $requestType) {
117
+ id
118
+ group {
119
+ grant {
120
+ id
121
+ }
122
+ }
123
+ requestType
106
124
  status {
107
125
  status
108
126
  }
@@ -1,17 +1,16 @@
1
1
  import _ from "lodash/fp";
2
2
  import React from "react";
3
3
  import PropTypes from "prop-types";
4
+ import Moment from "react-moment";
4
5
  import { useIntl } from "react-intl";
5
6
  import { Link, useParams } from "react-router-dom";
6
-
7
- import Moment from "react-moment";
8
7
  import { Grid, Segment, Header, Icon, List, Button } from "semantic-ui-react";
9
-
10
8
  import { linkTo } from "@truedat/core/routes";
11
9
  import { useGrant, useGrantRemoval, useStructure } from "../hooks/useGrant";
12
10
  import GrantCrumbs from "./GrantCrumbs";
13
11
  import GrantRemoval from "./GrantRemoval";
14
12
  import GrantChangeRequest from "./GrantChangeRequest";
13
+
15
14
  const DynamicFieldValue = React.lazy(() =>
16
15
  import("@truedat/df/components/DynamicFieldValue")
17
16
  );
@@ -52,6 +51,8 @@ export const GrantHeader = ({ grant, title, actions, onStatusChange }) => {
52
51
  })} ${title}`}
53
52
  </Header.Content>
54
53
  </Header>
54
+ </Grid.Column>
55
+ <Grid.Column width={4} textAlign="right">
55
56
  {actions?.update && !loading ? (
56
57
  <GrantChangeRequest
57
58
  structure={data_structure}
@@ -66,30 +67,11 @@ export const GrantHeader = ({ grant, title, actions, onStatusChange }) => {
66
67
  />
67
68
  </GrantChangeRequest>
68
69
  ) : null}
69
- </Grid.Column>
70
- <Grid.Column width={4} textAlign="right">
71
- {actions?.request_removal ? (
72
- <GrantRemoval grant={grant} onConfirm={onStatusChange}>
73
- <Button
74
- className="button icon group-actions"
75
- color="red"
76
- content={formatMessage({
77
- id: "grant.actions.request_removal.button",
78
- })}
79
- />
80
- </GrantRemoval>
81
- ) : null}
82
- {actions?.cancel_removal ? (
83
- <GrantRemoval grant={grant} onConfirm={onStatusChange}>
84
- <Button
85
- className="button icon group-actions"
86
- color="olive"
87
- content={formatMessage({
88
- id: "grant.actions.cancel_removal.button",
89
- })}
90
- />
91
- </GrantRemoval>
92
- ) : null}
70
+ <GrantRemoval
71
+ actions={actions}
72
+ grant={grant}
73
+ onConfirm={onStatusChange}
74
+ />
93
75
  </Grid.Column>
94
76
  </Grid.Row>
95
77
  </Grid>
@@ -1,56 +1,48 @@
1
1
  import React from "react";
2
2
  import PropTypes from "prop-types";
3
3
  import { connect } from "react-redux";
4
- import { useIntl } from "react-intl";
5
- import { ConfirmModal } from "@truedat/core/components";
6
- import _ from "lodash";
7
4
  import { requestGrantRemoval } from "../routines";
5
+ import GrantRemovalDirectButton from "./GrantRemovalDirectButton";
6
+ import GrantRemovalWorkflow from "./GrantRemovalWorkflow";
8
7
 
9
8
  export const GrantRemoval = ({
10
9
  grant,
10
+ actions,
11
11
  requestGrantRemoval,
12
- children,
13
12
  onConfirm,
14
- previousStructureQuery,
13
+ userPermissions,
15
14
  }) => {
16
- const { formatMessage } = useIntl();
17
-
18
15
  if (!grant) return null;
19
16
 
20
- const action = grant.pending_removal ? "cancel_removal" : "request_removal";
17
+ const directButtonProps = ({ pending_removal }) => ({
18
+ actions,
19
+ grant,
20
+ requestGrantRemoval,
21
+ onConfirm,
22
+ operation: !pending_removal ? "markPendingRemoval" : "unmarkPendingRemoval",
23
+ });
24
+
25
+ // from StructureAccess
26
+ if (userPermissions?.update_grant_removal) {
27
+ return <GrantRemovalDirectButton {...directButtonProps(grant)} />;
28
+ }
29
+
30
+ // from Grant
31
+ if (actions?.manage_grant_removal) {
32
+ return <GrantRemovalDirectButton {...directButtonProps(grant)} />;
33
+ } else if (actions?.manage_grant_removal_request) {
34
+ return <GrantRemovalWorkflow grant={grant} actions={actions} />;
35
+ }
21
36
 
22
- return (
23
- <ConfirmModal
24
- trigger={children}
25
- header={formatMessage({
26
- id: `grant.actions.${action}.confirmation.header`,
27
- })}
28
- content={formatMessage({
29
- id: `grant.actions.${action}.confirmation.content`,
30
- })}
31
- onConfirm={() => {
32
- const onConfirmPayload = {
33
- id: grant.id,
34
- action,
35
- data_structure_id: _.prop("data_structure.id")(grant),
36
- version: _.prop("data_structure_version.id")(grant),
37
- previousStructureQuery,
38
- };
39
- if (_.isFunction(onConfirm)) onConfirm(onConfirmPayload);
40
- else requestGrantRemoval(onConfirmPayload);
41
- }}
42
- onOpen={(e) => e.stopPropagation()}
43
- onClose={(e) => e.stopPropagation()}
44
- />
45
- );
37
+ return null;
46
38
  };
47
39
 
48
40
  GrantRemoval.propTypes = {
49
41
  grant: PropTypes.object,
42
+ actions: PropTypes.object,
50
43
  requestGrantRemoval: PropTypes.func,
51
- children: PropTypes.node,
44
+ userPermissions: PropTypes.object,
52
45
  onConfirm: PropTypes.func,
53
- previousStructureQuery: PropTypes.object,
54
46
  };
55
47
 
56
48
  export default connect(null, { requestGrantRemoval })(GrantRemoval);
@@ -0,0 +1,89 @@
1
+ import _ from "lodash/fp";
2
+ import React, { useEffect, useState } from "react";
3
+ import PropTypes from "prop-types";
4
+ import { useIntl } from "react-intl";
5
+ import { ConfirmModal } from "@truedat/core/components";
6
+ import { Button } from "semantic-ui-react";
7
+
8
+ export const GrantRemovalDirectButton = ({
9
+ actions,
10
+ grant,
11
+ requestGrantRemoval,
12
+ onConfirm,
13
+ previousStructureQuery,
14
+ operation,
15
+ }) => {
16
+ const { formatMessage } = useIntl();
17
+ const [button, setButton] = useState(null);
18
+
19
+ useEffect(() => {
20
+ const { color, translation } =
21
+ operation === "markPendingRemoval"
22
+ ? {
23
+ color: "red",
24
+ translation: "grant.actions.markPendingRemoval.button",
25
+ }
26
+ : operation === "unmarkPendingRemoval"
27
+ ? {
28
+ color: "olive",
29
+ translation: "grant.actions.unmarkPendingRemoval.button",
30
+ }
31
+ : { color: null, translation: null };
32
+
33
+ setButton(
34
+ <Button
35
+ className="button icon group-actions"
36
+ color={color}
37
+ content={formatMessage({
38
+ id: translation,
39
+ })}
40
+ />
41
+ );
42
+ }, [operation, formatMessage]);
43
+
44
+ if (!grant) {
45
+ return null;
46
+ }
47
+
48
+ const action = grant.pending_removal
49
+ ? "unmarkPendingRemoval"
50
+ : "markPendingRemoval";
51
+
52
+ const removeGrant = _.isFunction(onConfirm) ? onConfirm : requestGrantRemoval;
53
+ const payload = {
54
+ id: grant.id,
55
+ action: _.snakeCase(action),
56
+ data_structure_id: _.prop("data_structure.id")(grant),
57
+ version: _.prop("data_structure_version.id")(grant),
58
+ previousStructureQuery,
59
+ version: grant?.data_structure_version?.id,
60
+ };
61
+
62
+ return (
63
+ <ConfirmModal
64
+ trigger={button}
65
+ header={formatMessage({
66
+ id: `grant.actions.${action}.confirmation.header`,
67
+ })}
68
+ content={formatMessage({
69
+ id: `grant.actions.${action}.confirmation.content`,
70
+ })}
71
+ onConfirm={() => {
72
+ removeGrant(payload)
73
+ }}
74
+ onOpen={(e) => e.stopPropagation()}
75
+ onClose={(e) => e.stopPropagation()}
76
+ />
77
+ );
78
+ };
79
+
80
+ GrantRemovalDirectButton.propTypes = {
81
+ actions: PropTypes.object,
82
+ grant: PropTypes.object,
83
+ requestGrantRemoval: PropTypes.func,
84
+ onConfirm: PropTypes.func,
85
+ previousStructureQuery: PropTypes.object,
86
+ operation: PropTypes.string,
87
+ };
88
+
89
+ export default GrantRemovalDirectButton;
@@ -0,0 +1,107 @@
1
+ import _ from "lodash/fp";
2
+ import React, { useState } from "react";
3
+ import PropTypes from "prop-types";
4
+ import { useQuery } from "@apollo/client";
5
+ import { Button, Loader, Popup } from "semantic-ui-react";
6
+ import { useHistory } from "react-router-dom";
7
+ import { useIntl } from "react-intl";
8
+ import { linkTo } from "@truedat/core/routes";
9
+ import { LATEST_GRANT_REQUEST_BY_GRANT_QUERY } from "@truedat/dd/api/queries";
10
+ import { useGrantRequestGroup } from "../hooks/useGrantRequest";
11
+ import GrantRemovalWorkflowDropdown from "./GrantRemovalWorkflowDropdown";
12
+
13
+ export const GrantRemovalWorkflow = ({
14
+ latestGrantRequest: latestGrantRequestProp,
15
+ grant,
16
+ }) => {
17
+ const history = useHistory();
18
+ const { formatMessage } = useIntl();
19
+ const [latestGrantRequest, setLatestGrantRequest] = useState(
20
+ latestGrantRequestProp
21
+ );
22
+ const grantRequestStatus = _.prop("status.status")(latestGrantRequest);
23
+ const { trigger: createGrantRequestGroupTrigger } = useGrantRequestGroup();
24
+
25
+ const createGrantRequestGroupAndRedirect = (payload) => async (e) => {
26
+ const {
27
+ data: {
28
+ data: {
29
+ _embedded: {
30
+ requests: [{ id: grantDeletionRequestId }],
31
+ },
32
+ },
33
+ },
34
+ } = await createGrantRequestGroupTrigger(payload);
35
+ history.push(linkTo.GRANT_REQUEST({ id: grantDeletionRequestId }));
36
+ };
37
+
38
+ const payload = {
39
+ grant_request_group: {
40
+ requests: [
41
+ {
42
+ grant_id: grant?.id,
43
+ filters: {},
44
+ request_type: "grant_removal",
45
+ },
46
+ ],
47
+ },
48
+ };
49
+
50
+ const requestButton = (
51
+ <Button
52
+ className="button basic icon group-actions"
53
+ icon="shield"
54
+ color="orange"
55
+ onClick={createGrantRequestGroupAndRedirect(payload)}
56
+ />
57
+ );
58
+
59
+ return (!grantRequestStatus || grantRequestStatus === "cancelled") &&
60
+ !grant.pending_removal ? (
61
+ <Popup
62
+ content={formatMessage({
63
+ id: "request.grantRemoval.action.start.description",
64
+ })}
65
+ trigger={requestButton}
66
+ />
67
+ ) : (
68
+ <GrantRemovalWorkflowDropdown
69
+ latestGrantRequest={latestGrantRequest}
70
+ setLatestGrantRequest={setLatestGrantRequest}
71
+ grant={grant}
72
+ />
73
+ );
74
+ };
75
+
76
+ GrantRemovalWorkflow.propTypes = {
77
+ latestGrantRequest: PropTypes.object,
78
+ grant: PropTypes.object,
79
+ };
80
+
81
+ export const GrantRemovalWorkflowLoader = (props) => {
82
+ const { loading, error, data } = useQuery(
83
+ LATEST_GRANT_REQUEST_BY_GRANT_QUERY,
84
+ {
85
+ fetchPolicy: "cache-and-network",
86
+ skip: props?.actions?.hasOwnProperty("manage_grant_removal"),
87
+ variables: { id: props.grant.id, requestType: "GRANT_REMOVAL" },
88
+ }
89
+ );
90
+ if (error) return null;
91
+ if (loading) return <Loader />;
92
+ const latestGrantRequest = data?.latestGrantRequest;
93
+ return (
94
+ <GrantRemovalWorkflow
95
+ loading={loading}
96
+ latestGrantRequest={latestGrantRequest}
97
+ {...props}
98
+ />
99
+ );
100
+ };
101
+
102
+ GrantRemovalWorkflowLoader.propTypes = {
103
+ actions: PropTypes.object,
104
+ grant: PropTypes.object,
105
+ };
106
+
107
+ export default GrantRemovalWorkflowLoader;
@@ -0,0 +1,144 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { Button, Dropdown, Popup, Header } from "semantic-ui-react";
5
+ import { useHistory } from "react-router-dom";
6
+ import { useIntl } from "react-intl";
7
+ import { linkTo } from "@truedat/core/routes";
8
+
9
+ import GrantRequestCancel from "./GrantRequestCancel";
10
+
11
+ export const GrantRemovalWorkflowDropdown = ({
12
+ latestGrantRequest,
13
+ setLatestGrantRequest,
14
+ grant,
15
+ }) => {
16
+ const history = useHistory();
17
+ const { formatMessage } = useIntl();
18
+
19
+ const grantRequestStatus = _.prop("status.status")(latestGrantRequest);
20
+
21
+ const actions = {
22
+ viewRequest: latestGrantRequest ? (
23
+ <Dropdown.Item
24
+ key="viewRequest"
25
+ onClick={() =>
26
+ history.push(linkTo.GRANT_REQUEST({ id: latestGrantRequest.id }))
27
+ }
28
+ icon="info"
29
+ text={formatMessage({
30
+ id: `grant.actions.viewRequest.confirmation.header`,
31
+ })}
32
+ />
33
+ ) : null,
34
+ cancelRequest: (
35
+ <GrantRequestCancel
36
+ key="cancelRequest"
37
+ grantRequest={latestGrantRequest}
38
+ setLatestGrantRequest={setLatestGrantRequest}
39
+ >
40
+ <Dropdown.Item
41
+ icon="delete"
42
+ text={formatMessage({
43
+ id: "structure.grant.cancel_grant_request",
44
+ })}
45
+ />
46
+ </GrantRequestCancel>
47
+ ),
48
+ };
49
+
50
+ const dropdownStates = {
51
+ pending: {
52
+ color: "yellow",
53
+ options: [actions["viewRequest"], actions["cancelRequest"]],
54
+ header: "request.grantRemoval.state.pending.description",
55
+ },
56
+ rejected: {
57
+ color: "red",
58
+ options: [actions["viewRequest"]],
59
+ header: "request.grantRemoval.state.rejected.description",
60
+ },
61
+ processing: {
62
+ color: "grey",
63
+ options: [actions["viewRequest"]],
64
+ header: "request.grantRemoval.state.processing.description",
65
+ },
66
+ approved: {
67
+ color: "brown",
68
+ options: [actions["viewRequest"]],
69
+ header:
70
+ "request.grantRemoval.state.approvedAndGrantPendingRemoval.description",
71
+ },
72
+ workflowBypassedActive: {
73
+ color: "grey",
74
+ options: [actions["viewRequest"]],
75
+ header: "request.grantRemoval.state.workflowBypassedActive.description",
76
+ },
77
+ workflowBypassedPendingRemoval: {
78
+ color: "grey",
79
+ options: [actions["viewRequest"]],
80
+ header:
81
+ "request.grantRemoval.state.workflowBypassedPendingRemoval.description",
82
+ },
83
+ };
84
+
85
+ const currentState = (() => {
86
+ if (!grant.pending_removal) {
87
+ if (["pending", "rejected", "processing"].includes(grantRequestStatus))
88
+ return grantRequestStatus;
89
+ else return "workflowBypassedActive";
90
+ } else {
91
+ if (grantRequestStatus === "approved") return "approved";
92
+ else return "workflowBypassedPendingRemoval";
93
+ }
94
+ })();
95
+
96
+ const stateProps = dropdownStates[currentState];
97
+ const grantRequestDetails = grantRequestStatus ? (
98
+ <>{formatMessage({ id: `grantRequest.content.${grantRequestStatus}` })}</>
99
+ ) : null;
100
+ const popupContent = (
101
+ <>
102
+ {stateProps?.header ? (
103
+ <Header as="h6">{formatMessage({ id: stateProps.header })}</Header>
104
+ ) : null}
105
+ {!["workflowBypassedActive", "workflowBypassedPendingRemoval"].includes(
106
+ currentState
107
+ ) && latestGrantRequest
108
+ ? grantRequestDetails
109
+ : null}
110
+ </>
111
+ );
112
+
113
+ const dropdown = (
114
+ <Dropdown
115
+ icon={null}
116
+ floating
117
+ direction="left"
118
+ trigger={
119
+ <Button
120
+ color={stateProps?.color}
121
+ className="button basic icon group-actions"
122
+ icon="shield"
123
+ />
124
+ }
125
+ >
126
+ <Dropdown.Menu>
127
+ <>{stateProps?.options}</>
128
+ </Dropdown.Menu>
129
+ </Dropdown>
130
+ );
131
+ return currentState ? (
132
+ <Popup content={popupContent} trigger={dropdown} />
133
+ ) : (
134
+ <p>Error: undefined currentState</p>
135
+ );
136
+ };
137
+
138
+ GrantRemovalWorkflowDropdown.propTypes = {
139
+ latestGrantRequest: PropTypes.object,
140
+ setLatestGrantRequest: PropTypes.func,
141
+ grant: PropTypes.object,
142
+ };
143
+
144
+ export default GrantRemovalWorkflowDropdown;
@@ -4,7 +4,7 @@ import PropTypes from "prop-types";
4
4
  import { connect } from "react-redux";
5
5
  import { Link } from "react-router-dom";
6
6
  import { linkTo } from "@truedat/core/routes";
7
- import { useIntl } from "react-intl";
7
+ import { FormattedMessage, useIntl } from "react-intl";
8
8
  import { Grid, Segment, List, Label } from "semantic-ui-react";
9
9
  import GenericCrumbs from "@truedat/core/components/GenericCrumbs";
10
10
  import { getGrantRequestTemplate } from "../selectors";
@@ -19,6 +19,7 @@ const ConditionSummary = React.lazy(() =>
19
19
  import("@truedat/dq/components/ConditionSummary")
20
20
  );
21
21
 
22
+
22
23
  export const GrantRequest = ({ grantRequest, template }) => {
23
24
  const { formatMessage } = useIntl();
24
25
  const metadata = grantRequest?.metadata;
@@ -8,6 +8,7 @@ import { createGrantRequestStatus } from "../routines";
8
8
  export const GrantRequestCancel = ({
9
9
  structureId,
10
10
  grantRequest,
11
+ setLatestGrantRequest,
11
12
  createGrantRequestStatus,
12
13
  children,
13
14
  previousStructureQuery,
@@ -27,7 +28,8 @@ export const GrantRequestCancel = ({
27
28
  onConfirm={() =>
28
29
  createGrantRequestStatus({
29
30
  structureId,
30
- grantRequestId: grantRequest.id,
31
+ grantRequest,
32
+ setLatestGrantRequest,
31
33
  status: "cancelled",
32
34
  previousStructureQuery,
33
35
  })
@@ -44,6 +46,7 @@ GrantRequestCancel.propTypes = {
44
46
  createGrantRequestStatus: PropTypes.func,
45
47
  children: PropTypes.node,
46
48
  previousStructureQuery: PropTypes.object,
49
+ setLatestGrantRequest: PropTypes.func,
47
50
  };
48
51
 
49
- export default connect(null, { createGrantRequestStatus })(GrantRequestCancel);
52
+ export default connect(null, { createGrantRequestStatus })(GrantRequestCancel);
@@ -6,12 +6,20 @@ import { useIntl } from "react-intl";
6
6
  import { Header, Grid, Icon } from "semantic-ui-react";
7
7
  import GrantRequestApprovalPopup from "./GrantRequestApprovalPopup";
8
8
 
9
- export const GrantRequestHeader = ({ grantRequest }) => {
10
- const isModification = _.prop("_embedded.group._embedded.modification_grant")(
11
- grantRequest
12
- );
9
+ const selectGrantTypeOptions = (grantRequest) => {
10
+ const [color, translationId] =
11
+ grantRequest?.request_type === "grant_removal"
12
+ ? ["red", "request.grantRemoval"]
13
+ : grantRequest?._embedded?.group?._embedded.modification_grant
14
+ ? ["yellow", "grantRequest.header.modification"]
15
+ : [undefined, "request.grantAccess"];
16
+ return { color, translationId };
17
+ };
13
18
 
19
+ export const GrantRequestHeader = ({ grantRequest }) => {
14
20
  const { formatMessage } = useIntl();
21
+ const { translationId: grantTypeTranslationId } =
22
+ selectGrantTypeOptions(grantRequest);
15
23
  const canApprove =
16
24
  grantRequest?.status === "pending" &&
17
25
  !_.isEmpty(grantRequest?.pending_roles);
@@ -22,9 +30,7 @@ export const GrantRequestHeader = ({ grantRequest }) => {
22
30
  <Icon circular name="key" />
23
31
  <Header.Content>
24
32
  {formatMessage({
25
- id: isModification
26
- ? "grantRequest.header.modification"
27
- : "grantRequest.header",
33
+ id: grantTypeTranslationId,
28
34
  })}
29
35
  </Header.Content>
30
36
  </Header>
@@ -40,7 +40,14 @@ export const GrantRequestRow = ({
40
40
  <Table.Cell
41
41
  key={key}
42
42
  textAlign={column.textAlign}
43
- onClick={() => history.push(linkTo.GRANT_REQUEST(grantRequest))}
43
+ onClick={(ev) => {
44
+ /* Only do history.push if the table cell but not
45
+ * some possibly contained anchor (or an element
46
+ * inside an anchor, such as an icon) is clicked
47
+ */
48
+ !ev.target.closest("a") &&
49
+ history.push(linkTo.GRANT_REQUEST(grantRequest))
50
+ }}
44
51
  content={columnDecoratorComponent(column)(grantRequest)}
45
52
  />
46
53
  ))}
@@ -51,9 +51,14 @@ export const GrantRequests = ({
51
51
  <Table.Cell
52
52
  key={key}
53
53
  textAlign={column.textAlign}
54
- onClick={() =>
55
- history.push(linkTo.GRANT_REQUEST(grantRequest))
56
- }
54
+ onClick={(ev) => {
55
+ /* Only do history.push if the table cell but not
56
+ * some possibly contained anchor (or an element
57
+ * inside an anchor, such as an icon) is clicked
58
+ */
59
+ !ev.target.closest("a") &&
60
+ history.push(linkTo.GRANT_REQUEST(grantRequest));
61
+ }}
57
62
  content={columnDecoratorComponent(column)(grantRequest)}
58
63
  />
59
64
  ))}
@@ -40,7 +40,7 @@ export const GrantRequestsTable = ({
40
40
  return (
41
41
  <>
42
42
  {!_.isEmpty(grantRequests) ? (
43
- <Table sortable>
43
+ <Table sortable selectable>
44
44
  <Table.Header>
45
45
  <Table.Row>
46
46
  {roleSelected ? (