@truedat/bg 6.1.4 → 6.2.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.
@@ -1,161 +1,117 @@
1
1
  import _ from "lodash/fp";
2
- import React, { useState, useEffect } from "react";
2
+ import React, { useState } from "react";
3
3
  import PropTypes from "prop-types";
4
- import {
5
- Button,
6
- Container,
7
- Form,
8
- Header,
9
- Icon,
10
- Label,
11
- Segment,
12
- } from "semantic-ui-react";
13
4
  import { compose } from "redux";
14
5
  import { connect } from "react-redux";
15
- import { injectIntl, FormattedMessage } from "react-intl";
16
- import { HistoryBackButton } from "@truedat/core/components";
17
- import { apiJsonPost } from "@truedat/core/services/api";
18
- import { selectTemplate, selectDomain } from "@truedat/df/routines";
19
6
  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";
7
+ import { useLocales } from "@truedat/core/hooks";
8
+ import {
9
+ splitTranslatableFields,
10
+ formatLocales,
11
+ } from "@truedat/core/services/i18nContent";
23
12
  import { conceptAction } from "../routines";
24
13
 
25
- const DynamicForm = React.lazy(() =>
26
- import("@truedat/df/components/DynamicForm")
27
- );
28
-
29
- const TemplateLoader = React.lazy(() =>
30
- import("@truedat/df/templates/components/TemplateLoader")
31
- );
32
-
14
+ import ConceptForms from "./ConceptForms";
33
15
  const actionKey = "update";
34
16
 
35
- const isNonEmptyString = _.flow(_.trim, _.negate(_.isEmpty));
36
- const isValid = _.conforms({
37
- name: isNonEmptyString,
38
- });
39
-
40
17
  export const ConceptEdit = ({
41
18
  concept,
42
19
  loading,
43
- intl: { formatMessage },
44
20
  template,
45
21
  action,
46
22
  conceptAction,
47
23
  applyTemplate,
48
- selectDomain,
49
- selectTemplate,
50
- templateId,
51
24
  }) => {
52
25
  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
- }, []);
71
-
72
- if (_.isNil(content)) {
26
+ const { locales, loading: localesLoading } = useLocales(false);
27
+ const [i18nConcept, setI18nConcept] = useState({});
28
+
29
+ if (_.isNil(propContent)) {
73
30
  return null;
74
31
  }
75
32
 
76
- const isInvalid = () => _.negate(isValid)({ name });
33
+ if (_.isEmpty(i18nConcept)) {
34
+ const i18n_content = concept.i18n_content || {};
35
+
36
+ const { translatable: translatableFields } =
37
+ splitTranslatableFields(template);
38
+
39
+ const i18nConceptInit = _.flow(
40
+ _.filter(({ is_enabled }) => is_enabled),
41
+ _.map(({ id, lang, is_default, is_required }) => {
42
+ const contentLang = _.get(lang)(i18n_content);
43
+
44
+ const langContent = is_default
45
+ ? propContent
46
+ : { ...translatableFields, ...contentLang?.content };
47
+ const langName = is_default ? propName : contentLang?.name || "";
48
+
49
+ return {
50
+ id,
51
+ lang,
52
+ is_default,
53
+ is_required,
54
+ name: langName,
55
+ content: langContent,
56
+ };
57
+ }),
58
+ _.keyBy("lang")
59
+ )(locales);
60
+
61
+ setI18nConcept(i18nConceptInit);
62
+ }
63
+
64
+ const templateFields = splitTranslatableFields(template);
65
+
66
+ const translatableKeys = _.keys(templateFields.translatable);
67
+ const noTranslatableKeys = _.keys(templateFields.noTranslatable);
68
+ const langs = formatLocales(locales);
69
+ const defaultContent = _.flow(_.find("is_default"))(i18nConcept);
77
70
 
78
71
  const handleSubmit = (e) => {
79
72
  e.preventDefault();
80
73
 
81
- const businessConceptVersion = { name, type: concept.type };
82
- const newContent = applyTemplate(content, domain?.id);
74
+ const i18nSendConcept = _.flow(
75
+ _.omit(defaultContent.lang),
76
+ _.mapValues((langConcept) =>
77
+ _.flow(_.pick(["name", "content"]), ({ name, content }) => {
78
+ return { name, content: _.pick(translatableKeys)(content) };
79
+ })(langConcept)
80
+ )
81
+ )(i18nConcept);
82
+
83
+ const newContent = applyTemplate(defaultContent.content, domain?.id);
84
+
85
+ const business_concept_version = {
86
+ name: defaultContent.name,
87
+ domain_id: domain.id,
88
+ type: template.name,
89
+ content: newContent,
90
+ i18n_content: i18nSendConcept,
91
+ };
83
92
 
84
93
  conceptAction({
85
94
  action: actionKey,
86
95
  ...action,
87
- business_concept_version: {
88
- ...businessConceptVersion,
89
- content: newContent,
90
- },
96
+ business_concept_version,
91
97
  });
92
98
  };
93
99
 
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
100
  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)}
131
- />
132
- </Form.Field>
133
- {hasSuggestions ? (
134
- <SuggestionsWidget
135
- requestAiSuggestion={requestAiSuggestion}
136
- template={template}
137
- applySuggestions={(suggestions) =>
138
- setContent({ ...content, ...suggestions })
139
- }
140
- isModification={true}
141
- />
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>
101
+ <>
102
+ <ConceptForms
103
+ actionKey={actionKey}
104
+ domainId={domain.id}
105
+ template={template}
106
+ i18nConcept={i18nConcept}
107
+ loading={loading || localesLoading}
108
+ langs={langs}
109
+ noTranslatableFields={noTranslatableKeys}
110
+ translatableFields={translatableKeys}
111
+ setI18nConcept={setI18nConcept}
112
+ onSubmit={handleSubmit}
113
+ />
114
+ </>
159
115
  );
160
116
  };
161
117
 
@@ -169,7 +125,6 @@ ConceptEdit.propTypes = {
169
125
  selectDomain: PropTypes.func,
170
126
  selectTemplate: PropTypes.func,
171
127
  template: PropTypes.object,
172
- templateId: PropTypes.number,
173
128
  };
174
129
 
175
130
  const mapStateToProps = ({
@@ -178,29 +133,26 @@ const mapStateToProps = ({
178
133
  conceptActions,
179
134
  templateLoading,
180
135
  templates,
181
- template,
182
136
  }) => {
183
137
  const action = _.prop(actionKey)(conceptActions);
184
138
  const loading =
185
139
  templateLoading ||
186
140
  (action && action.href && conceptActionLoading === action.href);
187
141
 
188
- const templateId =
142
+ const template =
189
143
  templates && concept && concept.type
190
- ? _.flow(_.find(_.propEq("name", concept.type)), _.prop("id"))(templates)
144
+ ? _.flow(_.find(_.propEq("name", concept.type)))(templates)
191
145
  : null;
192
146
  return {
193
147
  action,
194
148
  concept,
195
149
  loading,
196
- templateId,
197
150
  template,
198
151
  loading,
199
152
  applyTemplate: applyTemplate(template),
200
153
  };
201
154
  };
202
155
 
203
- export default compose(
204
- injectIntl,
205
- connect(mapStateToProps, { conceptAction, selectTemplate, selectDomain })
206
- )(ConceptEdit);
156
+ export default compose(connect(mapStateToProps, { conceptAction }))(
157
+ ConceptEdit
158
+ );
@@ -1,57 +1,39 @@
1
1
  import _ from "lodash/fp";
2
2
  import React, { useState, useEffect } from "react";
3
3
  import PropTypes from "prop-types";
4
- import {
5
- Button,
6
- Container,
7
- Form,
8
- Header,
9
- Icon,
10
- Label,
11
- Segment,
12
- } from "semantic-ui-react";
13
- import { compose } from "redux";
14
- import { connect } from "react-redux";
15
- import { injectIntl, FormattedMessage } from "react-intl";
4
+ import { Form, Header, Label } from "semantic-ui-react";
5
+ import { useIntl, FormattedMessage } from "react-intl";
16
6
  import { apiJsonPost } from "@truedat/core/services/api";
17
- import { DomainSelector, HistoryBackButton } from "@truedat/core/components";
7
+ import { DomainSelector } from "@truedat/core/components";
18
8
  import { useAvailabilityCheck } from "@truedat/ai/hooks/useSuggestions";
19
9
  import { API_SUGGESTIONS_REQUEST } from "@truedat/ai/api";
20
10
  import SuggestionsWidget from "@truedat/ai/components/suggestions/SuggestionsWidget";
21
- import { conceptAction } from "../routines";
22
11
 
23
12
  const SelectableDynamicForm = React.lazy(() =>
24
13
  import("@truedat/df/components/SelectableDynamicForm")
25
14
  );
26
15
 
27
- const actionKey = "create";
28
-
29
- const isNonEmptyString = _.flow(_.trim, _.negate(_.isEmpty));
30
- const isValid = _.conforms({
31
- name: isNonEmptyString,
32
- domain_id: _.isFinite,
33
- type: isNonEmptyString,
34
- });
35
-
36
16
  const ConceptForm = ({
37
- action,
38
- conceptActionLoading,
39
- conceptAction,
40
- intl: { formatMessage },
17
+ actionKey,
18
+ domainId,
19
+ template,
20
+ concept,
21
+ loading,
22
+ defaultContent,
23
+ noTranslatableFields,
24
+ onChangeDomainId,
25
+ onChangeName,
26
+ onChangeTemplate,
27
+ onChangeConceptContent,
41
28
  }) => {
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();
29
+ const { formatMessage } = useIntl();
47
30
  const [domains, setDomains] = useState();
48
31
  const [domainsLoading, setDomainsLoading] = useState(true);
49
32
  const [hasSuggestions, setHasSuggestions] = useState(false);
50
33
 
51
34
  const { trigger: checkSuggestionAvailability } = useAvailabilityCheck();
52
35
 
53
- const loading = action && action.href && conceptActionLoading === action.href;
54
- const templateDisabled = !domainId || !name;
36
+ const templateDisabled = !domainId || (!concept?.name && concept?.is_default);
55
37
  const templateId = template?.id;
56
38
 
57
39
  useEffect(() => {
@@ -66,33 +48,14 @@ const ConceptForm = ({
66
48
  }
67
49
  }, [templateId, domainId]);
68
50
 
69
- const handleDomainSelected = (_e, { value }) =>
70
- setDomainId(_.toInteger(value));
71
- const handleContentChange = ({ content }) => setContent(content);
72
51
  const handleDomainsLoaded = ({ domains }) => {
73
52
  setDomains(domains);
74
53
  setDomainsLoading(false);
75
54
  };
76
55
 
77
- const isInvalid = () =>
78
- _.negate(isValid)({ name, domain_id: domainId, type });
79
-
80
- const handleSubmit = (e) => {
81
- e.preventDefault();
82
-
83
- const business_concept_version = {
84
- name,
85
- domain_id: domainId,
86
- type,
87
- content,
88
- };
89
-
90
- conceptAction({
91
- action: actionKey,
92
- ...action,
93
- business_concept_version,
94
- });
95
- };
56
+ if (domainsLoading && domainId) {
57
+ setDomainsLoading(false);
58
+ }
96
59
 
97
60
  const requestAiSuggestion = (callback) => {
98
61
  const domainName = _.flow(
@@ -102,9 +65,9 @@ const ConceptForm = ({
102
65
  const body = {
103
66
  resource_type: "business_concept",
104
67
  resource_body: {
105
- name,
68
+ name: concept.name,
106
69
  domain: domainName,
107
- type,
70
+ type: template?.name,
108
71
  },
109
72
  domain_ids: [domainId],
110
73
  template_id: templateId,
@@ -112,32 +75,29 @@ const ConceptForm = ({
112
75
  apiJsonPost(API_SUGGESTIONS_REQUEST, body).then(callback);
113
76
  };
114
77
 
78
+ const handleOnChangeConceptContent = (content) =>
79
+ onChangeConceptContent(concept.lang, content);
80
+
115
81
  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>
82
+ <>
123
83
  <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
- />
84
+ {actionKey == "create" ? (
85
+ <DomainSelector
86
+ action="createBusinessConcept"
87
+ label={formatMessage({ id: "domain.selector.label" })}
88
+ labels
89
+ required
90
+ disabled={!concept?.is_default}
91
+ onChange={onChangeDomainId}
92
+ onLoad={handleDomainsLoaded}
93
+ value={domainId}
94
+ />
95
+ ) : null}
96
+
137
97
  <Form.Field required>
138
98
  <label>
139
99
  <FormattedMessage id="concepts.props.name" />
140
- {_.isEmpty(name) ? (
100
+ {_.isEmpty(concept?.name) ? (
141
101
  <Label pointing="left">
142
102
  <FormattedMessage id="template.form.validation.empty_required" />
143
103
  </Label>
@@ -145,11 +105,13 @@ const ConceptForm = ({
145
105
  </label>
146
106
  <Form.Input
147
107
  name="name"
148
- value={name}
149
- onChange={(_e, { value }) => setName(value)}
108
+ value={concept?.name}
109
+ onChange={(_e, { value }) => onChangeName(concept?.lang, value)}
150
110
  />
151
111
  </Form.Field>
152
112
  <SelectableDynamicForm
113
+ key={concept?.lang}
114
+ disableSelector={!concept?.is_default}
153
115
  disabled={templateDisabled}
154
116
  scope="bg"
155
117
  domainIds={_.isNil(domainId) ? null : [domainId]}
@@ -162,53 +124,47 @@ const ConceptForm = ({
162
124
  id: "concepts.form.aditional_fields",
163
125
  })}
164
126
  />
165
- {hasSuggestions ? (
127
+ {hasSuggestions && concept?.is_default ? (
166
128
  <SuggestionsWidget
167
129
  requestAiSuggestion={requestAiSuggestion}
168
130
  template={template}
169
131
  applySuggestions={(suggestions) =>
170
- setContent({ ...content, ...suggestions })
132
+ handleOnChangeConceptContent({
133
+ ...concept?.content,
134
+ ...suggestions,
135
+ })
171
136
  }
172
137
  isModification={true}
173
138
  />
174
139
  ) : null}
175
140
  </>
176
141
  }
177
- content={content}
178
- name={type}
179
- onChange={handleContentChange}
180
- onNameChange={setType}
181
- onTemplateChange={setTemplate}
142
+ content={{ ...defaultContent, ...concept?.content }}
143
+ name={template?.name}
144
+ noTranslatableFields={noTranslatableFields}
145
+ actionKey={actionKey}
146
+ onChange={handleOnChangeConceptContent}
147
+ onNameChange={onChangeName}
148
+ onTemplateChange={onChangeTemplate}
149
+ selectedTemplate={template}
182
150
  />
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" })}
192
- />
193
- </div>
194
151
  </Form>
195
- </Container>
152
+ </>
196
153
  );
197
154
  };
198
155
 
199
156
  ConceptForm.propTypes = {
200
- action: PropTypes.object,
201
- conceptAction: PropTypes.func,
157
+ domainId: PropTypes.number,
158
+ template: PropTypes.object,
159
+ concept: PropTypes.object,
160
+ defaultContent: PropTypes.object,
161
+ noTranslatableFields: PropTypes.array,
162
+ onChangeDomainId: PropTypes.func,
163
+ onChangeName: PropTypes.func,
164
+ onChangeTemplate: PropTypes.func,
165
+ onChangeConceptContent: PropTypes.func,
166
+ actionKey: PropTypes.string,
202
167
  conceptActionLoading: PropTypes.string,
203
- intl: PropTypes.object,
204
168
  };
205
169
 
206
- const mapStateToProps = ({ conceptActionLoading, conceptsActions }) => ({
207
- action: _.prop(actionKey)(conceptsActions),
208
- conceptActionLoading,
209
- });
210
-
211
- export default compose(
212
- injectIntl,
213
- connect(mapStateToProps, { conceptAction })
214
- )(ConceptForm);
170
+ export default ConceptForm;
@@ -0,0 +1,75 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { Grid, Tab } from "semantic-ui-react";
5
+ import { useIntl } from "react-intl";
6
+ import ConceptForm from "./ConceptForm";
7
+
8
+ const ConceptFormTabs = ({
9
+ domainId,
10
+ template,
11
+ langs,
12
+ conceptActionLoading,
13
+ i18nConcept,
14
+ noTranslatableFields = [],
15
+ actionKey,
16
+ onChangeDomainId,
17
+ onChangeName,
18
+ onChangeTemplate,
19
+ onChangeConceptContent,
20
+ }) => {
21
+ const { formatMessage } = useIntl();
22
+
23
+ const defaultContent = _.flow(
24
+ _.find("is_default"),
25
+ _.get("content")
26
+ )(i18nConcept);
27
+
28
+ const panes = _.map((locale) => {
29
+ const { lang, name, local_name, is_required, is_default } = locale;
30
+ return {
31
+ menuItem: `${formatMessage({
32
+ id: `i18n.messages.locale.${lang}`,
33
+ defaultMessage: `${name} ( ${local_name} )`,
34
+ })} ${is_required && !is_default ? "*" : ""}`,
35
+ render: () => (
36
+ <ConceptForm
37
+ domainId={domainId}
38
+ template={template}
39
+ concept={i18nConcept[lang]}
40
+ defaultContent={defaultContent}
41
+ noTranslatableFields={noTranslatableFields}
42
+ onChangeDomainId={onChangeDomainId}
43
+ onChangeName={onChangeName}
44
+ onChangeTemplate={onChangeTemplate}
45
+ onChangeConceptContent={onChangeConceptContent}
46
+ actionKey={actionKey}
47
+ conceptActionLoading={conceptActionLoading}
48
+ />
49
+ ),
50
+ };
51
+ })(langs);
52
+
53
+ return (
54
+ <Grid.Column>
55
+ <Tab className="concept-forms-tab" panes={panes} />
56
+ </Grid.Column>
57
+ );
58
+ };
59
+
60
+ ConceptFormTabs.propTypes = {
61
+ domainId: PropTypes.number,
62
+ template: PropTypes.object,
63
+ langs: PropTypes.object,
64
+ action: PropTypes.object,
65
+ actionKey: PropTypes.string,
66
+ conceptActionLoading: PropTypes.string,
67
+ i18nConcept: PropTypes.object,
68
+ noTranslatableFields: PropTypes.array,
69
+ onChangeDomainId: PropTypes.func,
70
+ onChangeName: PropTypes.func,
71
+ onChangeTemplate: PropTypes.func,
72
+ onChangeConceptContent: PropTypes.func,
73
+ };
74
+
75
+ export default ConceptFormTabs;