@truedat/bg 6.0.2 → 6.0.4

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 (27) hide show
  1. package/package.json +6 -6
  2. package/src/concepts/api.js +3 -0
  3. package/src/concepts/components/ConceptDetails.js +1 -3
  4. package/src/concepts/components/ConceptEdit.js +124 -110
  5. package/src/concepts/components/ConceptForm.js +154 -123
  6. package/src/concepts/components/ConceptRoutes.js +9 -0
  7. package/src/concepts/components/ConceptsActions.js +4 -1
  8. package/src/concepts/components/ConceptsUpdateButton.js +3 -2
  9. package/src/concepts/components/ConceptsUploadButton.js +21 -2
  10. package/src/concepts/components/ConceptsUploadEvents.js +24 -0
  11. package/src/concepts/components/ConceptsUploadEventsTable.js +166 -0
  12. package/src/concepts/components/__tests__/ConceptForm.spec.js +23 -1
  13. package/src/concepts/components/__tests__/ConceptsActions.spec.js +1 -0
  14. package/src/concepts/components/__tests__/ConceptsUploadButton.spec.js +35 -13
  15. package/src/concepts/components/__tests__/ConceptsUploadEventsTable.spec.js +98 -0
  16. package/src/concepts/components/__tests__/__snapshots__/ConceptsUploadButton.spec.js.snap +75 -24
  17. package/src/concepts/components/__tests__/__snapshots__/ConceptsUploadEventsTable.spec.js.snap +115 -0
  18. package/src/concepts/hooks/useUploadEvents.js +11 -0
  19. package/src/concepts/reducers/uploadConceptsFile.js +0 -1
  20. package/src/concepts/selectors/__tests__/getConceptUploadEventColumns.spec.js +20 -0
  21. package/src/concepts/selectors/getConceptUploadEventColumns.js +72 -0
  22. package/src/concepts/selectors/index.js +4 -0
  23. package/src/concepts/styles/conceptsUploadEventColumns.less +24 -0
  24. package/src/messages/en.js +45 -9
  25. package/src/messages/es.js +46 -9
  26. package/src/reducers/__tests__/bgMessage.spec.js +12 -9
  27. package/src/reducers/bgMessage.js +12 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/bg",
3
- "version": "6.0.2",
3
+ "version": "6.0.4",
4
4
  "description": "Truedat Web Business Glossary",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -34,7 +34,7 @@
34
34
  "@testing-library/jest-dom": "^5.16.5",
35
35
  "@testing-library/react": "^12.0.0",
36
36
  "@testing-library/user-event": "^13.2.1",
37
- "@truedat/test": "6.0.2",
37
+ "@truedat/test": "6.0.4",
38
38
  "babel-jest": "^28.1.0",
39
39
  "babel-plugin-dynamic-import-node": "^2.3.3",
40
40
  "babel-plugin-lodash": "^3.3.4",
@@ -86,9 +86,9 @@
86
86
  ]
87
87
  },
88
88
  "dependencies": {
89
- "@truedat/core": "6.0.2",
90
- "@truedat/df": "6.0.2",
91
- "@truedat/lm": "6.0.2",
89
+ "@truedat/core": "6.0.4",
90
+ "@truedat/df": "6.0.4",
91
+ "@truedat/lm": "6.0.4",
92
92
  "decode-uri-component": "^0.2.2",
93
93
  "file-saver": "^2.0.5",
94
94
  "moment": "^2.29.4",
@@ -111,5 +111,5 @@
111
111
  "react-dom": ">= 16.8.6 < 17",
112
112
  "semantic-ui-react": ">= 2.0.3 < 2.2"
113
113
  },
114
- "gitHead": "dab67cd6f569ca6de2fd8e0b5de260cb5ee261c3"
114
+ "gitHead": "eee544490dfa90647cfdf8a3d442054394027553"
115
115
  }
@@ -16,6 +16,8 @@ const API_GET_CONCEPT_USER_FILTERS =
16
16
  "/api/business_concept_user_filters/user/me";
17
17
  const API_BUSINESS_CONCEPT_BULK_UPDATE =
18
18
  "/api/business_concept_versions/bulk_update";
19
+ const API_BUSINESS_CONCEPT_BULK_UPLOAD =
20
+ "/api/business_concepts/bulk_upload_event";
19
21
  const API_BUSINESS_CONCEPT_SET_CONFIDENTIAL =
20
22
  "/api/business_concept_versions/:id/set_confidential";
21
23
 
@@ -32,5 +34,6 @@ export {
32
34
  API_CONCEPT_USER_FILTERS,
33
35
  API_GET_CONCEPT_USER_FILTERS,
34
36
  API_BUSINESS_CONCEPT_BULK_UPDATE,
37
+ API_BUSINESS_CONCEPT_BULK_UPLOAD,
35
38
  API_BUSINESS_CONCEPT_SET_CONFIDENTIAL,
36
39
  };
@@ -1,9 +1,7 @@
1
1
  import React from "react";
2
2
  import PropTypes from "prop-types";
3
3
  import { connect } from "react-redux";
4
- import { Segment, Header } from "semantic-ui-react";
5
- import { FormattedMessage } from "react-intl";
6
- import { RichTextEditor } from "@truedat/core/components";
4
+ import { Segment } from "semantic-ui-react";
7
5
 
8
6
  const DynamicFormViewer = React.lazy(() =>
9
7
  import("@truedat/df/components/DynamicFormViewer")
@@ -1,5 +1,5 @@
1
1
  import _ from "lodash/fp";
2
- import React from "react";
2
+ import React, { useState, useEffect } from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import {
5
5
  Button,
@@ -14,8 +14,12 @@ import { compose } from "redux";
14
14
  import { connect } from "react-redux";
15
15
  import { injectIntl, FormattedMessage } from "react-intl";
16
16
  import { HistoryBackButton } from "@truedat/core/components";
17
+ import { apiJsonPost } from "@truedat/core/services/api";
17
18
  import { selectTemplate, selectDomain } from "@truedat/df/routines";
18
19
  import { applyTemplate } from "@truedat/df/utils";
20
+ import { useAvailabilityCheck } from "@truedat/ai/hooks/useSuggestions";
21
+ import { API_SUGGESTIONS_REQUEST } from "@truedat/ai/api";
22
+ import SuggestionsWidget from "@truedat/ai/components/suggestions/SuggestionsWidget";
19
23
  import { conceptAction } from "../routines";
20
24
 
21
25
  const DynamicForm = React.lazy(() =>
@@ -28,135 +32,145 @@ const TemplateLoader = React.lazy(() =>
28
32
 
29
33
  const actionKey = "update";
30
34
 
31
- const staticFields = ["type", "name"];
32
-
33
- const initialState = {
34
- type: "",
35
- name: "",
36
- };
37
-
38
35
  const isNonEmptyString = _.flow(_.trim, _.negate(_.isEmpty));
39
36
  const isValid = _.conforms({
40
37
  name: isNonEmptyString,
41
38
  });
42
39
 
43
- export class ConceptEdit extends React.Component {
44
- static propTypes = {
45
- action: PropTypes.object,
46
- applyTemplate: PropTypes.func,
47
- concept: PropTypes.object,
48
- conceptAction: PropTypes.func.isRequired,
49
- intl: PropTypes.object,
50
- loading: PropTypes.bool,
51
- selectDomain: PropTypes.func,
52
- selectTemplate: PropTypes.func,
53
- template: PropTypes.object,
54
- templateId: PropTypes.number,
55
- };
56
-
57
- state = initialState;
58
-
59
- componentDidMount() {
60
- this.setStateFromProps();
61
- }
62
-
63
- componentDidUpdate() {
64
- this.setStateFromProps();
65
- }
40
+ export const ConceptEdit = ({
41
+ concept,
42
+ loading,
43
+ intl: { formatMessage },
44
+ template,
45
+ action,
46
+ conceptAction,
47
+ applyTemplate,
48
+ selectDomain,
49
+ selectTemplate,
50
+ templateId,
51
+ }) => {
52
+ const { content: propContent, name: propName, domain } = concept;
53
+ const [content, setContent] = useState(propContent);
54
+ const [name, setName] = useState(propName);
55
+ const [hasSuggestions, setHasSuggestions] = useState(false);
56
+
57
+ const { trigger: checkSuggestionAvailability } = useAvailabilityCheck();
58
+
59
+ useEffect(() => {
60
+ const domain_id = _.prop("id")(domain);
61
+ selectDomain({ id: domain_id });
62
+ selectTemplate({ id: templateId });
63
+ checkSuggestionAvailability({
64
+ template_id: templateId,
65
+ domain_ids: [domain.id],
66
+ resource_type: "business_concept",
67
+ }).then((prop) =>
68
+ setHasSuggestions(_.prop("data.data.status")(prop) == "ok")
69
+ );
70
+ }, []);
66
71
 
67
- setStateFromProps() {
68
- if (
69
- _.isUndefined(this.state.content) &&
70
- !_.isUndefined(this.props.concept.content)
71
- ) {
72
- const { content, domain, name, type } = this.props.concept;
73
- this.setState({ content, name, type });
74
- const { selectDomain, selectTemplate, templateId } = this.props;
75
- const domain_id = _.prop("id")(domain);
76
- selectDomain({ id: domain_id });
77
- selectTemplate({ id: templateId });
78
- }
72
+ if (_.isNil(content)) {
73
+ return null;
79
74
  }
80
- handleChange = (e, { name, value }) => {
81
- e && e.preventDefault();
82
-
83
- if (staticFields.includes(name)) {
84
- this.setState({ [name]: value });
85
- }
86
- };
87
75
 
88
- handleContentChange = (content) => this.setState({ content });
76
+ const isInvalid = () => _.negate(isValid)({ name });
89
77
 
90
- isInvalid = () => _.negate(isValid)(this.state);
91
-
92
- handleSubmit = (e) => {
78
+ const handleSubmit = (e) => {
93
79
  e.preventDefault();
94
- const { action, concept, conceptAction, applyTemplate } = this.props;
95
- const businessConceptVersion = _.pick(["name", "type"])(this.state);
96
80
 
97
- const content = applyTemplate(this.state?.content, concept?.domain?.id);
81
+ const businessConceptVersion = { name, type: concept.type };
82
+ const newContent = applyTemplate(content, domain?.id);
98
83
 
99
84
  conceptAction({
100
85
  action: actionKey,
101
86
  ...action,
102
- business_concept_version: { ...businessConceptVersion, content },
87
+ business_concept_version: {
88
+ ...businessConceptVersion,
89
+ content: newContent,
90
+ },
103
91
  });
104
92
  };
105
93
 
106
- render() {
107
- const {
108
- loading,
109
- intl: { formatMessage },
110
- template,
111
- } = this.props;
112
-
113
- const { content, name } = this.state;
114
- if (_.isNil(content)) {
115
- return null;
116
- }
117
-
118
- return (
119
- <Container as={Segment} text>
120
- <Header as="h2">
121
- <Icon name="book" />
122
- <Header.Content>
123
- <FormattedMessage id="concepts.header.edit" />
124
- </Header.Content>
125
- </Header>
126
- <TemplateLoader />
127
- <Form loading={loading}>
128
- <Form.Field required>
129
- <label>
130
- {formatMessage({ id: "concepts.props.name" })}
131
- {_.isEmpty(name) ? (
132
- <Label pointing="left">
133
- <FormattedMessage id="template.form.validation.empty_required" />
134
- </Label>
135
- ) : null}
136
- </label>
137
- <Form.Input name="name" value={name} onChange={this.handleChange} />
138
- </Form.Field>
139
- {_.isEmpty(template) || _.isNil(content) ? null : (
140
- <DynamicForm
141
- onChange={this.handleContentChange}
142
- content={content}
143
- isModification={true}
144
- />
145
- )}
146
- <Button
147
- primary
148
- onClick={this.handleSubmit}
149
- disabled={this.isInvalid()}
150
- content={formatMessage({ id: "actions.save" })}
94
+ const requestAiSuggestion = (callback) => {
95
+ const body = {
96
+ resource_type: "business_concept",
97
+ resource_body: {
98
+ name,
99
+ domain: domain.name,
100
+ type: template.name,
101
+ },
102
+ domain_ids: [domain.id],
103
+ template_id: templateId,
104
+ };
105
+ apiJsonPost(API_SUGGESTIONS_REQUEST, body).then(callback);
106
+ };
107
+
108
+ return (
109
+ <Container as={Segment} text>
110
+ <Header as="h2">
111
+ <Icon name="book" />
112
+ <Header.Content>
113
+ <FormattedMessage id="concepts.header.edit" />
114
+ </Header.Content>
115
+ </Header>
116
+ <TemplateLoader />
117
+ <Form loading={loading}>
118
+ <Form.Field required>
119
+ <label>
120
+ {formatMessage({ id: "concepts.props.name" })}
121
+ {_.isEmpty(name) ? (
122
+ <Label pointing="left">
123
+ <FormattedMessage id="template.form.validation.empty_required" />
124
+ </Label>
125
+ ) : null}
126
+ </label>
127
+ <Form.Input
128
+ name="name"
129
+ value={name}
130
+ onChange={(_e, { value }) => setName(value)}
151
131
  />
152
- <HistoryBackButton
153
- content={formatMessage({ id: "actions.cancel" })}
132
+ </Form.Field>
133
+ {hasSuggestions ? (
134
+ <SuggestionsWidget
135
+ requestAiSuggestion={requestAiSuggestion}
136
+ template={template}
137
+ applySuggestions={(suggestions) =>
138
+ setContent({ ...content, ...suggestions })
139
+ }
140
+ isModification={true}
154
141
  />
155
- </Form>
156
- </Container>
157
- );
158
- }
159
- }
142
+ ) : null}
143
+ {_.isEmpty(template) || _.isNil(content) ? null : (
144
+ <DynamicForm
145
+ onChange={setContent}
146
+ content={content}
147
+ isModification={true}
148
+ />
149
+ )}
150
+ <Button
151
+ primary
152
+ onClick={handleSubmit}
153
+ disabled={isInvalid()}
154
+ content={formatMessage({ id: "actions.save" })}
155
+ />
156
+ <HistoryBackButton content={formatMessage({ id: "actions.cancel" })} />
157
+ </Form>
158
+ </Container>
159
+ );
160
+ };
161
+
162
+ ConceptEdit.propTypes = {
163
+ action: PropTypes.object,
164
+ applyTemplate: PropTypes.func,
165
+ concept: PropTypes.object,
166
+ conceptAction: PropTypes.func.isRequired,
167
+ intl: PropTypes.object,
168
+ loading: PropTypes.bool,
169
+ selectDomain: PropTypes.func,
170
+ selectTemplate: PropTypes.func,
171
+ template: PropTypes.object,
172
+ templateId: PropTypes.number,
173
+ };
160
174
 
161
175
  const mapStateToProps = ({
162
176
  concept,
@@ -1,5 +1,5 @@
1
1
  import _ from "lodash/fp";
2
- import React from "react";
2
+ import React, { useState, useEffect } from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import {
5
5
  Button,
@@ -13,11 +13,11 @@ import {
13
13
  import { compose } from "redux";
14
14
  import { connect } from "react-redux";
15
15
  import { injectIntl, FormattedMessage } from "react-intl";
16
- import {
17
- DomainSelector,
18
- HistoryBackButton,
19
- RichTextEditor,
20
- } from "@truedat/core/components";
16
+ import { apiJsonPost } from "@truedat/core/services/api";
17
+ import { DomainSelector, HistoryBackButton } from "@truedat/core/components";
18
+ import { useAvailabilityCheck } from "@truedat/ai/hooks/useSuggestions";
19
+ import { API_SUGGESTIONS_REQUEST } from "@truedat/ai/api";
20
+ import SuggestionsWidget from "@truedat/ai/components/suggestions/SuggestionsWidget";
21
21
  import { conceptAction } from "../routines";
22
22
 
23
23
  const SelectableDynamicForm = React.lazy(() =>
@@ -26,64 +26,66 @@ const SelectableDynamicForm = React.lazy(() =>
26
26
 
27
27
  const actionKey = "create";
28
28
 
29
- const staticFields = ["name", "description"];
30
-
31
- const INITIAL_STATE = {
32
- name: "",
33
- description: {},
34
- content: {},
35
- domainsLoading: true,
36
- };
37
-
38
29
  const isNonEmptyString = _.flow(_.trim, _.negate(_.isEmpty));
39
30
  const isValid = _.conforms({
40
31
  name: isNonEmptyString,
41
32
  domain_id: _.isFinite,
42
- description: _.negate(_.isEmpty),
43
33
  type: isNonEmptyString,
44
34
  });
45
35
 
46
- export class ConceptForm extends React.Component {
47
- static propTypes = {
48
- action: PropTypes.object,
49
- conceptAction: PropTypes.func,
50
- conceptActionLoading: PropTypes.string,
51
- intl: PropTypes.object,
52
- };
53
-
54
- state = INITIAL_STATE;
55
-
56
- handleDomainSelected = (_e, { value }) => {
57
- this.setState({ domain_id: _.toInteger(value) });
58
- };
59
-
60
- handleEditorChange = (e, { value }) => {
61
- this.handleChange(null, { name: "description", value });
62
- };
63
-
64
- handleChange = (e, { name, value }) => {
65
- e && e.preventDefault();
66
-
67
- if (staticFields.includes(name)) {
68
- this.setState({ [name]: value });
36
+ const ConceptForm = ({
37
+ action,
38
+ conceptActionLoading,
39
+ conceptAction,
40
+ intl: { formatMessage },
41
+ }) => {
42
+ const [name, setName] = useState("");
43
+ const [content, setContent] = useState({});
44
+ const [type, setType] = useState();
45
+ const [template, setTemplate] = useState();
46
+ const [domainId, setDomainId] = useState();
47
+ const [domains, setDomains] = useState();
48
+ const [domainsLoading, setDomainsLoading] = useState(true);
49
+ const [hasSuggestions, setHasSuggestions] = useState(false);
50
+
51
+ const { trigger: checkSuggestionAvailability } = useAvailabilityCheck();
52
+
53
+ const loading = action && action.href && conceptActionLoading === action.href;
54
+ const templateDisabled = !domainId || !name;
55
+ const templateId = template?.id;
56
+
57
+ useEffect(() => {
58
+ if (templateId && domainId) {
59
+ checkSuggestionAvailability({
60
+ template_id: templateId,
61
+ domain_ids: [domainId],
62
+ resource_type: "business_concept",
63
+ }).then((prop) =>
64
+ setHasSuggestions(_.prop("data.data.status")(prop) == "ok")
65
+ );
69
66
  }
67
+ }, [templateId, domainId]);
68
+
69
+ const handleDomainSelected = (_e, { value }) =>
70
+ setDomainId(_.toInteger(value));
71
+ const handleContentChange = ({ content }) => setContent(content);
72
+ const handleDomainsLoaded = ({ domains }) => {
73
+ setDomains(domains);
74
+ setDomainsLoading(false);
70
75
  };
71
76
 
72
- handleContentChange = ({ content }) => this.setState({ content });
77
+ const isInvalid = () =>
78
+ _.negate(isValid)({ name, domain_id: domainId, type });
73
79
 
74
- handleDomainsLoaded = () => this.setState({ domainsLoading: false });
75
-
76
- isInvalid = () => _.negate(isValid)(this.state);
77
-
78
- handleSubmit = (e) => {
80
+ const handleSubmit = (e) => {
79
81
  e.preventDefault();
80
- const { action, conceptAction } = this.props;
81
- const business_concept_version = _.pick([
82
- "domain_id",
83
- "type",
84
- "content",
85
- ...staticFields,
86
- ])(this.state);
82
+
83
+ const business_concept_version = {
84
+ name,
85
+ domain_id: domainId,
86
+ type,
87
+ content,
88
+ };
87
89
 
88
90
  conceptAction({
89
91
  action: actionKey,
@@ -92,85 +94,114 @@ export class ConceptForm extends React.Component {
92
94
  });
93
95
  };
94
96
 
95
- render() {
96
- const {
97
- action,
98
- conceptActionLoading,
99
- intl: { formatMessage },
100
- } = this.props;
101
-
102
- const { name, description, content, type, domain_id, domainsLoading } =
103
- this.state;
104
-
105
- const loading =
106
- action && action.href && conceptActionLoading === action.href;
107
-
108
- return (
109
- <Container as={Segment} text>
110
- <Header as="h2">
111
- <Icon name="book" />
112
- <Header.Content>
113
- <FormattedMessage id="concepts.actions.create" />
114
- </Header.Content>
115
- </Header>
116
- <Form loading={loading || domainsLoading}>
117
- <Header
118
- as="h3"
119
- content={formatMessage({ id: "concepts.form.required_fields" })}
120
- />
121
- <DomainSelector
122
- action="createBusinessConcept"
123
- label={formatMessage({ id: "domain.selector.label" })}
124
- labels
125
- required
126
- onChange={this.handleDomainSelected}
127
- onLoad={this.handleDomainsLoaded}
128
- value={domain_id}
97
+ const requestAiSuggestion = (callback) => {
98
+ const domainName = _.flow(
99
+ _.find({ id: domainId }),
100
+ _.prop("name")
101
+ )(domains);
102
+ const body = {
103
+ resource_type: "business_concept",
104
+ resource_body: {
105
+ name,
106
+ domain: domainName,
107
+ type,
108
+ },
109
+ domain_ids: [domainId],
110
+ template_id: templateId,
111
+ };
112
+ apiJsonPost(API_SUGGESTIONS_REQUEST, body).then(callback);
113
+ };
114
+
115
+ return (
116
+ <Container as={Segment} text>
117
+ <Header as="h2">
118
+ <Icon name="book" />
119
+ <Header.Content>
120
+ <FormattedMessage id="concepts.actions.create" />
121
+ </Header.Content>
122
+ </Header>
123
+ <Form loading={loading || domainsLoading}>
124
+ <Header
125
+ as="h3"
126
+ content={formatMessage({ id: "concepts.form.required_fields" })}
127
+ />
128
+ <DomainSelector
129
+ action="createBusinessConcept"
130
+ label={formatMessage({ id: "domain.selector.label" })}
131
+ labels
132
+ required
133
+ onChange={handleDomainSelected}
134
+ onLoad={handleDomainsLoaded}
135
+ value={domainId}
136
+ />
137
+ <Form.Field required>
138
+ <label>
139
+ <FormattedMessage id="concepts.props.name" />
140
+ {_.isEmpty(name) ? (
141
+ <Label pointing="left">
142
+ <FormattedMessage id="template.form.validation.empty_required" />
143
+ </Label>
144
+ ) : null}
145
+ </label>
146
+ <Form.Input
147
+ name="name"
148
+ value={name}
149
+ onChange={(_e, { value }) => setName(value)}
129
150
  />
130
- <Form.Field required>
131
- <label>
132
- <FormattedMessage id="concepts.props.name" />
133
- {_.isEmpty(name) ? (
134
- <Label pointing="left">
135
- <FormattedMessage id="template.form.validation.empty_required" />
136
- </Label>
137
- ) : null}
138
- </label>
139
- <Form.Input name="name" value={name} onChange={this.handleChange} />
140
- </Form.Field>
141
- <SelectableDynamicForm
142
- scope="bg"
143
- domainIds={_.isNil(domain_id) ? null : [domain_id]}
144
- required
145
- header={
151
+ </Form.Field>
152
+ <SelectableDynamicForm
153
+ disabled={templateDisabled}
154
+ scope="bg"
155
+ domainIds={_.isNil(domainId) ? null : [domainId]}
156
+ required
157
+ header={
158
+ <>
146
159
  <Header
147
160
  as="h3"
148
161
  content={formatMessage({
149
162
  id: "concepts.form.aditional_fields",
150
163
  })}
151
164
  />
152
- }
153
- content={content}
154
- name={type}
155
- onChange={this.handleContentChange}
156
- onNameChange={(type) => this.setState({ type })}
165
+ {hasSuggestions ? (
166
+ <SuggestionsWidget
167
+ requestAiSuggestion={requestAiSuggestion}
168
+ template={template}
169
+ applySuggestions={(suggestions) =>
170
+ setContent({ ...content, ...suggestions })
171
+ }
172
+ isModification={true}
173
+ />
174
+ ) : null}
175
+ </>
176
+ }
177
+ content={content}
178
+ name={type}
179
+ onChange={handleContentChange}
180
+ onNameChange={setType}
181
+ onTemplateChange={setTemplate}
182
+ />
183
+ <div className="actions">
184
+ <HistoryBackButton
185
+ content={formatMessage({ id: "actions.cancel" })}
186
+ />
187
+ <Button
188
+ primary
189
+ disabled={isInvalid()}
190
+ onClick={handleSubmit}
191
+ content={formatMessage({ id: "actions.create" })}
157
192
  />
158
- <div className="actions">
159
- <HistoryBackButton
160
- content={formatMessage({ id: "actions.cancel" })}
161
- />
162
- <Button
163
- primary
164
- disabled={this.isInvalid()}
165
- onClick={this.handleSubmit}
166
- content={formatMessage({ id: "actions.create" })}
167
- />
168
- </div>
169
- </Form>
170
- </Container>
171
- );
172
- }
173
- }
193
+ </div>
194
+ </Form>
195
+ </Container>
196
+ );
197
+ };
198
+
199
+ ConceptForm.propTypes = {
200
+ action: PropTypes.object,
201
+ conceptAction: PropTypes.func,
202
+ conceptActionLoading: PropTypes.string,
203
+ intl: PropTypes.object,
204
+ };
174
205
 
175
206
  const mapStateToProps = ({ conceptActionLoading, conceptsActions }) => ({
176
207
  action: _.prop(actionKey)(conceptsActions),