@truedat/dd 8.2.4 → 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.
@@ -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: structureVersion", () => {
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 requests = _.path("data._embedded.requests")(payload);
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");
@@ -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(),
@@ -0,0 +1,3 @@
1
+ .grant-cart-user-selector-label-spaced {
2
+ margin-right: 1em;
3
+ }
@@ -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
- }