@truedat/core 8.2.4 → 8.3.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/core",
3
- "version": "8.2.4",
3
+ "version": "8.3.1",
4
4
  "description": "Truedat Web Core",
5
5
  "sideEffects": false,
6
6
  "module": "src/index.js",
@@ -48,7 +48,7 @@
48
48
  "@testing-library/jest-dom": "^6.6.3",
49
49
  "@testing-library/react": "^16.3.0",
50
50
  "@testing-library/user-event": "^14.6.1",
51
- "@truedat/test": "8.2.4",
51
+ "@truedat/test": "8.3.1",
52
52
  "identity-obj-proxy": "^3.0.0",
53
53
  "jest": "^29.7.0",
54
54
  "redux-saga-test-plan": "^4.0.6"
@@ -85,5 +85,5 @@
85
85
  "slate-react": "^0.22.10",
86
86
  "swr": "^2.3.3"
87
87
  },
88
- "gitHead": "b98f63284093c90c57faa1794b6ccc1fcf761d89"
88
+ "gitHead": "59c8197b9eab936607c7d205ac25a378e6dd073e"
89
89
  }
@@ -18,9 +18,7 @@ export const StatusPill = ({ status }) => (
18
18
  ])(status)}
19
19
  size="small"
20
20
  >
21
- <FormattedMessage
22
- id={`uploadJob.parser.event.status.${status}`}
23
- />
21
+ <FormattedMessage id={`uploadJob.parser.event.status.${status}`} />
24
22
  </Label>
25
23
  );
26
24
 
@@ -64,9 +62,8 @@ const SheetAndLine = ({ response }) => (
64
62
  {response.row_number ? (
65
63
  <span>
66
64
  {" "}
67
- -{" "}
68
- <FormattedMessage id="uploadJob.parser.result.prop.row_number" />
69
- : {response.row_number}
65
+ - <FormattedMessage id="uploadJob.parser.result.prop.row_number" />:{" "}
66
+ {response.row_number}
70
67
  </span>
71
68
  ) : null}
72
69
  </Label>
@@ -90,7 +87,7 @@ const externalIdDetailValue = (details) =>
90
87
  external_id={details.external_id}
91
88
  />
92
89
  ) : (
93
- details?.external_id ?? ""
90
+ (details?.external_id ?? "")
94
91
  );
95
92
 
96
93
  const ErrorDetail = ({ response }) => {
@@ -131,18 +128,20 @@ const ErrorDetail = ({ response }) => {
131
128
  ],
132
129
  ],
133
130
  implementation_creation_error: (details) => {
134
- return details?.map(detail => {
135
- if (_.isArray(detail)) {
136
- const [field, [error]] = detail;
137
- return [field, error]
138
- }
131
+ return (
132
+ details?.map((detail) => {
133
+ if (_.isArray(detail)) {
134
+ const [field, [error]] = detail;
135
+ return [field, error];
136
+ }
139
137
 
140
- if (_.isObject(detail)) {
141
- return [detail.field, detail.message]
142
- }
138
+ if (_.isObject(detail)) {
139
+ return [detail.field, detail.message];
140
+ }
143
141
 
144
- return detail;
145
- }) || []
142
+ return detail;
143
+ }) || []
144
+ );
146
145
  },
147
146
  duplicate_field_names: (details) => [
148
147
  [
@@ -152,41 +151,60 @@ const ErrorDetail = ({ response }) => {
152
151
  ],
153
152
  pending_approval_conflict: (details) =>
154
153
  [
155
- [formatMessage({ id: `${messagePrefix}.message` }), details?.message ?? ""],
156
- [formatMessage({ id: `${messagePrefix}.externalId` }), externalIdDetailValue(details)],
154
+ [
155
+ formatMessage({ id: `${messagePrefix}.message` }),
156
+ details?.message ?? "",
157
+ ],
158
+ [null, externalIdDetailValue(details)],
157
159
  ].filter(([, v]) => v !== undefined && v !== ""),
158
160
  data_structure_not_found: (details) => [
159
- [formatMessage({ id: `${messagePrefix}.externalId` }), externalIdDetailValue(details)],
160
- ],
161
- template_not_found: (details) => [
162
- [formatMessage({ id: `${messagePrefix}.externalId` }), externalIdDetailValue(details)],
163
- ],
164
- unauthorized: (details) => [
165
- [formatMessage({ id: `${messagePrefix}.externalId` }), externalIdDetailValue(details)],
161
+ [null, externalIdDetailValue(details)],
166
162
  ],
163
+ template_not_found: (details) => [[null, externalIdDetailValue(details)]],
164
+ unauthorized: (details) => [[null, externalIdDetailValue(details)]],
167
165
  unreject_failed: (details) =>
168
166
  [
169
- [formatMessage({ id: `${messagePrefix}.reason` }), details?.reason ?? ""],
170
- [formatMessage({ id: `${messagePrefix}.externalId` }), externalIdDetailValue(details)],
167
+ [
168
+ formatMessage({ id: `${messagePrefix}.reason` }),
169
+ details?.reason ?? "",
170
+ ],
171
+ [null, externalIdDetailValue(details)],
171
172
  ].filter(([, v]) => v !== undefined && v !== ""),
172
173
  field_validation_error: (details) =>
173
174
  details?.errors?.map((e) => [
174
175
  e?.field ?? formatMessage({ id: `${messagePrefix}.message` }),
175
176
  e?.message ?? "",
176
- ]) ?? [[formatMessage({ id: `${messagePrefix}.externalId` }), externalIdDetailValue(details)]],
177
- unprocessable_entity: (details) => [
178
- [formatMessage({ id: `${messagePrefix}.externalId` }), externalIdDetailValue(details)],
179
- ],
177
+ ]) ?? [[null, externalIdDetailValue(details)]],
178
+ unprocessable_entity: (details) => [[null, externalIdDetailValue(details)]],
180
179
  structure_note_creation_error: (details) => {
180
+ const reason = details?.reason ?? details?.error;
181
+
182
+ const reasonMessageId =
183
+ reason != null
184
+ ? `structures.uploadStructures.failed.note.${reason}`
185
+ : null;
186
+
181
187
  const errorPairs = details?.errors?.length
182
188
  ? details.errors.map((e) => [
183
- e?.field ?? formatMessage({ id: `${messagePrefix}.message` }),
184
- e?.message ?? "",
185
- ])
186
- : [[formatMessage({ id: `${messagePrefix}.reason` }), String(details?.reason ?? details?.error ?? "")]];
189
+ e?.field ?? formatMessage({ id: `${messagePrefix}.message` }),
190
+ e?.message ?? "",
191
+ ])
192
+ : [
193
+ [
194
+ formatMessage({ id: `${messagePrefix}.reason` }),
195
+ reasonMessageId
196
+ ? formatMessage({
197
+ id: reasonMessageId,
198
+ defaultMessage: String(reason),
199
+ })
200
+ : String(reason ?? ""),
201
+ ],
202
+ ];
203
+
187
204
  const externalIdPair = details?.external_id
188
- ? [[formatMessage({ id: `${messagePrefix}.externalId` }), externalIdDetailValue(details)]]
205
+ ? [[null, externalIdDetailValue(details)]]
189
206
  : [];
207
+
190
208
  return [...errorPairs, ...externalIdPair];
191
209
  },
192
210
  };
@@ -195,12 +213,20 @@ const ErrorDetail = ({ response }) => {
195
213
 
196
214
  const details = _.cond([
197
215
  [_.isNil, _.constant([])],
198
- [_.stubTrue, (d) => {
199
- const result = detailBuilder(d);
200
- return _.isArray(result) ? result : [];
201
- }],
216
+ [
217
+ _.stubTrue,
218
+ (d) => {
219
+ const result = detailBuilder(d);
220
+ return _.isArray(result) ? result : [];
221
+ },
222
+ ],
202
223
  ])(response.details);
203
224
 
225
+ const visibleDetails = _.filter(
226
+ ([, value]) => value != null && value !== "",
227
+ details
228
+ );
229
+
204
230
  return (
205
231
  <div>
206
232
  <CellHeader
@@ -208,25 +234,25 @@ const ErrorDetail = ({ response }) => {
208
234
  response={response}
209
235
  />
210
236
 
211
- {
212
- _.isEmpty(details)
213
- ? null
214
- : details.map(([key, value], idx) => (
215
- <div key={idx} style={{ paddingLeft: "10px" }}>
216
- <span
217
- style={{
218
- fontSize: "1em",
219
- fontWeight: "bold",
220
- paddingRight: "5px",
221
- }}
222
- >
223
- {key}:
224
- </span>
237
+ {_.isEmpty(visibleDetails)
238
+ ? null
239
+ : visibleDetails.map(([key, value], idx) => (
240
+ <div key={idx}>
241
+ {key != null && key !== "" ? (
242
+ <span
243
+ style={{
244
+ fontSize: "1em",
245
+ fontWeight: "bold",
246
+ paddingRight: "5px",
247
+ }}
248
+ >
249
+ {key}:
250
+ </span>
251
+ ) : null}
225
252
  {value}
226
253
  </div>
227
- ))
228
- }
229
- </div >
254
+ ))}
255
+ </div>
230
256
  );
231
257
  };
232
258
  const summaryItems = [
@@ -268,17 +294,14 @@ const CompletedDetail = ({ response }) => {
268
294
  );
269
295
  }
270
296
  return result;
271
- }
297
+ },
272
298
  ])(summaryItems);
273
299
 
274
300
  if (items.length === 0) return null;
275
301
 
276
- return (
277
- <span>&nbsp;|&nbsp;{items}&nbsp;|&nbsp;</span>
278
- );
302
+ return <span>&nbsp;|&nbsp;{items}&nbsp;|&nbsp;</span>;
279
303
  };
280
304
 
281
-
282
305
  const InfoDetail = ({ response }) =>
283
306
  !response || !response.type ? null : (
284
307
  <div>
@@ -290,14 +313,13 @@ const InfoDetail = ({ response }) =>
290
313
  <div>
291
314
  {response.details.implementation_key != null ? (
292
315
  <RuleImplementationLink {...response.details} />
293
- ) : (response.details.data_structure_id != null ||
294
- response.details.external_id != null) ? (
316
+ ) : response.details.data_structure_id != null ||
317
+ response.details.external_id != null ? (
295
318
  <StructureNoteLink
296
319
  data_structure_id={response.details.data_structure_id}
297
320
  external_id={response.details.external_id}
298
321
  />
299
- ) : null}
300
- {" "}
322
+ ) : null}{" "}
301
323
  </div>
302
324
  ) : null}
303
325
  <ChangesDetail changes={response?.details?.changes} />
@@ -360,21 +382,21 @@ const ChangesDetail = ({
360
382
  );
361
383
  };
362
384
 
363
-
364
385
  const formatValue = (value, key) => {
365
386
  if (_.has("value")(value)) return formatValue(value.value, key);
366
387
  if (_.isArray(value)) {
367
- return _.flow(_.map((v) => formatValue(v, key)), _.join(", "))(value);
388
+ return _.flow(
389
+ _.map((v) => formatValue(v, key)),
390
+ _.join(", ")
391
+ )(value);
368
392
  }
369
393
  if (_.isObject(value)) {
370
394
  if (_.has("document")(value))
371
395
  return <RichTextEditor readOnly value={value} />;
372
396
  if (_.has("url_value")(value))
373
397
  return `[${value.url_name}] (${value.url_value})`;
374
- if (_.has("name")(value))
375
- return value.name;
376
- if (_.has("external_id")(value))
377
- return value.external_id;
398
+ if (_.has("name")(value)) return value.name;
399
+ if (_.has("external_id")(value)) return value.external_id;
378
400
 
379
401
  return _.flow(_.keys, _.join(", "))(value);
380
402
  }
@@ -1,3 +1,4 @@
1
+ import userEvent from "@testing-library/user-event";
1
2
  import { render } from "@truedat/test/render";
2
3
  import { StatusPill, ResponseCell } from "../UploadJobParser";
3
4
 
@@ -35,9 +36,7 @@ describe("<ResponseCell />", () => {
35
36
  const rendered = render(
36
37
  <ResponseCell response={response} status="FAILED" />
37
38
  );
38
- expect(
39
- rendered.getByText(/missing_required_headers/i)
40
- ).toBeInTheDocument();
39
+ expect(rendered.getByText(/missing_required_headers/i)).toBeInTheDocument();
41
40
  });
42
41
 
43
42
  it("renders ERROR status with details", () => {
@@ -51,9 +50,7 @@ describe("<ResponseCell />", () => {
51
50
  <ResponseCell response={response} status="ERROR" />
52
51
  );
53
52
  expect(
54
- rendered.getByText(
55
- /uploadJob.parser.error.missing_required_headers/i
56
- )
53
+ rendered.getByText(/uploadJob.parser.error.missing_required_headers/i)
57
54
  ).toBeInTheDocument();
58
55
  expect(rendered.getByText(/header1, header2/i)).toBeInTheDocument();
59
56
  });
@@ -87,9 +84,7 @@ describe("<ResponseCell />", () => {
87
84
  };
88
85
  const rendered = render(<ResponseCell response={response} status="INFO" />);
89
86
  expect(
90
- rendered.getByText(
91
- /uploadJob.parser.info.implementation_updated/i
92
- )
87
+ rendered.getByText(/uploadJob.parser.info.implementation_updated/i)
93
88
  ).toBeInTheDocument();
94
89
  });
95
90
 
@@ -100,4 +95,261 @@ describe("<ResponseCell />", () => {
100
95
  );
101
96
  expect(rendered.getByText(/some text/i)).toBeInTheDocument();
102
97
  });
98
+
99
+ it("renders ERROR status with structure_note_creation_error and translated workflow reason", () => {
100
+ const response = {
101
+ type: "structure_note_creation_error",
102
+ details: {
103
+ reason: "content_unchanged",
104
+ external_id: "EXT-001",
105
+ data_structure_id: 123,
106
+ },
107
+ };
108
+ const renderOpts = {
109
+ messages: {
110
+ en: {
111
+ "uploadJob.parser.error.structure_note_creation_error":
112
+ "Structure note error",
113
+ "uploadJob.parser.detail.reason": "Reason",
114
+ "uploadJob.parser.detail.externalId": "External id",
115
+ "structures.uploadStructures.failed.note.content_unchanged":
116
+ "Content unchanged",
117
+ },
118
+ },
119
+ };
120
+ const rendered = render(
121
+ <ResponseCell response={response} status="ERROR" />,
122
+ renderOpts
123
+ );
124
+ expect(rendered.getByText(/structure note error/i)).toBeInTheDocument();
125
+ expect(rendered.getByText(/content unchanged/i)).toBeInTheDocument();
126
+ });
127
+
128
+ it("returns null when ERROR response has no type", () => {
129
+ const response = { details: {} };
130
+ const rendered = render(
131
+ <ResponseCell response={response} status="ERROR" />
132
+ );
133
+ expect(rendered.container.firstChild).toBeNull();
134
+ });
135
+
136
+ it("renders ERROR status with invalid_template_name details", () => {
137
+ const response = {
138
+ type: "invalid_template_name",
139
+ details: { template_name: "My Template" },
140
+ };
141
+ const rendered = render(
142
+ <ResponseCell response={response} status="ERROR" />
143
+ );
144
+ expect(
145
+ rendered.getByText(/uploadJob.parser.error.invalid_template_name/i)
146
+ ).toBeInTheDocument();
147
+ expect(rendered.getByText(/My Template/i)).toBeInTheDocument();
148
+ });
149
+
150
+ it("renders ERROR status with duplicate_field_names details", () => {
151
+ const response = {
152
+ type: "duplicate_field_names",
153
+ details: { duplicate_fields: ["col_a", "col_b"] },
154
+ };
155
+ const rendered = render(
156
+ <ResponseCell response={response} status="ERROR" />
157
+ );
158
+ expect(
159
+ rendered.getByText(/uploadJob.parser.error.duplicate_field_names/i)
160
+ ).toBeInTheDocument();
161
+ expect(rendered.getByText(/col_a, col_b/i)).toBeInTheDocument();
162
+ });
163
+
164
+ it("renders ERROR status with implementation_creation_error and array details", () => {
165
+ const response = {
166
+ type: "implementation_creation_error",
167
+ details: [
168
+ ["name", ["Name is required"]],
169
+ ["key", ["Key is invalid"]],
170
+ ],
171
+ };
172
+ const rendered = render(
173
+ <ResponseCell response={response} status="ERROR" />
174
+ );
175
+ expect(
176
+ rendered.getByText(
177
+ /uploadJob.parser.error.implementation_creation_error/i
178
+ )
179
+ ).toBeInTheDocument();
180
+ expect(rendered.getByText(/Name is required/i)).toBeInTheDocument();
181
+ expect(rendered.getByText(/Key is invalid/i)).toBeInTheDocument();
182
+ });
183
+
184
+ it("renders ERROR status with implementation_creation_error and object details", () => {
185
+ const response = {
186
+ type: "implementation_creation_error",
187
+ details: [
188
+ { field: "name", message: "Invalid" },
189
+ { field: "key", message: "Duplicate" },
190
+ ],
191
+ };
192
+ const rendered = render(
193
+ <ResponseCell response={response} status="ERROR" />
194
+ );
195
+ expect(rendered.getByText(/Invalid/i)).toBeInTheDocument();
196
+ expect(rendered.getByText(/Duplicate/i)).toBeInTheDocument();
197
+ });
198
+
199
+ it("renders ERROR status with field_validation_error details", () => {
200
+ const response = {
201
+ type: "field_validation_error",
202
+ details: {
203
+ errors: [
204
+ { field: "name", message: "Required" },
205
+ { field: "external_id", message: "Already exists" },
206
+ ],
207
+ },
208
+ };
209
+ const rendered = render(
210
+ <ResponseCell response={response} status="ERROR" />
211
+ );
212
+ expect(
213
+ rendered.getByText(/uploadJob.parser.error.field_validation_error/i)
214
+ ).toBeInTheDocument();
215
+ expect(rendered.getByText(/Required/i)).toBeInTheDocument();
216
+ expect(rendered.getByText(/Already exists/i)).toBeInTheDocument();
217
+ });
218
+
219
+ it("renders ERROR status with deprecated_implementation details", () => {
220
+ const response = {
221
+ type: "deprecated_implementation",
222
+ details: { implementation_key: "old_impl_key" },
223
+ };
224
+ const rendered = render(
225
+ <ResponseCell response={response} status="ERROR" />
226
+ );
227
+ expect(
228
+ rendered.getByText(/uploadJob.parser.error.deprecated_implementation/i)
229
+ ).toBeInTheDocument();
230
+ expect(rendered.getByText(/old_impl_key/i)).toBeInTheDocument();
231
+ });
232
+
233
+ it("renders ERROR status with unreject_failed details", () => {
234
+ const response = {
235
+ type: "unreject_failed",
236
+ details: { reason: "Workflow in progress" },
237
+ };
238
+ const renderOpts = {
239
+ messages: {
240
+ en: {
241
+ "uploadJob.parser.error.unreject_failed": "Unreject failed",
242
+ "uploadJob.parser.detail.reason": "Reason",
243
+ },
244
+ },
245
+ };
246
+ const rendered = render(
247
+ <ResponseCell response={response} status="ERROR" />,
248
+ renderOpts
249
+ );
250
+ expect(rendered.getByText(/unreject failed/i)).toBeInTheDocument();
251
+ expect(rendered.getByText(/Workflow in progress/i)).toBeInTheDocument();
252
+ });
253
+
254
+ it("renders COMPLETED status with no summary when all counts are zero", () => {
255
+ const response = {
256
+ insert_count: 0,
257
+ update_count: 0,
258
+ unchanged_count: 0,
259
+ error_count: 0,
260
+ invalid_sheet_count: 0,
261
+ };
262
+ const rendered = render(
263
+ <ResponseCell response={response} status="COMPLETED" />
264
+ );
265
+ expect(
266
+ rendered.queryByText(/uploadJob.parser.result.summary.created/i)
267
+ ).not.toBeInTheDocument();
268
+ });
269
+
270
+ it("renders default case with object response as JSON", () => {
271
+ const response = { code: "ERR_001", description: "Something failed" };
272
+ const rendered = render(
273
+ <ResponseCell response={response} status="UNKNOWN" />
274
+ );
275
+ expect(rendered.getByText(/"code":\s*"ERR_001"/i)).toBeInTheDocument();
276
+ expect(
277
+ rendered.getByText(/"description":\s*"Something failed"/i)
278
+ ).toBeInTheDocument();
279
+ });
280
+
281
+ it("renders INFO status with details.changes and expandable ChangesDetail", async () => {
282
+ const response = {
283
+ type: "implementation_updated",
284
+ details: {
285
+ changes: {
286
+ name: "New Name",
287
+ description: "Updated description",
288
+ },
289
+ },
290
+ };
291
+ const user = userEvent.setup({ delay: null });
292
+ const rendered = render(<ResponseCell response={response} status="INFO" />);
293
+ expect(
294
+ rendered.getByText(/uploadJob.parser.info.implementation_updated/i)
295
+ ).toBeInTheDocument();
296
+ expect(
297
+ rendered.getByText(/uploadJob.parser.result.prop.changes/i)
298
+ ).toBeInTheDocument();
299
+ expect(rendered.getByText(/\(2\)/)).toBeInTheDocument();
300
+ await user.click(
301
+ rendered.getByText(/uploadJob.parser.result.prop.changes/i).closest("div")
302
+ );
303
+ expect(rendered.getByText(/New Name/i)).toBeInTheDocument();
304
+ expect(rendered.getByText(/Updated description/i)).toBeInTheDocument();
305
+ });
306
+
307
+ it("renders INFO status with nested df_content in changes", async () => {
308
+ const response = {
309
+ type: "implementation_updated",
310
+ details: {
311
+ changes: {
312
+ df_content: {
313
+ inner_key: "inner_value",
314
+ },
315
+ },
316
+ },
317
+ };
318
+ const user = userEvent.setup({ delay: null });
319
+ const rendered = render(<ResponseCell response={response} status="INFO" />);
320
+ const changesHeader = rendered.getByText(
321
+ /uploadJob.parser.result.prop.changes/i
322
+ );
323
+ await user.click(changesHeader.closest("div"));
324
+ expect(
325
+ rendered.getByText(/ruleImplementations.props.df_content/i)
326
+ ).toBeInTheDocument();
327
+ });
328
+
329
+ it("renders ERROR with sheet and row_number in response", () => {
330
+ const response = {
331
+ type: "missing_required_headers",
332
+ sheet: "Sheet1",
333
+ row_number: 5,
334
+ details: { missing_headers: ["H1"] },
335
+ };
336
+ const rendered = render(
337
+ <ResponseCell response={response} status="ERROR" />
338
+ );
339
+ expect(rendered.getByText(/Sheet1/i)).toBeInTheDocument();
340
+ expect(rendered.getByText(/5/)).toBeInTheDocument();
341
+ });
342
+
343
+ it("renders INFO status with null details", () => {
344
+ const response = { type: "some_info" };
345
+ const rendered = render(<ResponseCell response={response} status="INFO" />);
346
+ expect(
347
+ rendered.getByText(/uploadJob.parser.info.some_info/i)
348
+ ).toBeInTheDocument();
349
+ });
350
+
351
+ it("renders INFO status with null response as null", () => {
352
+ const rendered = render(<ResponseCell response={null} status="INFO" />);
353
+ expect(rendered.container.firstChild).toBeNull();
354
+ });
103
355
  });
package/src/routes.js CHANGED
@@ -85,6 +85,7 @@ export const GRANT_APPROVAL_RULES = "/grantApprovalRules";
85
85
  export const GRANT = "/grants/:id";
86
86
  export const GRANTS = "/grants";
87
87
  export const GRANTS_REQUESTS_CHECKOUT = "/grantsRequests/checkout";
88
+ export const GRANTS_REQUESTS_RESULT = "/grantsRequests/result";
88
89
  export const GRANT_REQUEST = "/grantRequests/:id";
89
90
  export const GRANT_REQUESTS = "/grantRequests";
90
91
  export const GRANT_REQUESTS_APPROVALS_RESULT =
@@ -363,6 +364,7 @@ const routes = {
363
364
  GRANT_APPROVAL_RULES,
364
365
  GRANTS,
365
366
  GRANTS_REQUESTS_CHECKOUT,
367
+ GRANTS_REQUESTS_RESULT,
366
368
  GRANT_REQUEST,
367
369
  GRANT_REQUESTS,
368
370
  GRANT_REQUESTS_APPROVALS_RESULT,
package/src/webContext.js CHANGED
@@ -1,6 +1,10 @@
1
1
  import { use, createContext, useState } from "react";
2
2
 
3
- const defaultContext = { disable_td_ai: false, structureTabs: {} };
3
+ const defaultContext = {
4
+ disable_td_ai: false,
5
+ structureTabs: {},
6
+ scopesWithRelations: [],
7
+ };
4
8
  export const WebContext = createContext(defaultContext);
5
9
  export const WebContextProvider = ({ children, value = {} }) => {
6
10
  const [alertMessage, setAlertMessage] = useState({});