@truedat/ai 6.11.1 → 6.12.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.
- package/package.json +3 -3
- package/src/api.js +8 -0
- package/src/components/AiRoutes.js +26 -0
- package/src/components/actions/Action.js +90 -0
- package/src/components/actions/ActionActions.js +134 -0
- package/src/components/actions/ActionBreadcrumbs.js +22 -0
- package/src/components/actions/ActionDetail.js +41 -0
- package/src/components/actions/ActionEdit.js +70 -0
- package/src/components/actions/ActionForm.js +168 -0
- package/src/components/actions/ActionNew.js +50 -0
- package/src/components/actions/Actions.js +61 -0
- package/src/components/actions/ActionsContext.js +52 -0
- package/src/components/actions/ActionsTable.js +124 -0
- package/src/components/actions/__tests__/Action.spec.js +59 -0
- package/src/components/actions/__tests__/ActionActions.spec.js +47 -0
- package/src/components/actions/__tests__/ActionBreadcrumbs.spec.js +21 -0
- package/src/components/actions/__tests__/ActionDetail.spec.js +106 -0
- package/src/components/actions/__tests__/ActionEdit.spec.js +133 -0
- package/src/components/actions/__tests__/ActionForm.spec.js +146 -0
- package/src/components/actions/__tests__/ActionNew.spec.js +113 -0
- package/src/components/actions/__tests__/Actions.spec.js +21 -0
- package/src/components/actions/__tests__/ActionsTable.spec.js +72 -0
- package/src/components/actions/__tests__/__snapshots__/Action.spec.js.snap +159 -0
- package/src/components/actions/__tests__/__snapshots__/ActionActions.spec.js.snap +57 -0
- package/src/components/actions/__tests__/__snapshots__/ActionBreadcrumbs.spec.js.snap +25 -0
- package/src/components/actions/__tests__/__snapshots__/ActionDetail.spec.js.snap +46 -0
- package/src/components/actions/__tests__/__snapshots__/ActionEdit.spec.js.snap +316 -0
- package/src/components/actions/__tests__/__snapshots__/ActionForm.spec.js.snap +326 -0
- package/src/components/actions/__tests__/__snapshots__/ActionNew.spec.js.snap +518 -0
- package/src/components/actions/__tests__/__snapshots__/Actions.spec.js.snap +63 -0
- package/src/components/actions/__tests__/__snapshots__/ActionsTable.spec.js.snap +121 -0
- package/src/hooks/useActions.js +63 -0
- package/src/styles/aiActionEdit.less +19 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/ai",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.12.0",
|
|
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.
|
|
87
|
+
"@truedat/core": "6.12.0",
|
|
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": "a182d4660919ab242d3150ef4a51641815913a39"
|
|
101
101
|
}
|
package/src/api.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
const API_ACTION = "/api/actions/:id";
|
|
2
|
+
const API_ACTIONS = "/api/actions";
|
|
3
|
+
const API_ACTIONS_SEARCH = "/api/actions/search";
|
|
4
|
+
const API_ACTION_SET_ACTIVE = "/api/actions/:id/set_active";
|
|
1
5
|
const API_RESOURCE_MAPPINGS = "/api/resource_mappings";
|
|
2
6
|
const API_RESOURCE_MAPPING = "/api/resource_mappings/:id";
|
|
3
7
|
const API_PROMPTS = "/api/prompts";
|
|
@@ -11,6 +15,10 @@ const API_SUGGESTIONS_AVAILABILITY_CHECK =
|
|
|
11
15
|
const API_SUGGESTIONS_REQUEST = "/api/suggestions/request";
|
|
12
16
|
|
|
13
17
|
export {
|
|
18
|
+
API_ACTION,
|
|
19
|
+
API_ACTIONS,
|
|
20
|
+
API_ACTIONS_SEARCH,
|
|
21
|
+
API_ACTION_SET_ACTIVE,
|
|
14
22
|
API_RESOURCE_MAPPINGS,
|
|
15
23
|
API_RESOURCE_MAPPING,
|
|
16
24
|
API_PROMPTS,
|
|
@@ -4,6 +4,10 @@ import { Unauthorized } from "@truedat/core/components";
|
|
|
4
4
|
import { useAuthorized } from "@truedat/core/hooks";
|
|
5
5
|
import {
|
|
6
6
|
AI_SANDBOX,
|
|
7
|
+
ACTIONS,
|
|
8
|
+
ACTION,
|
|
9
|
+
ACTION_EDIT,
|
|
10
|
+
ACTION_NEW,
|
|
7
11
|
RESOURCE_MAPPINGS,
|
|
8
12
|
PROMPTS,
|
|
9
13
|
PROVIDERS,
|
|
@@ -12,6 +16,10 @@ import ResourceMappings from "./resourceMappings/ResourceMappings";
|
|
|
12
16
|
import Prompts from "./prompts/Prompts";
|
|
13
17
|
import Providers from "./providers/Providers";
|
|
14
18
|
import AiSandbox from "./aiSandbox/AiSandbox";
|
|
19
|
+
import Actions from "./actions/Actions";
|
|
20
|
+
import Action from "./actions/Action";
|
|
21
|
+
import ActionEdit from "./actions/ActionEdit";
|
|
22
|
+
import ActionNew from "./actions/ActionNew";
|
|
15
23
|
|
|
16
24
|
export default function AiRoutes() {
|
|
17
25
|
const authorized = useAuthorized("manage_ai");
|
|
@@ -34,6 +42,24 @@ export default function AiRoutes() {
|
|
|
34
42
|
path={AI_SANDBOX}
|
|
35
43
|
render={() => (authorized ? <AiSandbox /> : <Unauthorized />)}
|
|
36
44
|
/>
|
|
45
|
+
<Route
|
|
46
|
+
path={ACTIONS}
|
|
47
|
+
exact
|
|
48
|
+
render={() => (authorized ? <Actions /> : <Unauthorized />)}
|
|
49
|
+
/>
|
|
50
|
+
<Route
|
|
51
|
+
path={ACTION_NEW}
|
|
52
|
+
exact
|
|
53
|
+
render={() => (authorized ? <ActionNew /> : <Unauthorized />)}
|
|
54
|
+
/>
|
|
55
|
+
<Route
|
|
56
|
+
path={ACTION_EDIT}
|
|
57
|
+
render={() => (authorized ? <ActionEdit /> : <Unauthorized />)}
|
|
58
|
+
/>
|
|
59
|
+
<Route
|
|
60
|
+
path={ACTION}
|
|
61
|
+
render={() => (authorized ? <Action /> : <Unauthorized />)}
|
|
62
|
+
/>
|
|
37
63
|
</Switch>
|
|
38
64
|
);
|
|
39
65
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
import { useParams } from "react-router-dom";
|
|
5
|
+
import { Loading } from "@truedat/core/components";
|
|
6
|
+
import { Header, Icon, Grid, Segment } from "semantic-ui-react";
|
|
7
|
+
import { useIntl } from "react-intl";
|
|
8
|
+
import { useAction } from "../../hooks/useActions";
|
|
9
|
+
import ActionBreadcrumbs from "./ActionBreadcrumbs";
|
|
10
|
+
import ActionActions from "./ActionActions";
|
|
11
|
+
import ActionDetail from "./ActionDetail";
|
|
12
|
+
|
|
13
|
+
export const ActionHeader = ({ action, actionMutate }) => {
|
|
14
|
+
const { formatMessage } = useIntl();
|
|
15
|
+
const buildStatusMessage = (status, icon, color) => (
|
|
16
|
+
<p>
|
|
17
|
+
<Icon name={icon} color={color} fitted />{" "}
|
|
18
|
+
{formatMessage({ id: `ai.actions.is_enabled.${status}` })}
|
|
19
|
+
</p>
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const statusMessage = _.cond([
|
|
23
|
+
[
|
|
24
|
+
({ _is_enabled, is_deleted }) => is_deleted,
|
|
25
|
+
() => buildStatusMessage("deleted", "trash", "grey"),
|
|
26
|
+
],
|
|
27
|
+
[
|
|
28
|
+
({ is_enabled, _is_deleted }) => is_enabled,
|
|
29
|
+
() => buildStatusMessage("true", "play", "green"),
|
|
30
|
+
],
|
|
31
|
+
[
|
|
32
|
+
({ is_enabled, _is_deleted }) => !is_enabled,
|
|
33
|
+
() => buildStatusMessage("false", "pause", "orange"),
|
|
34
|
+
],
|
|
35
|
+
])({
|
|
36
|
+
is_enabled: action?.is_enabled,
|
|
37
|
+
is_deleted: action?.deleted_at,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
return _.isEmpty(action) ? null : (
|
|
41
|
+
<Grid>
|
|
42
|
+
<Grid.Column width={8}>
|
|
43
|
+
<Header as="h2">
|
|
44
|
+
<Icon name="caret square right" circular />
|
|
45
|
+
<Header.Content>
|
|
46
|
+
{action.name}
|
|
47
|
+
<Header.Subheader>{statusMessage}</Header.Subheader>
|
|
48
|
+
</Header.Content>
|
|
49
|
+
</Header>
|
|
50
|
+
</Grid.Column>
|
|
51
|
+
<Grid.Column width={8} textAlign="right">
|
|
52
|
+
{!action?.deleted_at ? (
|
|
53
|
+
<ActionActions action={action} actionMutate={actionMutate} />
|
|
54
|
+
) : null}
|
|
55
|
+
</Grid.Column>
|
|
56
|
+
</Grid>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
ActionHeader.propTypes = {
|
|
61
|
+
action: PropTypes.object.isRequired,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const ActionView = ({ action, actionMutate }) => {
|
|
65
|
+
return action ? (
|
|
66
|
+
<>
|
|
67
|
+
<ActionBreadcrumbs text={action.name} />
|
|
68
|
+
<Segment>
|
|
69
|
+
<ActionHeader action={action} actionMutate={actionMutate} />
|
|
70
|
+
<ActionDetail action={action} />
|
|
71
|
+
</Segment>
|
|
72
|
+
</>
|
|
73
|
+
) : null;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
ActionView.propTypes = {
|
|
77
|
+
action: PropTypes.object.isRequired,
|
|
78
|
+
loading: PropTypes.bool,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const Action = () => {
|
|
82
|
+
const { id } = useParams();
|
|
83
|
+
const { action, loading, mutate } = useAction(id);
|
|
84
|
+
if (loading) return <Loading />;
|
|
85
|
+
return _.isEmpty(action) ? null : (
|
|
86
|
+
<ActionView action={action} actionMutate={mutate} />
|
|
87
|
+
);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export default Action;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import PropTypes from "prop-types";
|
|
3
|
+
import { useIntl } from "react-intl";
|
|
4
|
+
import { Link } from "react-router-dom";
|
|
5
|
+
import { linkTo, ACTIONS } from "@truedat/core/routes";
|
|
6
|
+
import { useHistory } from "react-router-dom";
|
|
7
|
+
import { Button } from "semantic-ui-react";
|
|
8
|
+
import { ConfirmModal, GroupActions } from "@truedat/core/components";
|
|
9
|
+
import { useActionDelete, useActionSetActive } from "../../hooks/useActions";
|
|
10
|
+
|
|
11
|
+
export const ConfirmToggleModal = ({ onConfirm, action: actionData }) => {
|
|
12
|
+
const { formatMessage } = useIntl();
|
|
13
|
+
const { is_enabled: active, name } = actionData || {};
|
|
14
|
+
const action = active ? "disable" : "enable";
|
|
15
|
+
const icon = active ? "pause" : "play";
|
|
16
|
+
return (
|
|
17
|
+
<ConfirmModal
|
|
18
|
+
icon={icon}
|
|
19
|
+
trigger={
|
|
20
|
+
<Button
|
|
21
|
+
icon={icon}
|
|
22
|
+
content={formatMessage({ id: `ai.actions.actions.${action}` })}
|
|
23
|
+
/>
|
|
24
|
+
}
|
|
25
|
+
header={formatMessage({
|
|
26
|
+
id: `ai.actions.actions.${action}.confirmation.header`,
|
|
27
|
+
})}
|
|
28
|
+
size="small"
|
|
29
|
+
content={formatMessage(
|
|
30
|
+
{ id: `ai.actions.actions.${action}.confirmation.content` },
|
|
31
|
+
{ name: <i>{name}</i> }
|
|
32
|
+
)}
|
|
33
|
+
onConfirm={() => onConfirm()}
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
ConfirmToggleModal.propTypes = {
|
|
39
|
+
action: PropTypes.object,
|
|
40
|
+
onConfirm: PropTypes.func,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const ConfirmDeleteModal = ({ handleDelete, name, id }) => {
|
|
44
|
+
const { formatMessage } = useIntl();
|
|
45
|
+
return (
|
|
46
|
+
<ConfirmModal
|
|
47
|
+
icon="trash"
|
|
48
|
+
trigger={
|
|
49
|
+
<Button
|
|
50
|
+
icon="trash"
|
|
51
|
+
content={formatMessage({ id: "ai.actions.actions.delete" })}
|
|
52
|
+
/>
|
|
53
|
+
}
|
|
54
|
+
header={formatMessage({
|
|
55
|
+
id: "ai.actions.actions.delete.confirmation.header",
|
|
56
|
+
})}
|
|
57
|
+
size="small"
|
|
58
|
+
content={formatMessage(
|
|
59
|
+
{ id: "ai.actions.actions.delete.confirmation.content" },
|
|
60
|
+
{ name: <i>{name}</i> }
|
|
61
|
+
)}
|
|
62
|
+
onConfirm={() => handleDelete({ id })}
|
|
63
|
+
/>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
ConfirmDeleteModal.propTypes = {
|
|
68
|
+
handleDelete: PropTypes.func,
|
|
69
|
+
name: PropTypes.string,
|
|
70
|
+
id: PropTypes.number,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export const ActionActions = ({ action, actionMutate }) => {
|
|
74
|
+
const { formatMessage } = useIntl();
|
|
75
|
+
const history = useHistory();
|
|
76
|
+
const { is_enabled: active, id, name } = action || {};
|
|
77
|
+
|
|
78
|
+
const { trigger: handleDelete } = useActionDelete({ id: id });
|
|
79
|
+
const { trigger: enableAction } = useActionSetActive({
|
|
80
|
+
id: id,
|
|
81
|
+
active: true,
|
|
82
|
+
});
|
|
83
|
+
const { trigger: disableAction } = useActionSetActive({
|
|
84
|
+
id: id,
|
|
85
|
+
active: false,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const handleSetActive = active ? disableAction : enableAction;
|
|
89
|
+
|
|
90
|
+
const onConfirmDelete = async () => {
|
|
91
|
+
await handleDelete();
|
|
92
|
+
history.push(ACTIONS);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const onConfirmToggle = async () => {
|
|
96
|
+
await handleSetActive();
|
|
97
|
+
actionMutate();
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const edit = {
|
|
101
|
+
key: "edit",
|
|
102
|
+
icon: "edit",
|
|
103
|
+
text: formatMessage({ id: "ai.actions.actions.edit" }),
|
|
104
|
+
value: "edit",
|
|
105
|
+
as: Link,
|
|
106
|
+
to: linkTo.ACTION_EDIT({ id }),
|
|
107
|
+
};
|
|
108
|
+
const toggle = {
|
|
109
|
+
key: "toggle",
|
|
110
|
+
value: "toggle",
|
|
111
|
+
as: ConfirmToggleModal,
|
|
112
|
+
onConfirm: onConfirmToggle,
|
|
113
|
+
action,
|
|
114
|
+
};
|
|
115
|
+
const remove = {
|
|
116
|
+
key: "delete",
|
|
117
|
+
icon: "trash",
|
|
118
|
+
value: "delete",
|
|
119
|
+
as: ConfirmDeleteModal,
|
|
120
|
+
handleDelete: onConfirmDelete,
|
|
121
|
+
name,
|
|
122
|
+
id,
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const actions = [edit, toggle, remove];
|
|
126
|
+
return <GroupActions key={action?.is_enabled} availableActions={actions} />;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
ActionActions.propTypes = {
|
|
130
|
+
action: PropTypes.object.isRequired,
|
|
131
|
+
mutate: PropTypes.func,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export default ActionActions;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import PropTypes from "prop-types";
|
|
3
|
+
import { Breadcrumb } from "semantic-ui-react";
|
|
4
|
+
import { Link } from "react-router-dom";
|
|
5
|
+
import { FormattedMessage } from "react-intl";
|
|
6
|
+
import { ACTIONS } from "@truedat/core/routes";
|
|
7
|
+
|
|
8
|
+
export const ActionBreadcrumbs = ({ text }) => (
|
|
9
|
+
<Breadcrumb>
|
|
10
|
+
<Breadcrumb.Section as={Link} to={ACTIONS} active={false}>
|
|
11
|
+
<FormattedMessage id="navigation.admin.actions" />
|
|
12
|
+
</Breadcrumb.Section>
|
|
13
|
+
<Breadcrumb.Divider icon="right angle" />
|
|
14
|
+
<Breadcrumb.Section active>{text}</Breadcrumb.Section>
|
|
15
|
+
</Breadcrumb>
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
ActionBreadcrumbs.propTypes = {
|
|
19
|
+
text: PropTypes.string,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default ActionBreadcrumbs;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
import { FormattedMessage } from "react-intl";
|
|
5
|
+
import { Segment, List, Divider } from "semantic-ui-react";
|
|
6
|
+
|
|
7
|
+
const DynamicFormViewer = React.lazy(() =>
|
|
8
|
+
import("@truedat/df/components/DynamicFormViewer")
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
export const ActionDetail = ({ action }) => {
|
|
12
|
+
const user = _.path("user.full_name")(action);
|
|
13
|
+
return (
|
|
14
|
+
<Segment attached="bottom">
|
|
15
|
+
<List size="big" relaxed>
|
|
16
|
+
<List.Item>
|
|
17
|
+
<List.Header className="dynamic-field-header">
|
|
18
|
+
<FormattedMessage id="ai.actions.actions.form.user" />
|
|
19
|
+
</List.Header>
|
|
20
|
+
<List.Content>{user}</List.Content>
|
|
21
|
+
</List.Item>
|
|
22
|
+
<List.Item>
|
|
23
|
+
<List.Header className="dynamic-field-header">
|
|
24
|
+
<FormattedMessage id="ai.actions.form.type" />
|
|
25
|
+
</List.Header>
|
|
26
|
+
<List.Content>{action.type}</List.Content>
|
|
27
|
+
</List.Item>
|
|
28
|
+
</List>
|
|
29
|
+
<DynamicFormViewer
|
|
30
|
+
template={action.type}
|
|
31
|
+
content={action.dynamic_content}
|
|
32
|
+
/>
|
|
33
|
+
</Segment>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
ActionDetail.propTypes = {
|
|
38
|
+
action: PropTypes.object.isRequired,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export default ActionDetail;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import { useParams } from "react-router-dom";
|
|
3
|
+
import {
|
|
4
|
+
Checkbox,
|
|
5
|
+
Container,
|
|
6
|
+
FormField,
|
|
7
|
+
Header,
|
|
8
|
+
Icon,
|
|
9
|
+
Segment,
|
|
10
|
+
} from "semantic-ui-react";
|
|
11
|
+
import { linkTo } from "@truedat/core/routes";
|
|
12
|
+
import { FormattedMessage, useIntl } from "react-intl";
|
|
13
|
+
import { useTemplate } from "@truedat/core/hooks";
|
|
14
|
+
import { useAction, useActionUpdate } from "../../hooks/useActions";
|
|
15
|
+
import ActionBreadcrumbs from "./ActionBreadcrumbs";
|
|
16
|
+
import ActionForm from "./ActionForm";
|
|
17
|
+
|
|
18
|
+
const ActionEdit = () => {
|
|
19
|
+
const { formatMessage } = useIntl();
|
|
20
|
+
const { id } = useParams();
|
|
21
|
+
const { action } = useAction(id);
|
|
22
|
+
const { data: template } = useTemplate({
|
|
23
|
+
name: action?.type,
|
|
24
|
+
domainIds: [],
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const { trigger: triggerUpdate } = useActionUpdate(id);
|
|
28
|
+
const [isEnabled, setIsEnabled] = useState(true);
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (action?.deleted_at) history.push(linkTo.ACTION({ id: action?.id }));
|
|
32
|
+
setIsEnabled(action?.is_enabled);
|
|
33
|
+
}, [action]);
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<>
|
|
37
|
+
<ActionBreadcrumbs
|
|
38
|
+
text={formatMessage({ id: "ai.actions.actions.edit" })}
|
|
39
|
+
/>
|
|
40
|
+
<Container as={Segment} text className="ai-action-edit ai-action-new">
|
|
41
|
+
<div className="ai-action-edit-header">
|
|
42
|
+
<Header as="h2">
|
|
43
|
+
<Icon name="caret square right" />
|
|
44
|
+
<Header.Content>
|
|
45
|
+
{formatMessage({ id: "ai.actions.actions.edit" })}
|
|
46
|
+
</Header.Content>
|
|
47
|
+
</Header>
|
|
48
|
+
<FormField className="ai-action-edit-header-active">
|
|
49
|
+
<label>
|
|
50
|
+
<FormattedMessage id="ai.actions.actions.form.active" />
|
|
51
|
+
</label>
|
|
52
|
+
<Checkbox
|
|
53
|
+
toggle
|
|
54
|
+
checked={isEnabled}
|
|
55
|
+
onChange={(_e, { checked }) => setIsEnabled(checked)}
|
|
56
|
+
/>
|
|
57
|
+
</FormField>
|
|
58
|
+
</div>
|
|
59
|
+
<ActionForm
|
|
60
|
+
action={action}
|
|
61
|
+
template={template?.template}
|
|
62
|
+
isEnabled={isEnabled}
|
|
63
|
+
onSubmit={triggerUpdate}
|
|
64
|
+
/>
|
|
65
|
+
</Container>
|
|
66
|
+
</>
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export default ActionEdit;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React, { useEffect, useState } from "react";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
import { useHistory } from "react-router-dom";
|
|
5
|
+
import { HistoryBackButton } from "@truedat/core/components";
|
|
6
|
+
import { Form, Header, Label, Button } from "semantic-ui-react";
|
|
7
|
+
import { FormattedMessage, useIntl } from "react-intl";
|
|
8
|
+
import { linkTo } from "@truedat/core/routes";
|
|
9
|
+
import { useAgents } from "@truedat/auth/hooks/useUsers";
|
|
10
|
+
|
|
11
|
+
const SelectableDynamicForm = React.lazy(() =>
|
|
12
|
+
import("@truedat/df/components/SelectableDynamicForm")
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
const ActionForm = ({
|
|
16
|
+
action: actionData,
|
|
17
|
+
template: templateData,
|
|
18
|
+
isEnabled,
|
|
19
|
+
onSubmit,
|
|
20
|
+
}) => {
|
|
21
|
+
const { formatMessage } = useIntl();
|
|
22
|
+
const history = useHistory();
|
|
23
|
+
const { agents } = useAgents();
|
|
24
|
+
|
|
25
|
+
const [action, setAction] = useState();
|
|
26
|
+
const [template, setTemplate] = useState();
|
|
27
|
+
const [dynamicContent, setDynamicContent] = useState();
|
|
28
|
+
const [isValid, setIsValid] = useState(false);
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (_.size(agents) === 1) {
|
|
32
|
+
handleActionChange(null, { name: "user_id", value: agents[0].id });
|
|
33
|
+
}
|
|
34
|
+
}, [agents]);
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
setAction(actionData);
|
|
38
|
+
setDynamicContent(actionData?.dynamic_content);
|
|
39
|
+
}, [actionData]);
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
setTemplate(templateData);
|
|
43
|
+
}, [templateData]);
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
validateAction(isValid);
|
|
47
|
+
}, [action]);
|
|
48
|
+
|
|
49
|
+
const handleTemplateChange = (template) => {
|
|
50
|
+
if (template) setTemplate(template);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const isEditing = _.prop("id")(actionData) ? true : false;
|
|
54
|
+
|
|
55
|
+
const agentOptions = _.map(({ id, full_name }) => ({
|
|
56
|
+
text: full_name,
|
|
57
|
+
value: id,
|
|
58
|
+
}))(agents);
|
|
59
|
+
|
|
60
|
+
// Validation
|
|
61
|
+
const validateAction = (valid) => {
|
|
62
|
+
setIsValid(_.isEmpty(valid) && !!action?.name && !!action?.user_id);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const handleActionChange = (_e, { name, value }) => {
|
|
66
|
+
setAction({ ...action, [name]: value });
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const handleDynamicContentChange = ({ content, valid }) => {
|
|
70
|
+
setDynamicContent(content);
|
|
71
|
+
validateAction(valid);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Submit
|
|
75
|
+
const handleSubmit = () => {
|
|
76
|
+
const payload = {
|
|
77
|
+
...action,
|
|
78
|
+
dynamic_content: dynamicContent,
|
|
79
|
+
type: template?.name,
|
|
80
|
+
is_enabled: isEnabled,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
onSubmit(payload).then((response) => {
|
|
84
|
+
history.push(linkTo.ACTION({ id: response?.data?.data?.id }));
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<>
|
|
90
|
+
<Form role="form">
|
|
91
|
+
<Form.Field required>
|
|
92
|
+
<label>
|
|
93
|
+
<FormattedMessage id="ai.actions.actions.form.name" />
|
|
94
|
+
{_.isEmpty(_.prop("name")(action)) ? (
|
|
95
|
+
<Label pointing="left">
|
|
96
|
+
<FormattedMessage id="template.form.validation.empty_required" />
|
|
97
|
+
</Label>
|
|
98
|
+
) : null}
|
|
99
|
+
</label>
|
|
100
|
+
<Form.Input
|
|
101
|
+
name="name"
|
|
102
|
+
value={_.prop("name")(action) || ""}
|
|
103
|
+
onChange={handleActionChange}
|
|
104
|
+
/>
|
|
105
|
+
</Form.Field>
|
|
106
|
+
<Form.Field required>
|
|
107
|
+
<label>
|
|
108
|
+
<FormattedMessage id="ai.actions.actions.form.user" />
|
|
109
|
+
{!_.isInteger(_.prop("user_id")(action)) ? (
|
|
110
|
+
<Label pointing="left">
|
|
111
|
+
<FormattedMessage id="template.form.validation.empty_required" />
|
|
112
|
+
</Label>
|
|
113
|
+
) : null}
|
|
114
|
+
</label>
|
|
115
|
+
<Form.Dropdown
|
|
116
|
+
name="user_id"
|
|
117
|
+
placeholder={formatMessage({ id: "fields.dropdown.placeholder" })}
|
|
118
|
+
value={_.prop("user_id")(action)}
|
|
119
|
+
options={agentOptions}
|
|
120
|
+
onChange={handleActionChange}
|
|
121
|
+
fluid
|
|
122
|
+
selection
|
|
123
|
+
/>
|
|
124
|
+
</Form.Field>
|
|
125
|
+
<SelectableDynamicForm
|
|
126
|
+
disableSelector={isEditing}
|
|
127
|
+
disabled={false}
|
|
128
|
+
scope="actions"
|
|
129
|
+
domainIds={null}
|
|
130
|
+
required
|
|
131
|
+
content={dynamicContent}
|
|
132
|
+
name={template?.name}
|
|
133
|
+
onChange={handleDynamicContentChange}
|
|
134
|
+
label={formatMessage({
|
|
135
|
+
id: "ai.actions.form.type",
|
|
136
|
+
defaultMessage: formatMessage({ id: "template.selector.label" }),
|
|
137
|
+
})}
|
|
138
|
+
onNameChange={(_a) => null}
|
|
139
|
+
onTemplateChange={isEditing ? () => null : handleTemplateChange}
|
|
140
|
+
selectedTemplate={template}
|
|
141
|
+
/>
|
|
142
|
+
</Form>
|
|
143
|
+
<div className="ai-action-edit-form-actions actions">
|
|
144
|
+
<HistoryBackButton content={formatMessage({ id: "actions.cancel" })} />
|
|
145
|
+
<Button
|
|
146
|
+
role="button"
|
|
147
|
+
primary
|
|
148
|
+
disabled={!isValid}
|
|
149
|
+
onClick={handleSubmit}
|
|
150
|
+
content={
|
|
151
|
+
isEditing
|
|
152
|
+
? formatMessage({ id: `actions.update` })
|
|
153
|
+
: formatMessage({ id: `actions.create` })
|
|
154
|
+
}
|
|
155
|
+
/>
|
|
156
|
+
</div>
|
|
157
|
+
</>
|
|
158
|
+
);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
ActionForm.propTypes = {
|
|
162
|
+
action: PropTypes.object,
|
|
163
|
+
template: PropTypes.object,
|
|
164
|
+
isEnabled: PropTypes.bool,
|
|
165
|
+
onSubmit: PropTypes.func,
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
export default ActionForm;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Checkbox,
|
|
4
|
+
Container,
|
|
5
|
+
FormField,
|
|
6
|
+
Header,
|
|
7
|
+
Icon,
|
|
8
|
+
Segment,
|
|
9
|
+
} from "semantic-ui-react";
|
|
10
|
+
import { FormattedMessage, useIntl } from "react-intl";
|
|
11
|
+
import { useActionCreate } from "../../hooks/useActions";
|
|
12
|
+
import ActionBreadcrumbs from "./ActionBreadcrumbs";
|
|
13
|
+
import ActionForm from "./ActionForm";
|
|
14
|
+
|
|
15
|
+
const ActionNew = () => {
|
|
16
|
+
const { formatMessage } = useIntl();
|
|
17
|
+
const { trigger: triggerCreate } = useActionCreate();
|
|
18
|
+
const [isEnabled, setIsEnabled] = useState(true);
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<>
|
|
22
|
+
<ActionBreadcrumbs
|
|
23
|
+
text={formatMessage({ id: "ai.actions.actions.create" })}
|
|
24
|
+
/>
|
|
25
|
+
<Container as={Segment} text className="ai-action-edit ai-action-new">
|
|
26
|
+
<div className="ai-action-edit-header">
|
|
27
|
+
<Header as="h2">
|
|
28
|
+
<Icon name="caret square right" />
|
|
29
|
+
<Header.Content>
|
|
30
|
+
{formatMessage({ id: "ai.actions.actions.create" })}
|
|
31
|
+
</Header.Content>
|
|
32
|
+
</Header>
|
|
33
|
+
<FormField className="ai-action-edit-header-active">
|
|
34
|
+
<label>
|
|
35
|
+
<FormattedMessage id="ai.actions.actions.form.active" />
|
|
36
|
+
</label>
|
|
37
|
+
<Checkbox
|
|
38
|
+
toggle
|
|
39
|
+
checked={isEnabled}
|
|
40
|
+
onChange={(_e, { checked }) => setIsEnabled(checked)}
|
|
41
|
+
/>
|
|
42
|
+
</FormField>
|
|
43
|
+
</div>
|
|
44
|
+
<ActionForm isEnabled={isEnabled} onSubmit={triggerCreate} />
|
|
45
|
+
</Container>
|
|
46
|
+
</>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export default ActionNew;
|