@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.
Files changed (33) hide show
  1. package/package.json +3 -3
  2. package/src/api.js +8 -0
  3. package/src/components/AiRoutes.js +26 -0
  4. package/src/components/actions/Action.js +90 -0
  5. package/src/components/actions/ActionActions.js +134 -0
  6. package/src/components/actions/ActionBreadcrumbs.js +22 -0
  7. package/src/components/actions/ActionDetail.js +41 -0
  8. package/src/components/actions/ActionEdit.js +70 -0
  9. package/src/components/actions/ActionForm.js +168 -0
  10. package/src/components/actions/ActionNew.js +50 -0
  11. package/src/components/actions/Actions.js +61 -0
  12. package/src/components/actions/ActionsContext.js +52 -0
  13. package/src/components/actions/ActionsTable.js +124 -0
  14. package/src/components/actions/__tests__/Action.spec.js +59 -0
  15. package/src/components/actions/__tests__/ActionActions.spec.js +47 -0
  16. package/src/components/actions/__tests__/ActionBreadcrumbs.spec.js +21 -0
  17. package/src/components/actions/__tests__/ActionDetail.spec.js +106 -0
  18. package/src/components/actions/__tests__/ActionEdit.spec.js +133 -0
  19. package/src/components/actions/__tests__/ActionForm.spec.js +146 -0
  20. package/src/components/actions/__tests__/ActionNew.spec.js +113 -0
  21. package/src/components/actions/__tests__/Actions.spec.js +21 -0
  22. package/src/components/actions/__tests__/ActionsTable.spec.js +72 -0
  23. package/src/components/actions/__tests__/__snapshots__/Action.spec.js.snap +159 -0
  24. package/src/components/actions/__tests__/__snapshots__/ActionActions.spec.js.snap +57 -0
  25. package/src/components/actions/__tests__/__snapshots__/ActionBreadcrumbs.spec.js.snap +25 -0
  26. package/src/components/actions/__tests__/__snapshots__/ActionDetail.spec.js.snap +46 -0
  27. package/src/components/actions/__tests__/__snapshots__/ActionEdit.spec.js.snap +316 -0
  28. package/src/components/actions/__tests__/__snapshots__/ActionForm.spec.js.snap +326 -0
  29. package/src/components/actions/__tests__/__snapshots__/ActionNew.spec.js.snap +518 -0
  30. package/src/components/actions/__tests__/__snapshots__/Actions.spec.js.snap +63 -0
  31. package/src/components/actions/__tests__/__snapshots__/ActionsTable.spec.js.snap +121 -0
  32. package/src/hooks/useActions.js +63 -0
  33. 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.11.1",
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.11.1",
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": "a684646f8503335b666ea82b85a6c0aa8fbd2336"
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;