@truedat/dd 8.2.3 → 8.3.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 +3 -3
- package/src/api.js +2 -0
- package/src/components/GrantRequestCartResult.js +132 -0
- package/src/components/GrantRoutes.js +5 -16
- package/src/components/StructureGrantCart.js +11 -6
- package/src/components/StructureGrantCartCheckout.js +357 -40
- package/src/components/StructureGrantCartInformation.js +6 -0
- package/src/components/StructureGrantCartUserSelector.js +23 -8
- package/src/components/__tests__/GrantRequestCartResult.spec.js +79 -0
- package/src/components/__tests__/StructureGrantCartCheckout.spec.js +110 -8
- package/src/components/__tests__/__snapshots__/GrantRequestCartResult.spec.js.snap +179 -0
- package/src/components/__tests__/__snapshots__/StructureGrantCartCheckout.spec.js.snap +294 -1
- package/src/hooks/useCheckoutGrantRequest.js +16 -0
- package/src/hooks/useGrantRequestGroupsByIds.js +22 -0
- package/src/hooks/useSystems.js +27 -0
- package/src/hooks/useTemplatesByIds.js +66 -0
- package/src/reducers/__tests__/grantRequestsCart.spec.js +70 -3
- package/src/reducers/grantRequestsCart.js +35 -1
- package/src/reducers/structureRedirect.js +11 -1
- package/src/routines.js +7 -0
- package/src/sagas/index.js +0 -3
- package/src/styles/grantCartUserSelector.less +3 -0
- package/src/sagas/operateGrantRequestCart.js +0 -26
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
3
|
+
import { apiJson } from "@truedat/core/services/api";
|
|
4
|
+
|
|
5
|
+
export const normalizeTemplate = (raw) => {
|
|
6
|
+
if (!raw || typeof raw !== "object") return null;
|
|
7
|
+
const inner = raw?.data && typeof raw.data === "object" ? raw.data : raw;
|
|
8
|
+
const content = _.isArray(inner?.content)
|
|
9
|
+
? inner.content
|
|
10
|
+
: _.isArray(inner?.template_content)
|
|
11
|
+
? inner.template_content
|
|
12
|
+
: [];
|
|
13
|
+
return { ...inner, content };
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const ensureTemplateShape = (t) => {
|
|
17
|
+
if (!t) return null;
|
|
18
|
+
return t && _.isArray(t.content)
|
|
19
|
+
? t
|
|
20
|
+
: { ...t, content: _.isArray(t.template_content) ? t.template_content : [] };
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const useTemplatesByIds = (ids) => {
|
|
24
|
+
const [templatesById, setTemplatesById] = useState({});
|
|
25
|
+
const idsKey =
|
|
26
|
+
Array.isArray(ids) && ids.length > 0 ? ids.join(",") : "";
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (!idsKey) {
|
|
30
|
+
setTemplatesById({});
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const idList = idsKey
|
|
34
|
+
.split(",")
|
|
35
|
+
.map((s) => parseInt(s.trim(), 10))
|
|
36
|
+
.filter((n) => !Number.isNaN(n));
|
|
37
|
+
if (idList.length === 0) {
|
|
38
|
+
setTemplatesById({});
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
let cancelled = false;
|
|
42
|
+
Promise.all(
|
|
43
|
+
idList.map((id) =>
|
|
44
|
+
apiJson(`/api/templates/${id}`).then((res) => ({
|
|
45
|
+
id,
|
|
46
|
+
raw: res?.data ?? res,
|
|
47
|
+
}))
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
.then((results) => {
|
|
51
|
+
if (cancelled) return;
|
|
52
|
+
const map = {};
|
|
53
|
+
results.forEach(({ id, raw }) => {
|
|
54
|
+
const t = ensureTemplateShape(normalizeTemplate(raw) ?? raw);
|
|
55
|
+
if (t) map[id] = t;
|
|
56
|
+
});
|
|
57
|
+
setTemplatesById(map);
|
|
58
|
+
})
|
|
59
|
+
.catch(() => {});
|
|
60
|
+
return () => {
|
|
61
|
+
cancelled = true;
|
|
62
|
+
};
|
|
63
|
+
}, [idsKey]);
|
|
64
|
+
|
|
65
|
+
return templatesById;
|
|
66
|
+
};
|
|
@@ -6,10 +6,13 @@ import {
|
|
|
6
6
|
updateGrantRequestFilter,
|
|
7
7
|
updateGrantRequestTemplateContent,
|
|
8
8
|
checkoutGrantRequest,
|
|
9
|
+
initGroupTemplates,
|
|
10
|
+
selectGrantRequestTemplateForGroup,
|
|
11
|
+
updateGrantRequestTemplateContentForGroup,
|
|
9
12
|
} from "../../routines";
|
|
10
13
|
import { initialState, grantRequestsCart } from "../grantRequestsCart";
|
|
11
14
|
|
|
12
|
-
describe("reducers:
|
|
15
|
+
describe("reducers: grantRequestsCart", () => {
|
|
13
16
|
it("should provide the initial state", () => {
|
|
14
17
|
expect(grantRequestsCart(undefined, {})).toEqual(initialState);
|
|
15
18
|
});
|
|
@@ -324,10 +327,74 @@ describe("reducers: structureVersion", () => {
|
|
|
324
327
|
).toEqual(expectedState);
|
|
325
328
|
});
|
|
326
329
|
|
|
327
|
-
it("checkoutGrantRequest.SUCCESS should return the initialState", () => {
|
|
330
|
+
it("checkoutGrantRequest.SUCCESS should return the initialState and clear groupTemplates", () => {
|
|
328
331
|
expect(
|
|
329
332
|
grantRequestsCart({ foo: "bar" }, { type: checkoutGrantRequest.SUCCESS })
|
|
330
|
-
).toEqual(initialState);
|
|
333
|
+
).toEqual({ ...initialState, groupTemplates: {} });
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it("should handle initGroupTemplates.TRIGGER", () => {
|
|
337
|
+
const payload = {
|
|
338
|
+
"123": { template: "Template A", templateContent: {}, valid: false },
|
|
339
|
+
others: { template: null, templateContent: {}, valid: false },
|
|
340
|
+
};
|
|
341
|
+
const expectedState = {
|
|
342
|
+
...initialState,
|
|
343
|
+
groupTemplates: payload,
|
|
344
|
+
};
|
|
345
|
+
expect(
|
|
346
|
+
grantRequestsCart(initialState, {
|
|
347
|
+
type: initGroupTemplates.TRIGGER,
|
|
348
|
+
payload,
|
|
349
|
+
})
|
|
350
|
+
).toEqual(expectedState);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it("should handle selectGrantRequestTemplateForGroup.TRIGGER", () => {
|
|
354
|
+
const stateWithGroups = {
|
|
355
|
+
...initialState,
|
|
356
|
+
groupTemplates: {
|
|
357
|
+
"123": { template: null, templateContent: {}, valid: false },
|
|
358
|
+
},
|
|
359
|
+
};
|
|
360
|
+
const expectedState = {
|
|
361
|
+
...stateWithGroups,
|
|
362
|
+
groupTemplates: {
|
|
363
|
+
"123": { template: "My Template", templateContent: {}, valid: false },
|
|
364
|
+
},
|
|
365
|
+
};
|
|
366
|
+
expect(
|
|
367
|
+
grantRequestsCart(stateWithGroups, {
|
|
368
|
+
type: selectGrantRequestTemplateForGroup.TRIGGER,
|
|
369
|
+
payload: { key: "123", template: "My Template" },
|
|
370
|
+
})
|
|
371
|
+
).toEqual(expectedState);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it("should handle updateGrantRequestTemplateContentForGroup.TRIGGER", () => {
|
|
375
|
+
const stateWithGroups = {
|
|
376
|
+
...initialState,
|
|
377
|
+
groupTemplates: {
|
|
378
|
+
"123": { template: "T", templateContent: {}, valid: false },
|
|
379
|
+
},
|
|
380
|
+
};
|
|
381
|
+
const content = { field1: { value: "x", origin: "user" } };
|
|
382
|
+
const expectedState = {
|
|
383
|
+
...stateWithGroups,
|
|
384
|
+
groupTemplates: {
|
|
385
|
+
"123": {
|
|
386
|
+
template: "T",
|
|
387
|
+
templateContent: content,
|
|
388
|
+
valid: true,
|
|
389
|
+
},
|
|
390
|
+
},
|
|
391
|
+
};
|
|
392
|
+
expect(
|
|
393
|
+
grantRequestsCart(stateWithGroups, {
|
|
394
|
+
type: updateGrantRequestTemplateContentForGroup.TRIGGER,
|
|
395
|
+
payload: { key: "123", content, valid: [] },
|
|
396
|
+
})
|
|
397
|
+
).toEqual(expectedState);
|
|
331
398
|
});
|
|
332
399
|
|
|
333
400
|
it("should ignore unknown actions", () => {
|
|
@@ -3,11 +3,14 @@ import { dropAt, replaceAt } from "@truedat/core/services/arrays";
|
|
|
3
3
|
import {
|
|
4
4
|
addGrantRequestToCart,
|
|
5
5
|
checkoutGrantRequest,
|
|
6
|
+
initGroupTemplates,
|
|
6
7
|
removeGrantRequestFromCart,
|
|
7
8
|
selectGrantRequestTemplate,
|
|
9
|
+
selectGrantRequestTemplateForGroup,
|
|
8
10
|
setGrantChangeRequestCart,
|
|
9
11
|
updateGrantRequestFilter,
|
|
10
12
|
updateGrantRequestTemplateContent,
|
|
13
|
+
updateGrantRequestTemplateContentForGroup,
|
|
11
14
|
updateGrantRequestUser,
|
|
12
15
|
} from "../routines";
|
|
13
16
|
import { formatLegacyContent } from "@truedat/df/utils";
|
|
@@ -101,8 +104,39 @@ const grantRequestsCart = (state = initialState, { type, payload }) => {
|
|
|
101
104
|
: s
|
|
102
105
|
)(state.structures),
|
|
103
106
|
};
|
|
107
|
+
case initGroupTemplates.TRIGGER:
|
|
108
|
+
return {
|
|
109
|
+
...state,
|
|
110
|
+
groupTemplates: payload ?? {},
|
|
111
|
+
};
|
|
112
|
+
case selectGrantRequestTemplateForGroup.TRIGGER: {
|
|
113
|
+
const { key, template } = payload;
|
|
114
|
+
const groupTemplates = _.getOr({}, "groupTemplates", state);
|
|
115
|
+
return {
|
|
116
|
+
...state,
|
|
117
|
+
groupTemplates: {
|
|
118
|
+
...groupTemplates,
|
|
119
|
+
[key]: { ...groupTemplates[key], template },
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
case updateGrantRequestTemplateContentForGroup.TRIGGER: {
|
|
124
|
+
const { key, content, valid } = payload;
|
|
125
|
+
const groupTemplates = _.getOr({}, "groupTemplates", state);
|
|
126
|
+
return {
|
|
127
|
+
...state,
|
|
128
|
+
groupTemplates: {
|
|
129
|
+
...groupTemplates,
|
|
130
|
+
[key]: {
|
|
131
|
+
...groupTemplates[key],
|
|
132
|
+
templateContent: content,
|
|
133
|
+
valid: _.size(valid) === 0,
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
104
138
|
case checkoutGrantRequest.SUCCESS:
|
|
105
|
-
return initialState;
|
|
139
|
+
return { ...initialState, groupTemplates: {} };
|
|
106
140
|
default:
|
|
107
141
|
return state;
|
|
108
142
|
}
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
BUCKET_VIEW_CONFIGS,
|
|
5
5
|
GRANT_REQUESTS,
|
|
6
6
|
GRANT_APPROVAL_RULES,
|
|
7
|
+
GRANTS_REQUESTS_RESULT,
|
|
7
8
|
MY_GRANT_REQUESTS,
|
|
8
9
|
REFERENCE_DATASETS,
|
|
9
10
|
STRUCTURE_TAGS,
|
|
@@ -61,7 +62,16 @@ export const structureRedirect = (
|
|
|
61
62
|
return linkTo.SYSTEM_STRUCTURES({ id });
|
|
62
63
|
}
|
|
63
64
|
case checkoutGrantRequest.SUCCESS: {
|
|
64
|
-
const
|
|
65
|
+
const bulkGroups =
|
|
66
|
+
_.path("data.grant_request_groups")(payload) ??
|
|
67
|
+
_.get("grant_request_groups", payload);
|
|
68
|
+
if (_.isArray(bulkGroups) && !_.isEmpty(bulkGroups)) {
|
|
69
|
+
const ids = bulkGroups.map((g) => g.id).join("-");
|
|
70
|
+
return `${GRANTS_REQUESTS_RESULT}/${ids}`;
|
|
71
|
+
}
|
|
72
|
+
const requests =
|
|
73
|
+
_.path("data._embedded.requests")(payload) ??
|
|
74
|
+
_.get("_embedded.requests", payload);
|
|
65
75
|
return _.size(requests) === 1
|
|
66
76
|
? linkTo.GRANT_REQUEST({ id: requests[0].id })
|
|
67
77
|
: MY_GRANT_REQUESTS;
|
package/src/routines.js
CHANGED
|
@@ -133,6 +133,13 @@ export const selectGrantPage = createRoutine("SELECT_GRANT_PAGE");
|
|
|
133
133
|
export const selectGrantRequestTemplate = createRoutine(
|
|
134
134
|
"SELECT_GRANT_REQUEST_TEMPLATE"
|
|
135
135
|
);
|
|
136
|
+
export const initGroupTemplates = createRoutine("INIT_GROUP_TEMPLATES");
|
|
137
|
+
export const selectGrantRequestTemplateForGroup = createRoutine(
|
|
138
|
+
"SELECT_GRANT_REQUEST_TEMPLATE_FOR_GROUP"
|
|
139
|
+
);
|
|
140
|
+
export const updateGrantRequestTemplateContentForGroup = createRoutine(
|
|
141
|
+
"UPDATE_GRANT_REQUEST_TEMPLATE_CONTENT_FOR_GROUP"
|
|
142
|
+
);
|
|
136
143
|
export const selectGroup = createRoutine("SELECT_GROUP");
|
|
137
144
|
export const selectNode = createRoutine("SELECT_NODE");
|
|
138
145
|
export const selectPath = createRoutine("SELECT_PATH");
|
package/src/sagas/index.js
CHANGED
|
@@ -42,7 +42,6 @@ import { fetchSystemStructuresRequestSaga } from "./fetchSystemStructures";
|
|
|
42
42
|
import { fetchUserSearchFiltersRequestSaga } from "./fetchUserSearchFilters";
|
|
43
43
|
import { linkStructureToStructureRequestSaga } from "./linkStructureToStructure";
|
|
44
44
|
import { tagStructureRequestSaga } from "./tagStructure";
|
|
45
|
-
import { operateGrantRequestToCartRequestSaga } from "./operateGrantRequestCart";
|
|
46
45
|
import { requestGrantRemovalRequestSaga } from "./requestGrantRemoval";
|
|
47
46
|
import { saveUserSearchFiltersRequestSaga } from "./saveUserSearchFilters";
|
|
48
47
|
import { updateGrantApprovalRuleRequestSaga } from "./updateGrantApprovalRule";
|
|
@@ -99,7 +98,6 @@ export {
|
|
|
99
98
|
linkStructureToStructureRequestSaga,
|
|
100
99
|
legacyFetchStructureRequestSaga,
|
|
101
100
|
tagStructureRequestSaga,
|
|
102
|
-
operateGrantRequestToCartRequestSaga,
|
|
103
101
|
requestGrantRemovalRequestSaga,
|
|
104
102
|
saveUserSearchFiltersRequestSaga,
|
|
105
103
|
updateReferenceDatasetRequestSaga,
|
|
@@ -157,7 +155,6 @@ export default [
|
|
|
157
155
|
linkStructureToStructureRequestSaga(),
|
|
158
156
|
legacyFetchStructureRequestSaga(),
|
|
159
157
|
tagStructureRequestSaga(),
|
|
160
|
-
operateGrantRequestToCartRequestSaga(),
|
|
161
158
|
requestGrantRemovalRequestSaga(),
|
|
162
159
|
saveUserSearchFiltersRequestSaga(),
|
|
163
160
|
updateGrantApprovalRuleRequestSaga(),
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { call, put, takeLatest } from "redux-saga/effects";
|
|
2
|
-
import { apiJsonPost, JSON_OPTS } from "@truedat/core/services/api";
|
|
3
|
-
import { checkoutGrantRequest } from "../routines";
|
|
4
|
-
import { API_GRANT_REQUEST_GROUP } from "../api";
|
|
5
|
-
|
|
6
|
-
export function* checkoutGrantRequestSaga({ payload }) {
|
|
7
|
-
try {
|
|
8
|
-
const url = API_GRANT_REQUEST_GROUP;
|
|
9
|
-
yield put(checkoutGrantRequest.request());
|
|
10
|
-
const { data } = yield call(apiJsonPost, url, payload, JSON_OPTS);
|
|
11
|
-
yield put(checkoutGrantRequest.success(data));
|
|
12
|
-
} catch (error) {
|
|
13
|
-
if (error.response) {
|
|
14
|
-
const { status, data } = error.response;
|
|
15
|
-
yield put(checkoutGrantRequest.failure({ status, data }));
|
|
16
|
-
} else {
|
|
17
|
-
yield put(checkoutGrantRequest.failure(error.message));
|
|
18
|
-
}
|
|
19
|
-
} finally {
|
|
20
|
-
yield put(checkoutGrantRequest.fulfill());
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function* operateGrantRequestToCartRequestSaga() {
|
|
25
|
-
yield takeLatest(checkoutGrantRequest.TRIGGER, checkoutGrantRequestSaga);
|
|
26
|
-
}
|