@truedat/dd 8.7.2 → 8.7.4

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/dd",
3
- "version": "8.7.2",
3
+ "version": "8.7.4",
4
4
  "description": "Truedat Web Data Dictionary",
5
5
  "sideEffects": false,
6
6
  "module": "src/index.js",
@@ -51,7 +51,7 @@
51
51
  "@testing-library/jest-dom": "^6.6.3",
52
52
  "@testing-library/react": "^16.3.0",
53
53
  "@testing-library/user-event": "^14.6.1",
54
- "@truedat/test": "8.7.2",
54
+ "@truedat/test": "8.7.4",
55
55
  "identity-obj-proxy": "^3.0.0",
56
56
  "jest": "^29.7.0",
57
57
  "redux-saga-test-plan": "^4.0.6"
@@ -86,5 +86,5 @@
86
86
  "svg-pan-zoom": "^3.6.2",
87
87
  "swr": "^2.3.3"
88
88
  },
89
- "gitHead": "e971cd8773dc15ad949f260abbfe4d3236473af7"
89
+ "gitHead": "74bf59ddf29e05b518dc206271c4f1215d66a942"
90
90
  }
@@ -75,11 +75,11 @@ const getGroupedStructuresByDomainAndTemplate = (
75
75
 
76
76
  const templateId = isExplicitSystem
77
77
  ? (systemToTemplateId[normalizedSystemIdStr] ?? defaultTemplateId)
78
- : defaultTemplateId;
78
+ : null;
79
79
 
80
80
  const shouldGroupByDomain = isExplicitSystem
81
81
  ? systemGroupByDomain[normalizedSystemIdStr] === true
82
- : defaultGroupByDomain === true && defaultTemplateId != null;
82
+ : defaultGroupByDomain === true;
83
83
 
84
84
  const systemKey = isExplicitSystem ? normalizedSystemIdStr : "others";
85
85
 
@@ -161,16 +161,16 @@ export const StructureGrantCartCheckout = ({
161
161
 
162
162
  const getTemplateIdFromKey = (key) => {
163
163
  const parts = key.split("_");
164
- const templatePart = parts[2];
164
+ const templatePart = parts[parts.length - 1];
165
165
  if (templatePart === "notype" || !templatePart) {
166
- return defaultTemplateId;
166
+ return null;
167
167
  }
168
168
  return Number(templatePart) || templatePart;
169
169
  };
170
170
 
171
171
  const getDomainIdsFromKey = (key) => {
172
172
  const parts = key.split("_");
173
- const domainPart = parts[0];
173
+ const domainPart = parts.slice(0, -2).join("_");
174
174
  if (domainPart === "nodomain" || !domainPart) {
175
175
  return null;
176
176
  }
@@ -240,7 +240,7 @@ export const StructureGrantCartCheckout = ({
240
240
  k,
241
241
  {
242
242
  template: undefined,
243
- templateContent: {},
243
+ templateContent: undefined,
244
244
  valid: false,
245
245
  },
246
246
  ]),
@@ -256,20 +256,13 @@ export const StructureGrantCartCheckout = ({
256
256
  if (!allLoaded) return;
257
257
  const getTemplateById = (tid) =>
258
258
  tid != null ? (templatesById[String(tid)] ?? templatesById[tid]) : null;
259
- const initial = _.fromPairs(
260
- groupKeys.map((k) => {
261
- const t = getTemplateById(effectiveTemplateIdByKey[k])?.name;
262
- return [
263
- k,
264
- {
265
- template: t ?? undefined,
266
- templateContent: {},
267
- valid: false,
268
- },
269
- ];
270
- }),
271
- );
272
- initGroupTemplates(initial);
259
+ groupKeys.forEach((k) => {
260
+ const templateObj = getTemplateById(effectiveTemplateIdByKey[k]);
261
+ const t = templateObj?.name;
262
+ if (t) {
263
+ selectGrantRequestTemplateForGroup({ key: k, template: t });
264
+ }
265
+ });
273
266
  }, [
274
267
  isMultiGroup,
275
268
  groupKeys.join(","),
@@ -277,7 +270,7 @@ export const StructureGrantCartCheckout = ({
277
270
  defaultTemplateId,
278
271
  templatesById,
279
272
  effectiveTemplateIdByKey,
280
- initGroupTemplates,
273
+ selectGrantRequestTemplateForGroup,
281
274
  ]);
282
275
 
283
276
  useEffect(() => {
@@ -430,6 +423,7 @@ export const StructureGrantCartCheckout = ({
430
423
  const groupDomainIds = getUniqueDomainIds(groupStructures);
431
424
  const groupData = groupTemplates[key] ?? {};
432
425
  const selectedTemplate = systemTemplates[key];
426
+ const autoTemplateName = selectedTemplate?.name;
433
427
  const domainName = domainNamesByKey[key];
434
428
  const systemNames = getUniqueSystemName(groupStructures);
435
429
 
@@ -462,7 +456,7 @@ export const StructureGrantCartCheckout = ({
462
456
  >
463
457
  {formatMessage({ id: systemMessageId })}:{" "}
464
458
  <span style={{ fontWeight: "normal" }}>
465
- {key.split("_")[1] === "others"
459
+ {key.split("_").at(-2) === "others"
466
460
  ? formatMessage({
467
461
  id: "grantRequest.cart.group.others",
468
462
  })
@@ -494,11 +488,11 @@ export const StructureGrantCartCheckout = ({
494
488
  <Suspense fallback={null}>
495
489
  <SelectableDynamicForm
496
490
  scope="gr"
497
- content={groupData.templateContent ?? {}}
491
+ content={groupData.templateContent ?? null}
498
492
  domainIds={groupDomainIds}
499
493
  hideLabel
500
494
  isModification={!!modificationGrant}
501
- selectedTemplate={selectedTemplate}
495
+ name={autoTemplateName}
502
496
  disableSelector={!!selectedTemplate}
503
497
  onChange={({ content, valid }) =>
504
498
  updateGrantRequestTemplateContentForGroup({
@@ -536,7 +530,7 @@ export const StructureGrantCartCheckout = ({
536
530
  <Grid.Column width={5}>
537
531
  <StructureGrantCartInformation
538
532
  isModification={!!modificationGrant}
539
- selectedTemplate={singleSystemTemplate}
533
+ name={singleSystemTemplate?.name}
540
534
  disableSelector={!!singleSystemTemplate}
541
535
  />
542
536
  </Grid.Column>
@@ -26,6 +26,7 @@ export const StructureGrantCartInformation = ({
26
26
  updateGrantRequestTemplateContent,
27
27
  selectedTemplate,
28
28
  disableSelector,
29
+ name: autoTemplateName,
29
30
  }) => {
30
31
  const { formatMessage } = useIntl();
31
32
  const templateContent = _.pathOr({}, "templateContent")(grantRequestsCart);
@@ -36,6 +37,7 @@ export const StructureGrantCartInformation = ({
36
37
  fetchPolicy: "cache-and-network",
37
38
  variables: { action: "createForeignGrantRequest" },
38
39
  });
40
+
39
41
  return (
40
42
  <Segment loading={loading}>
41
43
  <Form>
@@ -68,7 +70,7 @@ export const StructureGrantCartInformation = ({
68
70
  id: `structure.grant.cart.template.placeholder`,
69
71
  })}
70
72
  required
71
- selectedTemplate={selectedTemplate}
73
+ name={autoTemplateName}
72
74
  disableSelector={disableSelector}
73
75
  />
74
76
  </Form>
@@ -83,6 +85,7 @@ StructureGrantCartInformation.propTypes = {
83
85
  isModification: PropTypes.bool,
84
86
  selectedTemplate: PropTypes.object,
85
87
  disableSelector: PropTypes.bool,
88
+ name: PropTypes.string,
86
89
  };
87
90
 
88
91
  const mapStateToProps = ({ grantRequestsCart }) => ({ grantRequestsCart });
@@ -357,4 +357,44 @@ describe("StructureGrantCartCheckout", () => {
357
357
  await waitForLoad(rendered);
358
358
  expect(rendered.container).toMatchSnapshot();
359
359
  });
360
+
361
+ it("calls selectGrantRequestTemplateForGroup (not initGroupTemplates) when templates loaded", async () => {
362
+ useWebContext.mockReturnValue({ scopesWithRelations: ["gr"] });
363
+ useTemplateRelations.mockReturnValue({
364
+ resource: [
365
+ { resource_id: 10, template_id: 1 },
366
+ { resource_id: 20, template_id: 2 },
367
+ ],
368
+ });
369
+ useTemplatesByIds.mockReturnValue({
370
+ 1: { name: "Template A", content: [] },
371
+ 2: { name: "Template B", content: [] },
372
+ });
373
+ const props = {
374
+ ...defaultProps,
375
+ grantRequestsCart: grantRequestsCartMultiGroup,
376
+ };
377
+ const renderOpts = {
378
+ state: { grantRequestsCart: grantRequestsCartMultiGroup },
379
+ mocks: [
380
+ singleTemplateMock({ scope: "gr" }),
381
+ domainsMock({ action: "createForeignGrantRequest" }),
382
+ ],
383
+ };
384
+ const rendered = render(
385
+ <StructureGrantCartCheckout {...props} />,
386
+ renderOpts,
387
+ );
388
+ await waitForLoad(rendered);
389
+ expect(mockSelectGrantRequestTemplateForGroup).toHaveBeenCalledTimes(2);
390
+ expect(mockSelectGrantRequestTemplateForGroup).toHaveBeenCalledWith({
391
+ key: "nodomain_10_1",
392
+ template: "Template A",
393
+ });
394
+ expect(mockSelectGrantRequestTemplateForGroup).toHaveBeenCalledWith({
395
+ key: "nodomain_20_2",
396
+ template: "Template B",
397
+ });
398
+ expect(mockInitGroupTemplates).not.toHaveBeenCalled();
399
+ });
360
400
  });
@@ -1,8 +1,10 @@
1
1
  import _ from "lodash/fp";
2
- import { useState, useEffect } from "react";
2
+ import { useState, useEffect, useMemo } from "react";
3
+ import { useQuery } from "@apollo/client";
4
+ import { TEMPLATES_QUERY } from "@truedat/core/api/queries";
3
5
  import { apiJson } from "@truedat/core/services/api";
4
6
 
5
- export const normalizeTemplate = (raw) => {
7
+ const normalizeTemplate = (raw) => {
6
8
  if (!raw || typeof raw !== "object") return null;
7
9
  const inner = raw?.data && typeof raw.data === "object" ? raw.data : raw;
8
10
  const content = _.isArray(inner?.content)
@@ -23,25 +25,56 @@ const ensureTemplateShape = (t) => {
23
25
  };
24
26
  };
25
27
 
26
- export const useTemplatesByIds = (ids) => {
27
- const [templatesById, setTemplatesById] = useState({});
28
+ const TEMPLATES_SCOPE = "gr";
29
+
30
+ const parseIdList = _.flow(
31
+ _.split(","),
32
+ _.map((s) => parseInt(s.trim(), 10)),
33
+ _.reject(Number.isNaN),
34
+ );
35
+
36
+ export const useTemplatesByIds = (ids, domainIds) => {
37
+ const [restTemplates, setRestTemplates] = useState({});
28
38
  const idsKey = Array.isArray(ids) && ids.length > 0 ? ids.join(",") : "";
39
+ const domainIdsKey =
40
+ Array.isArray(domainIds) && domainIds.length > 0 ? domainIds.join(",") : "";
41
+ const hasIds = !!idsKey;
42
+ const hasDomainIds = !!domainIdsKey;
43
+
44
+ const { data } = useQuery(TEMPLATES_QUERY, {
45
+ variables: { scope: TEMPLATES_SCOPE, domainIds },
46
+ skip: !hasIds || !hasDomainIds,
47
+ fetchPolicy: "cache-and-network",
48
+ });
49
+
50
+ const preprocessedTemplates = useMemo(() => {
51
+ if (!data?.templates || !hasIds) return null;
52
+ const idList = parseIdList(idsKey);
53
+ if (idList.length === 0) return null;
54
+ const map = _.reduce(
55
+ (acc, id) => {
56
+ const t = data.templates.find((tpl) => String(tpl.id) === String(id));
57
+ return t ? _.set(id, t, acc) : acc;
58
+ },
59
+ {},
60
+ idList,
61
+ );
62
+ return map;
63
+ }, [data, idsKey, hasIds]);
29
64
 
30
65
  useEffect(() => {
31
- if (!idsKey) {
32
- setTemplatesById({});
66
+ if (!hasIds || hasDomainIds) {
67
+ setRestTemplates({});
33
68
  return;
34
69
  }
35
- const idList = idsKey
36
- .split(",")
37
- .map((s) => parseInt(s.trim(), 10))
38
- .filter((n) => !Number.isNaN(n));
70
+ const idList = parseIdList(idsKey);
39
71
  if (idList.length === 0) {
40
- setTemplatesById({});
72
+ setRestTemplates({});
41
73
  return;
42
74
  }
43
75
  // eslint-disable-next-line fp/no-let
44
76
  let cancelled = false;
77
+
45
78
  Promise.all(
46
79
  idList.map((id) =>
47
80
  apiJson(`/api/templates/${id}`).then((res) => ({
@@ -52,20 +85,22 @@ export const useTemplatesByIds = (ids) => {
52
85
  )
53
86
  .then((results) => {
54
87
  if (cancelled) return;
55
- const map = {};
56
- results.forEach(({ id, raw }) => {
57
- const t = ensureTemplateShape(normalizeTemplate(raw) ?? raw);
58
- // eslint-disable-next-line fp/no-mutation
59
- if (t) map[id] = t;
60
- });
61
- setTemplatesById(map);
88
+ const map = _.reduce(
89
+ (acc, { id, raw }) => {
90
+ const t = ensureTemplateShape(normalizeTemplate(raw) ?? raw);
91
+ return t ? _.set(id, t, acc) : acc;
92
+ },
93
+ {},
94
+ results,
95
+ );
96
+ setRestTemplates(map);
62
97
  })
63
98
  .catch(() => {});
64
99
  return () => {
65
100
  // eslint-disable-next-line fp/no-mutation
66
101
  cancelled = true;
67
102
  };
68
- }, [idsKey]);
103
+ }, [idsKey, domainIdsKey, hasIds, hasDomainIds]);
69
104
 
70
- return templatesById;
105
+ return preprocessedTemplates !== null ? preprocessedTemplates : restTemplates;
71
106
  };
@@ -140,6 +140,22 @@ describe("reducers: grantRequestsCart", () => {
140
140
  ).toEqual(expectedState);
141
141
  });
142
142
 
143
+ it("should reset templateContent on selectGrantRequestTemplate.TRIGGER", () => {
144
+ const stateWithContent = {
145
+ ...initialState,
146
+ templateContent: { field1: { value: "x", origin: "default" } },
147
+ };
148
+ expect(
149
+ grantRequestsCart(stateWithContent, {
150
+ type: selectGrantRequestTemplate.TRIGGER,
151
+ payload: "another_template",
152
+ }),
153
+ ).toEqual({
154
+ ...initialState,
155
+ template: "another_template",
156
+ });
157
+ });
158
+
143
159
  it("should handle the updateGrantRequestTemplateContent.TRIGGER with valid content", () => {
144
160
  const stateWithTemplate = {
145
161
  ...initialState,