@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.
@@ -1,8 +1,33 @@
1
1
  import { render, waitForLoad } from "@truedat/test/render";
2
- import { singleTemplateMock } from "@truedat/test/mocks";
2
+ import { singleTemplateMock, domainsMock } from "@truedat/test/mocks";
3
3
  import { StructureGrantCartCheckout } from "../StructureGrantCartCheckout";
4
4
 
5
- const grantRequestsCart = {
5
+ jest.mock("@truedat/core/webContext", () => ({
6
+ useWebContext: jest.fn(),
7
+ }));
8
+
9
+ jest.mock("@truedat/df/hooks/useTemplateRelations", () => ({
10
+ useTemplateRelations: jest.fn(),
11
+ }));
12
+
13
+ jest.mock("../../hooks/useCheckoutGrantRequest", () => ({
14
+ useCheckoutGrantRequest: jest.fn(),
15
+ }));
16
+
17
+ jest.mock("react-redux", () => {
18
+ const actual = jest.requireActual("react-redux");
19
+ return {
20
+ ...actual,
21
+ useDispatch: jest.fn(),
22
+ };
23
+ });
24
+
25
+ const { useWebContext } = require("@truedat/core/webContext");
26
+ const { useTemplateRelations } = require("@truedat/df/hooks/useTemplateRelations");
27
+ const { useCheckoutGrantRequest } = require("../../hooks/useCheckoutGrantRequest");
28
+ const { useDispatch } = require("react-redux");
29
+
30
+ const grantRequestsCartWithModification = {
6
31
  template: null,
7
32
  templateContent: {},
8
33
  modificationGrant: {
@@ -13,7 +38,6 @@ const grantRequestsCart = {
13
38
  id: "4570121",
14
39
  system_id: 7,
15
40
  },
16
-
17
41
  detail: {},
18
42
  id: "73399",
19
43
  inserted_at: "2025-01-03T11:47:30.447054Z",
@@ -26,27 +50,70 @@ const grantRequestsCart = {
26
50
  },
27
51
  user_id: "10",
28
52
  },
53
+ structures: [],
54
+ };
55
+
56
+ const grantRequestsCartSingle = {
57
+ template: "some_template",
58
+ templateContent: {},
59
+ modificationGrant: null,
60
+ validCart: true,
61
+ user: { id: "1", valid: true },
62
+ structures: [],
63
+ };
64
+
65
+ const grantRequestsCartMultiGroup = {
66
+ template: null,
67
+ templateContent: {},
68
+ modificationGrant: null,
69
+ validCart: false,
70
+ user: { id: "1", valid: true },
71
+ structures: [
72
+ { id: 101, system_id: 10, system: { id: 10 } },
73
+ { id: 102, system_id: 10, system: { id: 10 } },
74
+ { id: 201, system_id: 20, system: { id: 20 } },
75
+ ],
76
+ groupTemplates: {
77
+ "1": { template: "Template A", templateContent: {}, valid: true },
78
+ "2": { template: "Template B", templateContent: {}, valid: false },
79
+ others: { template: null, templateContent: {}, valid: false },
80
+ },
29
81
  };
30
82
 
31
83
  describe("StructureGrantCartCheckout", () => {
32
- const mockCheckoutGrantRequest = jest.fn();
84
+ const mockTrigger = jest.fn();
85
+ const mockDispatch = jest.fn();
86
+ const mockInitGroupTemplates = jest.fn();
87
+ const mockSelectGrantRequestTemplateForGroup = jest.fn();
88
+ const mockUpdateGrantRequestTemplateContentForGroup = jest.fn();
33
89
 
34
90
  const defaultProps = {
35
- grantRequestsCart,
36
- checkoutGrantRequest: mockCheckoutGrantRequest,
91
+ grantRequestsCart: grantRequestsCartWithModification,
92
+ initGroupTemplates: mockInitGroupTemplates,
93
+ selectGrantRequestTemplateForGroup: mockSelectGrantRequestTemplateForGroup,
94
+ updateGrantRequestTemplateContentForGroup:
95
+ mockUpdateGrantRequestTemplateContentForGroup,
37
96
  };
38
97
 
39
98
  beforeEach(() => {
40
99
  jest.clearAllMocks();
100
+ useWebContext.mockReturnValue({ scopesWithRelations: [] });
101
+ useTemplateRelations.mockReturnValue({ resource: [] });
102
+ useCheckoutGrantRequest.mockReturnValue({
103
+ trigger: mockTrigger,
104
+ isMutating: false,
105
+ });
106
+ useDispatch.mockReturnValue(mockDispatch);
41
107
  });
42
108
 
43
109
  const variables = { scope: "gr" };
44
-
45
110
  const renderOpts = {
46
111
  mocks: [singleTemplateMock(variables)],
47
112
  };
48
113
 
49
- it("matches the latest snapshot", async () => {
114
+ it("matches snapshot when modification grant (single flow)", async () => {
115
+ useWebContext.mockReturnValue({ scopesWithRelations: [] });
116
+ useTemplateRelations.mockReturnValue({ resource: [] });
50
117
  const rendered = render(
51
118
  <StructureGrantCartCheckout {...defaultProps} />,
52
119
  renderOpts
@@ -54,4 +121,39 @@ describe("StructureGrantCartCheckout", () => {
54
121
  await waitForLoad(rendered);
55
122
  expect(rendered.container).toMatchSnapshot();
56
123
  });
124
+
125
+ it("matches snapshot when single flow without modification and empty cart", async () => {
126
+ useWebContext.mockReturnValue({ scopesWithRelations: [] });
127
+ useTemplateRelations.mockReturnValue({ resource: [] });
128
+ const props = {
129
+ ...defaultProps,
130
+ grantRequestsCart: grantRequestsCartSingle,
131
+ };
132
+ const rendered = render(<StructureGrantCartCheckout {...props} />, renderOpts);
133
+ await waitForLoad(rendered);
134
+ expect(rendered.container).toMatchSnapshot();
135
+ });
136
+
137
+ it("matches snapshot when multi-system (gr scope and multiple groups)", async () => {
138
+ useWebContext.mockReturnValue({ scopesWithRelations: ["gr"] });
139
+ useTemplateRelations.mockReturnValue({
140
+ resource: [
141
+ { resource_id: 10, template_id: 1 },
142
+ { resource_id: 20, template_id: 2 },
143
+ ],
144
+ });
145
+ const multiRenderOpts = {
146
+ state: { grantRequestsCart: grantRequestsCartMultiGroup },
147
+ mocks: [
148
+ singleTemplateMock({ scope: "gr" }),
149
+ domainsMock({ action: "createForeignGrantRequest" }),
150
+ ],
151
+ };
152
+ const rendered = render(
153
+ <StructureGrantCartCheckout {...defaultProps} />,
154
+ multiRenderOpts
155
+ );
156
+ await waitForLoad(rendered);
157
+ expect(rendered.container).toMatchSnapshot();
158
+ });
57
159
  });
@@ -0,0 +1,179 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`GrantRequestCartResult matches snapshot when groups have embedded requests with data structures 1`] = `
4
+ <div>
5
+ <div
6
+ class="ui segment"
7
+ >
8
+ <h2
9
+ class="ui header"
10
+ >
11
+ grantRequest.cart.result.header
12
+ </h2>
13
+ <div
14
+ class="ui positive message"
15
+ >
16
+ <div
17
+ class="header"
18
+ >
19
+ grantRequest.cart.result.success
20
+ </div>
21
+ </div>
22
+ <div
23
+ class="ui divided relaxed list"
24
+ role="list"
25
+ >
26
+ <div
27
+ class="item"
28
+ role="listitem"
29
+ >
30
+ <div
31
+ class="content"
32
+ >
33
+ <a
34
+ class="header"
35
+ data-discover="true"
36
+ href="/grantRequestGroups/1"
37
+ >
38
+ Template A
39
+ </a>
40
+ <div
41
+ class="description"
42
+ >
43
+ Structures requested
44
+ :
45
+ <ul
46
+ style="margin-top: 0.25em; margin-bottom: 0px;"
47
+ >
48
+ <li>
49
+ <a
50
+ data-discover="true"
51
+ href="/structures/10"
52
+ >
53
+ Structure One
54
+ </a>
55
+ </li>
56
+ <li>
57
+ <a
58
+ data-discover="true"
59
+ href="/structures/20"
60
+ >
61
+ Structure Two
62
+ </a>
63
+ </li>
64
+ </ul>
65
+ </div>
66
+ </div>
67
+ </div>
68
+ </div>
69
+ <a
70
+ class="ui primary button"
71
+ data-discover="true"
72
+ href="/myGrantRequests"
73
+ role="button"
74
+ >
75
+ grantRequest.cart.result.viewMyRequests
76
+ </a>
77
+ </div>
78
+ </div>
79
+ `;
80
+
81
+ exports[`GrantRequestCartResult matches snapshot when ids in URL and hook returns groups 1`] = `
82
+ <div>
83
+ <div
84
+ class="ui segment"
85
+ >
86
+ <h2
87
+ class="ui header"
88
+ >
89
+ grantRequest.cart.result.header
90
+ </h2>
91
+ <div
92
+ class="ui positive message"
93
+ >
94
+ <div
95
+ class="header"
96
+ >
97
+ grantRequest.cart.result.success
98
+ </div>
99
+ </div>
100
+ <div
101
+ class="ui divided relaxed list"
102
+ role="list"
103
+ >
104
+ <div
105
+ class="item"
106
+ role="listitem"
107
+ >
108
+ <div
109
+ class="content"
110
+ >
111
+ <a
112
+ class="header"
113
+ data-discover="true"
114
+ href="/grantRequestGroups/1"
115
+ >
116
+ Template A
117
+ </a>
118
+ <div
119
+ class="description"
120
+ />
121
+ </div>
122
+ </div>
123
+ <div
124
+ class="item"
125
+ role="listitem"
126
+ >
127
+ <div
128
+ class="content"
129
+ >
130
+ <a
131
+ class="header"
132
+ data-discover="true"
133
+ href="/grantRequestGroups/2"
134
+ >
135
+ Template B
136
+ </a>
137
+ <div
138
+ class="description"
139
+ />
140
+ </div>
141
+ </div>
142
+ </div>
143
+ <a
144
+ class="ui primary button"
145
+ data-discover="true"
146
+ href="/myGrantRequests"
147
+ role="button"
148
+ >
149
+ grantRequest.cart.result.viewMyRequests
150
+ </a>
151
+ </div>
152
+ </div>
153
+ `;
154
+
155
+ exports[`GrantRequestCartResult matches snapshot when no ids in URL (empty) 1`] = `
156
+ <div>
157
+ <div
158
+ class="ui segment"
159
+ >
160
+ <div
161
+ class="ui info message"
162
+ >
163
+ <div
164
+ class="header"
165
+ >
166
+ grantRequest.cart.result.empty
167
+ </div>
168
+ <a
169
+ class="ui button"
170
+ data-discover="true"
171
+ href="/myGrantRequests"
172
+ role="button"
173
+ >
174
+ grantRequest.cart.result.viewMyRequests
175
+ </a>
176
+ </div>
177
+ </div>
178
+ </div>
179
+ `;
@@ -1,6 +1,6 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
- exports[`StructureGrantCartCheckout matches the latest snapshot 1`] = `
3
+ exports[`StructureGrantCartCheckout matches snapshot when modification grant (single flow) 1`] = `
4
4
  <div>
5
5
  <div
6
6
  class="ui segment structures"
@@ -104,3 +104,296 @@ exports[`StructureGrantCartCheckout matches the latest snapshot 1`] = `
104
104
  </div>
105
105
  </div>
106
106
  `;
107
+
108
+ exports[`StructureGrantCartCheckout matches snapshot when multi-system (gr scope and multiple groups) 1`] = `
109
+ <div>
110
+ <div
111
+ class="ui segment structures"
112
+ >
113
+ <div
114
+ class="ui grid"
115
+ >
116
+ <div
117
+ class="row"
118
+ >
119
+ <div
120
+ class="eight wide column"
121
+ >
122
+ <h2
123
+ class="ui header"
124
+ >
125
+ <div
126
+ class="content"
127
+ >
128
+ grantRequest.header.update
129
+ </div>
130
+ </h2>
131
+ </div>
132
+ <div
133
+ class="right aligned eight wide column"
134
+ >
135
+ <a
136
+ class="ui secondary button"
137
+ data-discover="true"
138
+ href="/"
139
+ role="button"
140
+ >
141
+ actions.cancel
142
+ </a>
143
+ <button
144
+ class="ui primary disabled button"
145
+ disabled=""
146
+ tabindex="-1"
147
+ >
148
+ actions.save
149
+ </button>
150
+ </div>
151
+ </div>
152
+ <div
153
+ class="row"
154
+ >
155
+ <div
156
+ class="five wide column"
157
+ >
158
+ <div
159
+ class="ui segment"
160
+ >
161
+ <form
162
+ class="ui form"
163
+ >
164
+ <div
165
+ class="ui header"
166
+ >
167
+ <div
168
+ class="content"
169
+ >
170
+ grants.props.user_name
171
+ </div>
172
+ </div>
173
+ <div
174
+ class="ui header"
175
+ title="grants.props.user_name"
176
+ />
177
+ <div
178
+ class="field"
179
+ >
180
+ <div
181
+ class="ui checkbox"
182
+ >
183
+ <input
184
+ class="hidden"
185
+ readonly=""
186
+ tabindex="0"
187
+ type="checkbox"
188
+ />
189
+ <label>
190
+ grantRequestCart.user.label
191
+ </label>
192
+ </div>
193
+ <div
194
+ aria-expanded="false"
195
+ class="ui search selection dropdown"
196
+ role="combobox"
197
+ >
198
+ <input
199
+ aria-autocomplete="list"
200
+ autocomplete="off"
201
+ class="search"
202
+ tabindex="0"
203
+ type="text"
204
+ value=""
205
+ />
206
+ <div
207
+ aria-atomic="true"
208
+ aria-live="polite"
209
+ class="divider default text"
210
+ role="alert"
211
+ >
212
+ grantRequestCart.user.placeholder
213
+ </div>
214
+ <i
215
+ aria-hidden="true"
216
+ class="dropdown icon"
217
+ />
218
+ <div
219
+ class="menu transition"
220
+ role="listbox"
221
+ >
222
+ <div
223
+ class="message"
224
+ >
225
+ No results found.
226
+ </div>
227
+ </div>
228
+ </div>
229
+ </div>
230
+ filters
231
+ :
232
+ <div
233
+ class="ui labels"
234
+ >
235
+ <a
236
+ class="ui label"
237
+ >
238
+ userDomainsFilter.placeholder
239
+ </a>
240
+ <a
241
+ class="ui label"
242
+ >
243
+ roles.header
244
+ </a>
245
+ </div>
246
+ <div
247
+ class="ui divider"
248
+ />
249
+ <div
250
+ class="ui header"
251
+ title="structure.grant.cart.template"
252
+ >
253
+ structure.grant.cart.template
254
+ </div>
255
+ <div
256
+ class="ui segment"
257
+ >
258
+ <h4
259
+ class="ui header"
260
+ >
261
+ g1
262
+ </h4>
263
+ <div
264
+ class="field"
265
+ data-testid="form-field"
266
+ >
267
+ <label>
268
+ field1
269
+ </label>
270
+ <div
271
+ class="field"
272
+ >
273
+ <div
274
+ class="ui input"
275
+ >
276
+ <input
277
+ name="field1"
278
+ placeholder="field1"
279
+ type="text"
280
+ value=""
281
+ />
282
+ </div>
283
+ </div>
284
+ </div>
285
+ </div>
286
+ </form>
287
+ </div>
288
+ </div>
289
+ <div
290
+ class="eleven wide column"
291
+ />
292
+ </div>
293
+ </div>
294
+ </div>
295
+ </div>
296
+ `;
297
+
298
+ exports[`StructureGrantCartCheckout matches snapshot when single flow without modification and empty cart 1`] = `
299
+ <div>
300
+ <div
301
+ class="ui segment structures"
302
+ >
303
+ <div
304
+ class="ui grid"
305
+ >
306
+ <div
307
+ class="row"
308
+ >
309
+ <div
310
+ class="eight wide column"
311
+ >
312
+ <h2
313
+ class="ui header"
314
+ >
315
+ <div
316
+ class="content"
317
+ >
318
+ grantRequest.header.create
319
+ </div>
320
+ </h2>
321
+ </div>
322
+ <div
323
+ class="right aligned eight wide column"
324
+ >
325
+ <a
326
+ class="ui secondary button"
327
+ data-discover="true"
328
+ href="/structuresGrantRequests"
329
+ role="button"
330
+ >
331
+ actions.cancel
332
+ </a>
333
+ <button
334
+ class="ui primary button"
335
+ >
336
+ actions.save
337
+ </button>
338
+ </div>
339
+ </div>
340
+ <div
341
+ class="row"
342
+ >
343
+ <div
344
+ class="five wide column"
345
+ >
346
+ <div
347
+ class="ui segment"
348
+ >
349
+ <form
350
+ class="ui form"
351
+ >
352
+ <div
353
+ class="ui header"
354
+ title="structure.grant.cart.template"
355
+ >
356
+ structure.grant.cart.template
357
+ </div>
358
+ <div
359
+ class="ui segment"
360
+ >
361
+ <h4
362
+ class="ui header"
363
+ >
364
+ g1
365
+ </h4>
366
+ <div
367
+ class="field"
368
+ data-testid="form-field"
369
+ >
370
+ <label>
371
+ field1
372
+ </label>
373
+ <div
374
+ class="field"
375
+ >
376
+ <div
377
+ class="ui input"
378
+ >
379
+ <input
380
+ name="field1"
381
+ placeholder="field1"
382
+ type="text"
383
+ value=""
384
+ />
385
+ </div>
386
+ </div>
387
+ </div>
388
+ </div>
389
+ </form>
390
+ </div>
391
+ </div>
392
+ <div
393
+ class="eleven wide column"
394
+ />
395
+ </div>
396
+ </div>
397
+ </div>
398
+ </div>
399
+ `;
@@ -0,0 +1,16 @@
1
+ import useSWRMutation from "swr/mutation";
2
+ import { apiJsonPost, JSON_OPTS } from "@truedat/core/services/api";
3
+ import { API_GRANT_REQUEST_GROUP, API_GRANT_REQUEST_GROUPS_BULK } from "../api";
4
+
5
+ const CHECKOUT_GRANT_REQUEST_KEY = "checkout-grant-request";
6
+
7
+ const checkoutFetcher = (_key, { arg }) => {
8
+ const url = Array.isArray(arg?.grant_request_groups)
9
+ ? API_GRANT_REQUEST_GROUPS_BULK
10
+ : API_GRANT_REQUEST_GROUP;
11
+ return apiJsonPost(url, arg, JSON_OPTS).then((res) => res.data);
12
+ };
13
+
14
+ export const useCheckoutGrantRequest = () => {
15
+ return useSWRMutation(CHECKOUT_GRANT_REQUEST_KEY, checkoutFetcher);
16
+ };
@@ -0,0 +1,22 @@
1
+ import useSWR from "swr";
2
+ import { apiJson } from "@truedat/core/services/api";
3
+ import { API_GRANT_REQUEST_GROUP } from "../api";
4
+
5
+ const buildGrantRequestGroupsUrl = (ids) => {
6
+ if (!Array.isArray(ids) || ids.length === 0) return null;
7
+ const idsParam = ids.join(",");
8
+ return `${API_GRANT_REQUEST_GROUP}?ids=${idsParam}`;
9
+ };
10
+
11
+ export const useGrantRequestGroupsByIds = (ids) => {
12
+ const url = buildGrantRequestGroupsUrl(ids);
13
+ const { data, error, isLoading, mutate } = useSWR(url, apiJson);
14
+ const responseBody = data?.data;
15
+ const groups = url ? (responseBody?.data ?? []) : [];
16
+ return {
17
+ groups,
18
+ isLoading: !!url && !error && !data,
19
+ error,
20
+ mutate,
21
+ };
22
+ };
@@ -0,0 +1,27 @@
1
+ import useSWR from "swr";
2
+ import { apiJson } from "@truedat/core/services/api";
3
+ import { API_SYSTEMS } from "@truedat/core/api";
4
+
5
+ const GRANTABLE_CLASS = "grantable";
6
+
7
+ const buildSystemsUrl = (options = {}) => {
8
+ const { class: classFilter } = options;
9
+ const params = new URLSearchParams();
10
+ if (classFilter === GRANTABLE_CLASS) {
11
+ params.set("class", GRANTABLE_CLASS);
12
+ }
13
+ const query = params.toString();
14
+ return query ? `${API_SYSTEMS}?${query}` : API_SYSTEMS;
15
+ };
16
+
17
+ export const useSystems = (options = {}) => {
18
+ const url = buildSystemsUrl(options);
19
+ const { data, error, mutate } = useSWR(url, apiJson);
20
+ return {
21
+ data: data?.data ?? [],
22
+ isLoading: !error && !data,
23
+ error,
24
+ };
25
+ };
26
+
27
+ export const useGrantableSystems = () => useSystems({ class: GRANTABLE_CLASS });