@truedat/ai 6.0.2 → 6.0.3
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 +13 -1
- package/src/components/constants.js +1 -1
- package/src/components/prompts/PromptEditor.js +10 -0
- package/src/components/prompts/Prompts.js +20 -1
- package/src/components/prompts/__tests__/PromptEditor.spec.js +3 -0
- package/src/components/prompts/__tests__/__snapshots__/PromptEditor.spec.js.snap +74 -0
- package/src/components/prompts/__tests__/__snapshots__/{Prompt.spec.js.snap → Prompts.spec.js.snap} +4 -0
- package/src/components/resourceMappings/__tests__/ResourceMappingEditor.spec.js +1 -0
- package/src/components/resourceMappings/__tests__/ResourceMappings.spec.js +1 -0
- package/src/components/resourceMappings/__tests__/__snapshots__/ResourceMappingEditor.spec.js.snap +52 -0
- package/src/components/suggestions/SuggestionsWidget.js +192 -0
- package/src/components/suggestions/__tests__/SuggestionsWidget.spec.js +151 -0
- package/src/components/suggestions/__tests__/__snapshots__/SuggestionsWidget.spec.js.snap +316 -0
- package/src/hooks/usePrompts.js +7 -1
- package/src/hooks/useSuggestions.js +9 -0
- package/src/index.js +2 -1
- /package/src/components/prompts/__tests__/{Prompt.spec.js → Prompts.spec.js} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/ai",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.3",
|
|
4
4
|
"description": "Truedat Web Artificial Intelligence package",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"jsnext:main": "src/index.js",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
]
|
|
85
85
|
},
|
|
86
86
|
"dependencies": {
|
|
87
|
-
"@truedat/core": "6.0.
|
|
87
|
+
"@truedat/core": "6.0.3",
|
|
88
88
|
"prop-types": "^15.8.1",
|
|
89
89
|
"react-hook-form": "^7.45.4",
|
|
90
90
|
"react-intl": "^5.20.10",
|
|
@@ -97,5 +97,5 @@
|
|
|
97
97
|
"react-dom": ">= 16.8.6 < 17",
|
|
98
98
|
"semantic-ui-react": ">= 2.0.3 < 2.2"
|
|
99
99
|
},
|
|
100
|
-
"gitHead": "
|
|
100
|
+
"gitHead": "c88ddc501214b8c4e2732654ca0052ebbcc802c9"
|
|
101
101
|
}
|
package/src/api.js
CHANGED
|
@@ -2,5 +2,17 @@ const API_RESOURCE_MAPPINGS = "/api/resource_mappings";
|
|
|
2
2
|
const API_RESOURCE_MAPPING = "/api/resource_mappings/:id";
|
|
3
3
|
const API_PROMPTS = "/api/prompts";
|
|
4
4
|
const API_PROMPT = "/api/prompts/:id";
|
|
5
|
+
const API_PROMPT_SET_ACTIVE = "/api/prompts/:id/set_active";
|
|
6
|
+
const API_SUGGESTIONS_AVAILABILITY_CHECK =
|
|
7
|
+
"/api/suggestions/availability_check";
|
|
8
|
+
const API_SUGGESTIONS_REQUEST = "/api/suggestions/request";
|
|
5
9
|
|
|
6
|
-
export {
|
|
10
|
+
export {
|
|
11
|
+
API_RESOURCE_MAPPINGS,
|
|
12
|
+
API_RESOURCE_MAPPING,
|
|
13
|
+
API_PROMPTS,
|
|
14
|
+
API_PROMPT,
|
|
15
|
+
API_PROMPT_SET_ACTIVE,
|
|
16
|
+
API_SUGGESTIONS_AVAILABILITY_CHECK,
|
|
17
|
+
API_SUGGESTIONS_REQUEST,
|
|
18
|
+
};
|
|
@@ -18,6 +18,7 @@ import { useResourceTypeOptions, useProvidersOptions } from "../constants";
|
|
|
18
18
|
export default function PromptEditor({
|
|
19
19
|
selectedPrompt,
|
|
20
20
|
onSubmit,
|
|
21
|
+
onSetActive,
|
|
21
22
|
onCancel,
|
|
22
23
|
onDelete,
|
|
23
24
|
isSubmitting,
|
|
@@ -303,6 +304,15 @@ export default function PromptEditor({
|
|
|
303
304
|
disabled={!isValid || !isDirty}
|
|
304
305
|
content={formatMessage({ id: "actions.save" })}
|
|
305
306
|
/>
|
|
307
|
+
{selectedPrompt.id ? (
|
|
308
|
+
<Button
|
|
309
|
+
onClick={onSetActive}
|
|
310
|
+
primary
|
|
311
|
+
loading={isSubmitting}
|
|
312
|
+
disabled={isDirty || selectedPrompt.active}
|
|
313
|
+
content={formatMessage({ id: "actions.set_active" })}
|
|
314
|
+
/>
|
|
315
|
+
) : null}
|
|
306
316
|
{isDirty ? (
|
|
307
317
|
<ConfirmModal
|
|
308
318
|
trigger={
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
Icon,
|
|
10
10
|
List,
|
|
11
11
|
Segment,
|
|
12
|
+
Label,
|
|
12
13
|
} from "semantic-ui-react";
|
|
13
14
|
|
|
14
15
|
import {
|
|
@@ -16,6 +17,7 @@ import {
|
|
|
16
17
|
usePromptCreate,
|
|
17
18
|
usePromptDelete,
|
|
18
19
|
usePromptUpdate,
|
|
20
|
+
usePromptSetActive,
|
|
19
21
|
} from "@truedat/ai/hooks/usePrompts";
|
|
20
22
|
import PromptEditor from "./PromptEditor";
|
|
21
23
|
|
|
@@ -48,16 +50,27 @@ export default function Prompts() {
|
|
|
48
50
|
const { trigger: updatePrompt, isMutating: isUpdating } =
|
|
49
51
|
usePromptUpdate(selectedPrompt);
|
|
50
52
|
|
|
53
|
+
const { trigger: setPromptActive, isMutating: isSettingActive } =
|
|
54
|
+
usePromptSetActive(selectedPrompt);
|
|
55
|
+
|
|
51
56
|
const { trigger: deletePrompt, isMutating: isDeleting } =
|
|
52
57
|
usePromptDelete(selectedPrompt);
|
|
53
58
|
|
|
54
|
-
const isSubmitting =
|
|
59
|
+
const isSubmitting =
|
|
60
|
+
isCreating || isUpdating || isDeleting || isSettingActive;
|
|
55
61
|
const onPromptCreate = async (prompt) => {
|
|
56
62
|
const mutatePrompt = selectedPrompt?.id ? updatePrompt : createPrompt;
|
|
57
63
|
await mutatePrompt({ prompt });
|
|
58
64
|
clearForm();
|
|
59
65
|
mutate();
|
|
60
66
|
};
|
|
67
|
+
|
|
68
|
+
const onSetActive = async () => {
|
|
69
|
+
await setPromptActive();
|
|
70
|
+
clearForm();
|
|
71
|
+
mutate();
|
|
72
|
+
};
|
|
73
|
+
|
|
61
74
|
const onPromptDelete = async (prompt) => {
|
|
62
75
|
await deletePrompt({ prompt });
|
|
63
76
|
clearForm();
|
|
@@ -90,6 +103,11 @@ export default function Prompts() {
|
|
|
90
103
|
>
|
|
91
104
|
<List.Content>
|
|
92
105
|
<List.Header>
|
|
106
|
+
<Label
|
|
107
|
+
circular
|
|
108
|
+
color={prompt.active ? "green" : "grey"}
|
|
109
|
+
empty
|
|
110
|
+
/>{" "}
|
|
93
111
|
{prompt.name ||
|
|
94
112
|
formatMessage({
|
|
95
113
|
id: "prompts.form.name.new",
|
|
@@ -117,6 +135,7 @@ export default function Prompts() {
|
|
|
117
135
|
prompts={prompts}
|
|
118
136
|
onCancel={clearForm}
|
|
119
137
|
onSubmit={onPromptCreate}
|
|
138
|
+
onSetActive={onSetActive}
|
|
120
139
|
onDelete={selectedPrompt?.id ? onPromptDelete : null}
|
|
121
140
|
isSubmitting={isSubmitting}
|
|
122
141
|
setDirty={setDirty}
|
|
@@ -47,6 +47,9 @@ const renderOpts = {
|
|
|
47
47
|
"prompts.provider.openai": "prompts.provider.openai",
|
|
48
48
|
"resourceMappings.resourceType.data_structure":
|
|
49
49
|
"resourceMappings.resourceType.data_structure",
|
|
50
|
+
"actions.set_active": "actions.set_active",
|
|
51
|
+
"resourceMappings.resourceType.business_concept":
|
|
52
|
+
"resourceMappings.resourceType.business_concept",
|
|
50
53
|
},
|
|
51
54
|
},
|
|
52
55
|
fallback: "lazy",
|
|
@@ -68,6 +68,19 @@ exports[`<PromptEditor /> matches snapshot without onDelete 1`] = `
|
|
|
68
68
|
resourceMappings.resourceType.data_structure
|
|
69
69
|
</span>
|
|
70
70
|
</div>
|
|
71
|
+
<div
|
|
72
|
+
aria-checked="false"
|
|
73
|
+
aria-selected="false"
|
|
74
|
+
class="item"
|
|
75
|
+
role="option"
|
|
76
|
+
style="pointer-events: all;"
|
|
77
|
+
>
|
|
78
|
+
<span
|
|
79
|
+
class="text"
|
|
80
|
+
>
|
|
81
|
+
resourceMappings.resourceType.business_concept
|
|
82
|
+
</span>
|
|
83
|
+
</div>
|
|
71
84
|
</div>
|
|
72
85
|
</div>
|
|
73
86
|
</div>
|
|
@@ -210,6 +223,11 @@ exports[`<PromptEditor /> matches snapshot without onDelete 1`] = `
|
|
|
210
223
|
>
|
|
211
224
|
actions.save
|
|
212
225
|
</button>
|
|
226
|
+
<button
|
|
227
|
+
class="ui primary button"
|
|
228
|
+
>
|
|
229
|
+
actions.set_active
|
|
230
|
+
</button>
|
|
213
231
|
<button
|
|
214
232
|
class="ui button"
|
|
215
233
|
>
|
|
@@ -290,6 +308,19 @@ exports[`<PromptEditor /> matches the latest snapshot 1`] = `
|
|
|
290
308
|
resourceMappings.resourceType.data_structure
|
|
291
309
|
</span>
|
|
292
310
|
</div>
|
|
311
|
+
<div
|
|
312
|
+
aria-checked="false"
|
|
313
|
+
aria-selected="false"
|
|
314
|
+
class="item"
|
|
315
|
+
role="option"
|
|
316
|
+
style="pointer-events: all;"
|
|
317
|
+
>
|
|
318
|
+
<span
|
|
319
|
+
class="text"
|
|
320
|
+
>
|
|
321
|
+
resourceMappings.resourceType.business_concept
|
|
322
|
+
</span>
|
|
323
|
+
</div>
|
|
293
324
|
</div>
|
|
294
325
|
</div>
|
|
295
326
|
</div>
|
|
@@ -432,6 +463,11 @@ exports[`<PromptEditor /> matches the latest snapshot 1`] = `
|
|
|
432
463
|
>
|
|
433
464
|
actions.save
|
|
434
465
|
</button>
|
|
466
|
+
<button
|
|
467
|
+
class="ui primary button"
|
|
468
|
+
>
|
|
469
|
+
actions.set_active
|
|
470
|
+
</button>
|
|
435
471
|
<button
|
|
436
472
|
class="ui button"
|
|
437
473
|
>
|
|
@@ -515,6 +551,19 @@ exports[`<PromptEditor /> test cancel button with confirm 1`] = `
|
|
|
515
551
|
resourceMappings.resourceType.data_structure
|
|
516
552
|
</span>
|
|
517
553
|
</div>
|
|
554
|
+
<div
|
|
555
|
+
aria-checked="false"
|
|
556
|
+
aria-selected="false"
|
|
557
|
+
class="item"
|
|
558
|
+
role="option"
|
|
559
|
+
style="pointer-events: all;"
|
|
560
|
+
>
|
|
561
|
+
<span
|
|
562
|
+
class="text"
|
|
563
|
+
>
|
|
564
|
+
resourceMappings.resourceType.business_concept
|
|
565
|
+
</span>
|
|
566
|
+
</div>
|
|
518
567
|
</div>
|
|
519
568
|
</div>
|
|
520
569
|
</div>
|
|
@@ -655,6 +704,13 @@ exports[`<PromptEditor /> test cancel button with confirm 1`] = `
|
|
|
655
704
|
>
|
|
656
705
|
actions.save
|
|
657
706
|
</button>
|
|
707
|
+
<button
|
|
708
|
+
class="ui primary disabled button"
|
|
709
|
+
disabled=""
|
|
710
|
+
tabindex="-1"
|
|
711
|
+
>
|
|
712
|
+
actions.set_active
|
|
713
|
+
</button>
|
|
658
714
|
<button
|
|
659
715
|
class="ui button"
|
|
660
716
|
>
|
|
@@ -738,6 +794,19 @@ exports[`<PromptEditor /> test delete button 1`] = `
|
|
|
738
794
|
resourceMappings.resourceType.data_structure
|
|
739
795
|
</span>
|
|
740
796
|
</div>
|
|
797
|
+
<div
|
|
798
|
+
aria-checked="false"
|
|
799
|
+
aria-selected="false"
|
|
800
|
+
class="item"
|
|
801
|
+
role="option"
|
|
802
|
+
style="pointer-events: all;"
|
|
803
|
+
>
|
|
804
|
+
<span
|
|
805
|
+
class="text"
|
|
806
|
+
>
|
|
807
|
+
resourceMappings.resourceType.business_concept
|
|
808
|
+
</span>
|
|
809
|
+
</div>
|
|
741
810
|
</div>
|
|
742
811
|
</div>
|
|
743
812
|
</div>
|
|
@@ -880,6 +949,11 @@ exports[`<PromptEditor /> test delete button 1`] = `
|
|
|
880
949
|
>
|
|
881
950
|
actions.save
|
|
882
951
|
</button>
|
|
952
|
+
<button
|
|
953
|
+
class="ui primary button"
|
|
954
|
+
>
|
|
955
|
+
actions.set_active
|
|
956
|
+
</button>
|
|
883
957
|
<button
|
|
884
958
|
class="ui button"
|
|
885
959
|
>
|
|
@@ -66,6 +66,7 @@ describe("<ResourceMappings />", () => {
|
|
|
66
66
|
"actions.discard.confirmation.header": "confirmation_header",
|
|
67
67
|
"actions.discard.confirmation.content": "confirmation_content",
|
|
68
68
|
"resourceMappings.action.new": "resourceMappings.action.new",
|
|
69
|
+
"resourceMappings.resourceType.business_concept": "business concept",
|
|
69
70
|
},
|
|
70
71
|
},
|
|
71
72
|
fallback: "lazy",
|
package/src/components/resourceMappings/__tests__/__snapshots__/ResourceMappingEditor.spec.js.snap
CHANGED
|
@@ -60,6 +60,19 @@ exports[`<ResourceMappingEditor /> matches snapshot without onDelete 1`] = `
|
|
|
60
60
|
resourceMappings.resourceType.data_structure
|
|
61
61
|
</span>
|
|
62
62
|
</div>
|
|
63
|
+
<div
|
|
64
|
+
aria-checked="false"
|
|
65
|
+
aria-selected="false"
|
|
66
|
+
class="item"
|
|
67
|
+
role="option"
|
|
68
|
+
style="pointer-events: all;"
|
|
69
|
+
>
|
|
70
|
+
<span
|
|
71
|
+
class="text"
|
|
72
|
+
>
|
|
73
|
+
business concept
|
|
74
|
+
</span>
|
|
75
|
+
</div>
|
|
63
76
|
</div>
|
|
64
77
|
</div>
|
|
65
78
|
</div>
|
|
@@ -245,6 +258,19 @@ exports[`<ResourceMappingEditor /> matches the latest snapshot 1`] = `
|
|
|
245
258
|
resourceMappings.resourceType.data_structure
|
|
246
259
|
</span>
|
|
247
260
|
</div>
|
|
261
|
+
<div
|
|
262
|
+
aria-checked="false"
|
|
263
|
+
aria-selected="false"
|
|
264
|
+
class="item"
|
|
265
|
+
role="option"
|
|
266
|
+
style="pointer-events: all;"
|
|
267
|
+
>
|
|
268
|
+
<span
|
|
269
|
+
class="text"
|
|
270
|
+
>
|
|
271
|
+
business concept
|
|
272
|
+
</span>
|
|
273
|
+
</div>
|
|
248
274
|
</div>
|
|
249
275
|
</div>
|
|
250
276
|
</div>
|
|
@@ -433,6 +459,19 @@ exports[`<ResourceMappingEditor /> test cancel button with confirm 1`] = `
|
|
|
433
459
|
resourceMappings.resourceType.data_structure
|
|
434
460
|
</span>
|
|
435
461
|
</div>
|
|
462
|
+
<div
|
|
463
|
+
aria-checked="false"
|
|
464
|
+
aria-selected="false"
|
|
465
|
+
class="item"
|
|
466
|
+
role="option"
|
|
467
|
+
style="pointer-events: all;"
|
|
468
|
+
>
|
|
469
|
+
<span
|
|
470
|
+
class="text"
|
|
471
|
+
>
|
|
472
|
+
business concept
|
|
473
|
+
</span>
|
|
474
|
+
</div>
|
|
436
475
|
</div>
|
|
437
476
|
</div>
|
|
438
477
|
</div>
|
|
@@ -619,6 +658,19 @@ exports[`<ResourceMappingEditor /> test delete button 1`] = `
|
|
|
619
658
|
resourceMappings.resourceType.data_structure
|
|
620
659
|
</span>
|
|
621
660
|
</div>
|
|
661
|
+
<div
|
|
662
|
+
aria-checked="false"
|
|
663
|
+
aria-selected="false"
|
|
664
|
+
class="item"
|
|
665
|
+
role="option"
|
|
666
|
+
style="pointer-events: all;"
|
|
667
|
+
>
|
|
668
|
+
<span
|
|
669
|
+
class="text"
|
|
670
|
+
>
|
|
671
|
+
business concept
|
|
672
|
+
</span>
|
|
673
|
+
</div>
|
|
622
674
|
</div>
|
|
623
675
|
</div>
|
|
624
676
|
</div>
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React, { useState } from "react";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
import {
|
|
5
|
+
Button,
|
|
6
|
+
Checkbox,
|
|
7
|
+
Container,
|
|
8
|
+
Divider,
|
|
9
|
+
Icon,
|
|
10
|
+
List,
|
|
11
|
+
Message,
|
|
12
|
+
Segment,
|
|
13
|
+
} from "semantic-ui-react";
|
|
14
|
+
import { FormattedMessage } from "react-intl";
|
|
15
|
+
import { toEnrichedTextFormat } from "@truedat/core/services/format";
|
|
16
|
+
|
|
17
|
+
export default function SuggestionsWidget({
|
|
18
|
+
requestAiSuggestion,
|
|
19
|
+
applySuggestions,
|
|
20
|
+
template,
|
|
21
|
+
isModification,
|
|
22
|
+
}) {
|
|
23
|
+
const [loadingSuggestion, setLoadingSuggestion] = useState();
|
|
24
|
+
const [suggestions, setSuggestions] = useState();
|
|
25
|
+
const [suggestionsError, setSuggestionsError] = useState();
|
|
26
|
+
const [selectedSuggestions, setSelectedSuggestions] = useState([]);
|
|
27
|
+
|
|
28
|
+
const suggestionKeys = _.keys(suggestions);
|
|
29
|
+
const fieldInSuggestions = ({ name }) => _.includes(name)(suggestionKeys);
|
|
30
|
+
const templateFields = _.flow(
|
|
31
|
+
_.prop("content"),
|
|
32
|
+
_.filter(({ fields }) => _.any(fieldInSuggestions)(fields)),
|
|
33
|
+
_.map(({ name, fields }) => ({
|
|
34
|
+
name,
|
|
35
|
+
fields: _.filter(fieldInSuggestions)(fields),
|
|
36
|
+
}))
|
|
37
|
+
)(template);
|
|
38
|
+
const fieldTypes = _.flow(
|
|
39
|
+
_.prop("content"),
|
|
40
|
+
_.flatMap(({ fields }) =>
|
|
41
|
+
_.flow(
|
|
42
|
+
_.filter(({ editable = true }) => editable || !isModification),
|
|
43
|
+
_.map(({ name, type }) => [name, type])
|
|
44
|
+
)(fields)
|
|
45
|
+
),
|
|
46
|
+
_.fromPairs
|
|
47
|
+
)(template);
|
|
48
|
+
|
|
49
|
+
const validFields = _.keys(fieldTypes);
|
|
50
|
+
|
|
51
|
+
const handleRequestSuggestions = () => {
|
|
52
|
+
setLoadingSuggestion(true);
|
|
53
|
+
setSuggestionsError(null);
|
|
54
|
+
setSelectedSuggestions([]);
|
|
55
|
+
setSuggestions(null);
|
|
56
|
+
|
|
57
|
+
requestAiSuggestion(({ data: { data: suggestions } }) => {
|
|
58
|
+
if (_.prop("[0]")(suggestions) === "error") {
|
|
59
|
+
setSuggestionsError(
|
|
60
|
+
_.prop("[1].error.message")(suggestions) || _.prop("[1]")(suggestions)
|
|
61
|
+
);
|
|
62
|
+
} else {
|
|
63
|
+
setSuggestions(suggestions);
|
|
64
|
+
const selectedFields = _.flow(
|
|
65
|
+
_.keys,
|
|
66
|
+
_.filter(
|
|
67
|
+
(key) =>
|
|
68
|
+
_.includes(key)(validFields) && !_.isEmpty(suggestions[key])
|
|
69
|
+
)
|
|
70
|
+
)(suggestions);
|
|
71
|
+
setSelectedSuggestions(selectedFields);
|
|
72
|
+
}
|
|
73
|
+
setLoadingSuggestion(false);
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
const handleApplySuggestions = () => {
|
|
77
|
+
const parsedSuggestions = _.flow(
|
|
78
|
+
_.pick(selectedSuggestions),
|
|
79
|
+
_.toPairs,
|
|
80
|
+
_.map(([key, value]) => [
|
|
81
|
+
key,
|
|
82
|
+
fieldTypes[key] === "enriched_text"
|
|
83
|
+
? toEnrichedTextFormat(value)
|
|
84
|
+
: value,
|
|
85
|
+
]),
|
|
86
|
+
_.fromPairs
|
|
87
|
+
)(suggestions);
|
|
88
|
+
|
|
89
|
+
applySuggestions(parsedSuggestions);
|
|
90
|
+
setSelectedSuggestions([]);
|
|
91
|
+
setSuggestions(null);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const toggleSelectedSuggestion = (name) => {
|
|
95
|
+
setSelectedSuggestions(
|
|
96
|
+
_.includes(name)(selectedSuggestions)
|
|
97
|
+
? _.reject((v) => v == name)(selectedSuggestions)
|
|
98
|
+
: [...selectedSuggestions, name]
|
|
99
|
+
);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<Segment loading={loadingSuggestion}>
|
|
104
|
+
<Container style={{ display: "flex", justifyContent: "space-between" }}>
|
|
105
|
+
<h4>
|
|
106
|
+
{suggestions ? (
|
|
107
|
+
<Icon name="lightbulb outline" />
|
|
108
|
+
) : (
|
|
109
|
+
<Icon name="lightbulb" />
|
|
110
|
+
)}
|
|
111
|
+
<FormattedMessage id="structure_note.ai_suggestion.header" />
|
|
112
|
+
</h4>
|
|
113
|
+
<Divider hidden />
|
|
114
|
+
{suggestions ? null : (
|
|
115
|
+
<Button onClick={handleRequestSuggestions}>
|
|
116
|
+
<FormattedMessage id="actions.ai_suggestion" />
|
|
117
|
+
</Button>
|
|
118
|
+
)}
|
|
119
|
+
</Container>
|
|
120
|
+
{suggestionsError ? (
|
|
121
|
+
<Message negative>
|
|
122
|
+
<Message.Header>
|
|
123
|
+
<FormattedMessage id="structure_note.ai_suggestion.error" />
|
|
124
|
+
</Message.Header>
|
|
125
|
+
<p>{suggestionsError}</p>
|
|
126
|
+
</Message>
|
|
127
|
+
) : null}
|
|
128
|
+
{suggestions ? (
|
|
129
|
+
<>
|
|
130
|
+
{_.map(({ name: groupName, fields }) => (
|
|
131
|
+
<Container key={`group-${groupName}`}>
|
|
132
|
+
<>
|
|
133
|
+
<h5>{groupName ? groupName : ""}</h5>
|
|
134
|
+
<List>
|
|
135
|
+
{_.map(({ name, label, editable = true }) => (
|
|
136
|
+
<List.Item
|
|
137
|
+
key={name}
|
|
138
|
+
style={{
|
|
139
|
+
display: "flex",
|
|
140
|
+
alignItems: "center",
|
|
141
|
+
gap: "12px",
|
|
142
|
+
}}
|
|
143
|
+
>
|
|
144
|
+
<Checkbox
|
|
145
|
+
id={name}
|
|
146
|
+
disabled={
|
|
147
|
+
(isModification && !editable) ||
|
|
148
|
+
_.isEmpty(suggestions[name])
|
|
149
|
+
}
|
|
150
|
+
checked={_.includes(name)(selectedSuggestions)}
|
|
151
|
+
onChange={() => toggleSelectedSuggestion(name)}
|
|
152
|
+
/>
|
|
153
|
+
<label
|
|
154
|
+
htmlFor={name}
|
|
155
|
+
style={{ cursor: "pointer" }}
|
|
156
|
+
disabled
|
|
157
|
+
>
|
|
158
|
+
<List.Header>{label}</List.Header>
|
|
159
|
+
<List.Description>
|
|
160
|
+
{_.isEmpty(suggestions[name])
|
|
161
|
+
? "-"
|
|
162
|
+
: suggestions[name]}
|
|
163
|
+
</List.Description>
|
|
164
|
+
</label>
|
|
165
|
+
</List.Item>
|
|
166
|
+
))(fields)}
|
|
167
|
+
</List>
|
|
168
|
+
<Divider hidden />
|
|
169
|
+
</>
|
|
170
|
+
</Container>
|
|
171
|
+
))(templateFields)}
|
|
172
|
+
<Container textAlign="right">
|
|
173
|
+
<Button
|
|
174
|
+
primary
|
|
175
|
+
onClick={handleApplySuggestions}
|
|
176
|
+
disabled={_.isEmpty(selectedSuggestions)}
|
|
177
|
+
>
|
|
178
|
+
<FormattedMessage id="actions.apply_ai_suggestion" />
|
|
179
|
+
</Button>
|
|
180
|
+
</Container>
|
|
181
|
+
</>
|
|
182
|
+
) : null}
|
|
183
|
+
</Segment>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
SuggestionsWidget.propTypes = {
|
|
188
|
+
aiSuggestionsUrl: PropTypes.object,
|
|
189
|
+
applySuggestions: PropTypes.func,
|
|
190
|
+
template: PropTypes.object,
|
|
191
|
+
isModification: PropTypes.bool,
|
|
192
|
+
};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import React, { Suspense } from "react";
|
|
2
|
+
import { waitFor } from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import { render } from "@truedat/test/render";
|
|
5
|
+
import SuggestionsWidget from "../SuggestionsWidget";
|
|
6
|
+
|
|
7
|
+
const mockSuggestions = {
|
|
8
|
+
editable_field: "editable_field_value",
|
|
9
|
+
non_editable_field: "non_editable_field_value",
|
|
10
|
+
unselect_field: "unselect_field_value",
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const messages = {
|
|
14
|
+
en: {
|
|
15
|
+
"actions.cancel": "cancel",
|
|
16
|
+
"actions.save": "save",
|
|
17
|
+
"structure_note.ai_suggestion.header": "header",
|
|
18
|
+
"actions.apply_ai_suggestion": "actions.apply_ai_suggestion",
|
|
19
|
+
"actions.ai_suggestion": "actions.ai_suggestion",
|
|
20
|
+
"structure_note.ai_suggestion.error": "error",
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const requestAiSuggestion = (mock) => (callback) => {
|
|
25
|
+
callback({
|
|
26
|
+
data: {
|
|
27
|
+
data:
|
|
28
|
+
mock === "suggestions"
|
|
29
|
+
? mockSuggestions
|
|
30
|
+
: mock === "error_message"
|
|
31
|
+
? ["error", { error: { message: "ERROR MESSAGE" } }]
|
|
32
|
+
: ["error", "ERROR TEXT"],
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
describe("<SuggestionsWidget />", () => {
|
|
38
|
+
it("matches the latest snapshot", () => {
|
|
39
|
+
const props = {
|
|
40
|
+
template: {},
|
|
41
|
+
requestAiSuggestion: requestAiSuggestion("suggestions"),
|
|
42
|
+
};
|
|
43
|
+
const { container } = render(
|
|
44
|
+
<Suspense fallback={null}>
|
|
45
|
+
<SuggestionsWidget {...props} />
|
|
46
|
+
</Suspense>,
|
|
47
|
+
{ messages }
|
|
48
|
+
);
|
|
49
|
+
expect(container).toMatchSnapshot();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("component lifecycle", async () => {
|
|
53
|
+
const applySuggestions = jest.fn();
|
|
54
|
+
const props = {
|
|
55
|
+
requestAiSuggestion: requestAiSuggestion("suggestions"),
|
|
56
|
+
applySuggestions,
|
|
57
|
+
template: {
|
|
58
|
+
content: [
|
|
59
|
+
{
|
|
60
|
+
fields: [
|
|
61
|
+
{
|
|
62
|
+
name: "editable_field",
|
|
63
|
+
label: "EditableField",
|
|
64
|
+
editable: true,
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: "Group2",
|
|
70
|
+
fields: [
|
|
71
|
+
{
|
|
72
|
+
name: "non_editable_field",
|
|
73
|
+
label: "NonEditableField",
|
|
74
|
+
editable: false,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: "unselect_field",
|
|
78
|
+
label: "UnselectedField",
|
|
79
|
+
editable: true,
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
isModification: true,
|
|
86
|
+
};
|
|
87
|
+
const { container, findByText, queryByText } = render(
|
|
88
|
+
<Suspense fallback={null}>
|
|
89
|
+
<SuggestionsWidget {...props} />
|
|
90
|
+
</Suspense>,
|
|
91
|
+
{ messages }
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
userEvent.click(await findByText(/actions.ai_suggestion/i));
|
|
95
|
+
await waitFor(() =>
|
|
96
|
+
expect(queryByText(/actions.ai_suggestion/i)).not.toBeInTheDocument()
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
expect(container).toMatchSnapshot();
|
|
100
|
+
|
|
101
|
+
userEvent.click(await findByText(/UnselectedField/i));
|
|
102
|
+
userEvent.click(await findByText(/UnselectedField/i));
|
|
103
|
+
userEvent.click(await findByText(/UnselectedField/i));
|
|
104
|
+
|
|
105
|
+
userEvent.click(await findByText(/actions.apply_ai_suggestion/i));
|
|
106
|
+
await waitFor(() =>
|
|
107
|
+
expect(
|
|
108
|
+
queryByText(/actions.apply_ai_suggestion/i)
|
|
109
|
+
).not.toBeInTheDocument()
|
|
110
|
+
);
|
|
111
|
+
expect(applySuggestions).toHaveBeenCalledWith(
|
|
112
|
+
expect.objectContaining({ editable_field: "editable_field_value" })
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
expect(container).toMatchSnapshot();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("renders error with message", async () => {
|
|
119
|
+
const props = {
|
|
120
|
+
requestAiSuggestion: requestAiSuggestion("error_message"),
|
|
121
|
+
};
|
|
122
|
+
const { container, findByText, queryByText } = render(
|
|
123
|
+
<Suspense fallback={null}>
|
|
124
|
+
<SuggestionsWidget {...props} />
|
|
125
|
+
</Suspense>,
|
|
126
|
+
{ messages }
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
userEvent.click(await findByText(/actions.ai_suggestion/i));
|
|
130
|
+
await waitFor(() =>
|
|
131
|
+
expect(queryByText(/error message/i)).toBeInTheDocument()
|
|
132
|
+
);
|
|
133
|
+
expect(container).toMatchSnapshot();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("renders error with text", async () => {
|
|
137
|
+
const props = {
|
|
138
|
+
requestAiSuggestion: requestAiSuggestion("error_text"),
|
|
139
|
+
};
|
|
140
|
+
const { container, findByText, queryByText } = render(
|
|
141
|
+
<Suspense fallback={null}>
|
|
142
|
+
<SuggestionsWidget {...props} />
|
|
143
|
+
</Suspense>,
|
|
144
|
+
{ messages }
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
userEvent.click(await findByText(/actions.ai_suggestion/i));
|
|
148
|
+
await waitFor(() => expect(queryByText(/error text/i)).toBeInTheDocument());
|
|
149
|
+
expect(container).toMatchSnapshot();
|
|
150
|
+
});
|
|
151
|
+
});
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<SuggestionsWidget /> component lifecycle 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<div
|
|
6
|
+
class="ui segment"
|
|
7
|
+
>
|
|
8
|
+
<div
|
|
9
|
+
class="ui container"
|
|
10
|
+
style="display: flex; justify-content: space-between;"
|
|
11
|
+
>
|
|
12
|
+
<h4>
|
|
13
|
+
<i
|
|
14
|
+
aria-hidden="true"
|
|
15
|
+
class="lightbulb outline icon"
|
|
16
|
+
/>
|
|
17
|
+
header
|
|
18
|
+
</h4>
|
|
19
|
+
<div
|
|
20
|
+
class="ui hidden divider"
|
|
21
|
+
/>
|
|
22
|
+
</div>
|
|
23
|
+
<div
|
|
24
|
+
class="ui container"
|
|
25
|
+
>
|
|
26
|
+
<h5 />
|
|
27
|
+
<div
|
|
28
|
+
class="ui list"
|
|
29
|
+
role="list"
|
|
30
|
+
>
|
|
31
|
+
<div
|
|
32
|
+
class="item"
|
|
33
|
+
role="listitem"
|
|
34
|
+
style="display: flex; align-items: center; gap: 12px;"
|
|
35
|
+
>
|
|
36
|
+
<div
|
|
37
|
+
class="ui checked fitted checkbox"
|
|
38
|
+
>
|
|
39
|
+
<input
|
|
40
|
+
checked=""
|
|
41
|
+
class="hidden"
|
|
42
|
+
id="editable_field"
|
|
43
|
+
readonly=""
|
|
44
|
+
tabindex="0"
|
|
45
|
+
type="checkbox"
|
|
46
|
+
value=""
|
|
47
|
+
/>
|
|
48
|
+
<label
|
|
49
|
+
for="editable_field"
|
|
50
|
+
/>
|
|
51
|
+
</div>
|
|
52
|
+
<label
|
|
53
|
+
disabled=""
|
|
54
|
+
for="editable_field"
|
|
55
|
+
style="cursor: pointer;"
|
|
56
|
+
>
|
|
57
|
+
<div
|
|
58
|
+
class="header"
|
|
59
|
+
>
|
|
60
|
+
EditableField
|
|
61
|
+
</div>
|
|
62
|
+
<div
|
|
63
|
+
class="description"
|
|
64
|
+
>
|
|
65
|
+
editable_field_value
|
|
66
|
+
</div>
|
|
67
|
+
</label>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
<div
|
|
71
|
+
class="ui hidden divider"
|
|
72
|
+
/>
|
|
73
|
+
</div>
|
|
74
|
+
<div
|
|
75
|
+
class="ui container"
|
|
76
|
+
>
|
|
77
|
+
<h5>
|
|
78
|
+
Group2
|
|
79
|
+
</h5>
|
|
80
|
+
<div
|
|
81
|
+
class="ui list"
|
|
82
|
+
role="list"
|
|
83
|
+
>
|
|
84
|
+
<div
|
|
85
|
+
class="item"
|
|
86
|
+
role="listitem"
|
|
87
|
+
style="display: flex; align-items: center; gap: 12px;"
|
|
88
|
+
>
|
|
89
|
+
<div
|
|
90
|
+
class="ui disabled fitted checkbox"
|
|
91
|
+
>
|
|
92
|
+
<input
|
|
93
|
+
class="hidden"
|
|
94
|
+
disabled=""
|
|
95
|
+
id="non_editable_field"
|
|
96
|
+
readonly=""
|
|
97
|
+
tabindex="-1"
|
|
98
|
+
type="checkbox"
|
|
99
|
+
value=""
|
|
100
|
+
/>
|
|
101
|
+
<label
|
|
102
|
+
for="non_editable_field"
|
|
103
|
+
/>
|
|
104
|
+
</div>
|
|
105
|
+
<label
|
|
106
|
+
disabled=""
|
|
107
|
+
for="non_editable_field"
|
|
108
|
+
style="cursor: pointer;"
|
|
109
|
+
>
|
|
110
|
+
<div
|
|
111
|
+
class="header"
|
|
112
|
+
>
|
|
113
|
+
NonEditableField
|
|
114
|
+
</div>
|
|
115
|
+
<div
|
|
116
|
+
class="description"
|
|
117
|
+
>
|
|
118
|
+
non_editable_field_value
|
|
119
|
+
</div>
|
|
120
|
+
</label>
|
|
121
|
+
</div>
|
|
122
|
+
<div
|
|
123
|
+
class="item"
|
|
124
|
+
role="listitem"
|
|
125
|
+
style="display: flex; align-items: center; gap: 12px;"
|
|
126
|
+
>
|
|
127
|
+
<div
|
|
128
|
+
class="ui checked fitted checkbox"
|
|
129
|
+
>
|
|
130
|
+
<input
|
|
131
|
+
checked=""
|
|
132
|
+
class="hidden"
|
|
133
|
+
id="unselect_field"
|
|
134
|
+
readonly=""
|
|
135
|
+
tabindex="0"
|
|
136
|
+
type="checkbox"
|
|
137
|
+
value=""
|
|
138
|
+
/>
|
|
139
|
+
<label
|
|
140
|
+
for="unselect_field"
|
|
141
|
+
/>
|
|
142
|
+
</div>
|
|
143
|
+
<label
|
|
144
|
+
disabled=""
|
|
145
|
+
for="unselect_field"
|
|
146
|
+
style="cursor: pointer;"
|
|
147
|
+
>
|
|
148
|
+
<div
|
|
149
|
+
class="header"
|
|
150
|
+
>
|
|
151
|
+
UnselectedField
|
|
152
|
+
</div>
|
|
153
|
+
<div
|
|
154
|
+
class="description"
|
|
155
|
+
>
|
|
156
|
+
unselect_field_value
|
|
157
|
+
</div>
|
|
158
|
+
</label>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
<div
|
|
162
|
+
class="ui hidden divider"
|
|
163
|
+
/>
|
|
164
|
+
</div>
|
|
165
|
+
<div
|
|
166
|
+
class="ui right aligned container"
|
|
167
|
+
>
|
|
168
|
+
<button
|
|
169
|
+
class="ui primary button"
|
|
170
|
+
>
|
|
171
|
+
actions.apply_ai_suggestion
|
|
172
|
+
</button>
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
`;
|
|
177
|
+
|
|
178
|
+
exports[`<SuggestionsWidget /> component lifecycle 2`] = `
|
|
179
|
+
<div>
|
|
180
|
+
<div
|
|
181
|
+
class="ui segment"
|
|
182
|
+
>
|
|
183
|
+
<div
|
|
184
|
+
class="ui container"
|
|
185
|
+
style="display: flex; justify-content: space-between;"
|
|
186
|
+
>
|
|
187
|
+
<h4>
|
|
188
|
+
<i
|
|
189
|
+
aria-hidden="true"
|
|
190
|
+
class="lightbulb icon"
|
|
191
|
+
/>
|
|
192
|
+
header
|
|
193
|
+
</h4>
|
|
194
|
+
<div
|
|
195
|
+
class="ui hidden divider"
|
|
196
|
+
/>
|
|
197
|
+
<button
|
|
198
|
+
class="ui button"
|
|
199
|
+
>
|
|
200
|
+
actions.ai_suggestion
|
|
201
|
+
</button>
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
`;
|
|
206
|
+
|
|
207
|
+
exports[`<SuggestionsWidget /> matches the latest snapshot 1`] = `
|
|
208
|
+
<div>
|
|
209
|
+
<div
|
|
210
|
+
class="ui segment"
|
|
211
|
+
>
|
|
212
|
+
<div
|
|
213
|
+
class="ui container"
|
|
214
|
+
style="display: flex; justify-content: space-between;"
|
|
215
|
+
>
|
|
216
|
+
<h4>
|
|
217
|
+
<i
|
|
218
|
+
aria-hidden="true"
|
|
219
|
+
class="lightbulb icon"
|
|
220
|
+
/>
|
|
221
|
+
header
|
|
222
|
+
</h4>
|
|
223
|
+
<div
|
|
224
|
+
class="ui hidden divider"
|
|
225
|
+
/>
|
|
226
|
+
<button
|
|
227
|
+
class="ui button"
|
|
228
|
+
>
|
|
229
|
+
actions.ai_suggestion
|
|
230
|
+
</button>
|
|
231
|
+
</div>
|
|
232
|
+
</div>
|
|
233
|
+
</div>
|
|
234
|
+
`;
|
|
235
|
+
|
|
236
|
+
exports[`<SuggestionsWidget /> renders error with message 1`] = `
|
|
237
|
+
<div>
|
|
238
|
+
<div
|
|
239
|
+
class="ui segment"
|
|
240
|
+
>
|
|
241
|
+
<div
|
|
242
|
+
class="ui container"
|
|
243
|
+
style="display: flex; justify-content: space-between;"
|
|
244
|
+
>
|
|
245
|
+
<h4>
|
|
246
|
+
<i
|
|
247
|
+
aria-hidden="true"
|
|
248
|
+
class="lightbulb icon"
|
|
249
|
+
/>
|
|
250
|
+
header
|
|
251
|
+
</h4>
|
|
252
|
+
<div
|
|
253
|
+
class="ui hidden divider"
|
|
254
|
+
/>
|
|
255
|
+
<button
|
|
256
|
+
class="ui button"
|
|
257
|
+
>
|
|
258
|
+
actions.ai_suggestion
|
|
259
|
+
</button>
|
|
260
|
+
</div>
|
|
261
|
+
<div
|
|
262
|
+
class="ui negative message"
|
|
263
|
+
>
|
|
264
|
+
<div
|
|
265
|
+
class="header"
|
|
266
|
+
>
|
|
267
|
+
error
|
|
268
|
+
</div>
|
|
269
|
+
<p>
|
|
270
|
+
ERROR MESSAGE
|
|
271
|
+
</p>
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
`;
|
|
276
|
+
|
|
277
|
+
exports[`<SuggestionsWidget /> renders error with text 1`] = `
|
|
278
|
+
<div>
|
|
279
|
+
<div
|
|
280
|
+
class="ui segment"
|
|
281
|
+
>
|
|
282
|
+
<div
|
|
283
|
+
class="ui container"
|
|
284
|
+
style="display: flex; justify-content: space-between;"
|
|
285
|
+
>
|
|
286
|
+
<h4>
|
|
287
|
+
<i
|
|
288
|
+
aria-hidden="true"
|
|
289
|
+
class="lightbulb icon"
|
|
290
|
+
/>
|
|
291
|
+
header
|
|
292
|
+
</h4>
|
|
293
|
+
<div
|
|
294
|
+
class="ui hidden divider"
|
|
295
|
+
/>
|
|
296
|
+
<button
|
|
297
|
+
class="ui button"
|
|
298
|
+
>
|
|
299
|
+
actions.ai_suggestion
|
|
300
|
+
</button>
|
|
301
|
+
</div>
|
|
302
|
+
<div
|
|
303
|
+
class="ui negative message"
|
|
304
|
+
>
|
|
305
|
+
<div
|
|
306
|
+
class="header"
|
|
307
|
+
>
|
|
308
|
+
error
|
|
309
|
+
</div>
|
|
310
|
+
<p>
|
|
311
|
+
ERROR TEXT
|
|
312
|
+
</p>
|
|
313
|
+
</div>
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
|
+
`;
|
package/src/hooks/usePrompts.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
apiJsonPatch,
|
|
8
8
|
apiJsonDelete,
|
|
9
9
|
} from "@truedat/core/services/api";
|
|
10
|
-
import { API_PROMPTS, API_PROMPT } from "../api";
|
|
10
|
+
import { API_PROMPTS, API_PROMPT, API_PROMPT_SET_ACTIVE } from "../api";
|
|
11
11
|
|
|
12
12
|
export const usePrompts = () => {
|
|
13
13
|
const { data, error, mutate } = useSWR(API_PROMPTS, apiJson);
|
|
@@ -24,6 +24,12 @@ export const usePromptUpdate = (func) => {
|
|
|
24
24
|
return useSWRMutations(url, (url, { arg }) => apiJsonPatch(url, arg));
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
+
export const usePromptSetActive = (func) => {
|
|
28
|
+
const id = func?.id || 0;
|
|
29
|
+
const url = compile(API_PROMPT_SET_ACTIVE)({ id });
|
|
30
|
+
return useSWRMutations(url, (url) => apiJsonPatch(url));
|
|
31
|
+
};
|
|
32
|
+
|
|
27
33
|
export const usePromptDelete = (func) => {
|
|
28
34
|
const id = func?.id || 0;
|
|
29
35
|
const url = compile(API_PROMPT)({ id });
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import useSWRMutations from "swr/mutation";
|
|
2
|
+
import { apiJsonPost } from "@truedat/core/services/api";
|
|
3
|
+
import { API_SUGGESTIONS_AVAILABILITY_CHECK } from "../api";
|
|
4
|
+
|
|
5
|
+
export const useAvailabilityCheck = () => {
|
|
6
|
+
return useSWRMutations(API_SUGGESTIONS_AVAILABILITY_CHECK, (url, { arg }) =>
|
|
7
|
+
apiJsonPost(url, arg)
|
|
8
|
+
);
|
|
9
|
+
};
|
package/src/index.js
CHANGED
|
File without changes
|