@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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/bg",
3
- "version": "6.1.4",
3
+ "version": "6.2.0",
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.1.4",
37
+ "@truedat/test": "6.2.0",
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.1.4",
90
- "@truedat/df": "6.1.4",
91
- "@truedat/lm": "6.1.4",
89
+ "@truedat/core": "6.2.0",
90
+ "@truedat/df": "6.2.0",
91
+ "@truedat/lm": "6.2.0",
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": "c0ef00d553186b7601e136f3e38fa0d0d7287fd9"
114
+ "gitHead": "5c10ee429997ab081477edf0679547eb4bc11f9b"
115
115
  }
@@ -5,13 +5,11 @@ import { connect } from "react-redux";
5
5
  import { List, Header, Progress, Segment, Icon } from "semantic-ui-react";
6
6
  import { FormattedMessage } from "react-intl";
7
7
  import { isRequired } from "@truedat/df/utils";
8
+ import { useLocales } from "@truedat/core/hooks";
9
+ import { splitTranslatableFields } from "@truedat/core/services/i18nContent";
8
10
 
9
- export const ConceptCompleteness = ({ concept }) => {
10
- const completeness = _.get("completeness")(concept);
11
- const content = _.getOr({}, "content")(concept);
12
- const status = _.get("status")(concept);
13
-
14
- const missingRequiredFields = _.flow(
11
+ const missingRequiredFields = (concept, content) => {
12
+ return _.flow(
15
13
  _.path("template.content"),
16
14
  _.flatMap(({ fields }) =>
17
15
  _.filter((field) => {
@@ -25,15 +23,25 @@ export const ConceptCompleteness = ({ concept }) => {
25
23
  })(fields)
26
24
  )
27
25
  )(concept);
26
+ };
28
27
 
28
+ export const Completeness = ({
29
+ status,
30
+ completeness,
31
+ langLocalName,
32
+ concept,
33
+ content,
34
+ }) => {
29
35
  return (
30
- <Segment className="completeness">
36
+ <>
31
37
  <Header as="h3" dividing>
32
38
  <FormattedMessage
33
39
  id="concepts.props.completeness"
34
40
  defaultMessage="Completeness"
35
- />
41
+ />{" "}
42
+ {langLocalName || null}
36
43
  </Header>
44
+ <>{}</>
37
45
  <Progress
38
46
  error={completeness < 50}
39
47
  warning={completeness >= 50 && completeness < 85}
@@ -41,7 +49,7 @@ export const ConceptCompleteness = ({ concept }) => {
41
49
  percent={completeness}
42
50
  progress
43
51
  />
44
- {_.negate(_.isEmpty)(missingRequiredFields) &&
52
+ {_.negate(_.isEmpty)(missingRequiredFields(concept, content)) &&
45
53
  !_.includes(status)(["published", "versioned"]) && (
46
54
  <>
47
55
  <Header as="h4" dividing>
@@ -62,10 +70,80 @@ export const ConceptCompleteness = ({ concept }) => {
62
70
  />
63
71
  ) : null}
64
72
  </List.Item>
65
- ))(missingRequiredFields)}
73
+ ))(missingRequiredFields(concept, content))}
66
74
  </List>
67
75
  </>
68
76
  )}
77
+ </>
78
+ );
79
+ };
80
+
81
+ Completeness.propTypes = {
82
+ concept: PropTypes.object,
83
+ status: PropTypes.string,
84
+ completeness: PropTypes.number,
85
+ langLocalName: PropTypes.string,
86
+ content: PropTypes.object,
87
+ };
88
+
89
+ export const ConceptCompleteness = ({ concept }) => {
90
+ const { locales } = useLocales(false);
91
+
92
+ const completeness = _.get("completeness")(concept);
93
+ const content = _.getOr({}, "content")(concept);
94
+ const status = _.get("status")(concept);
95
+
96
+ const i18nContent = concept.i18n_content || {};
97
+
98
+ const { noTranslatable: noTranslatableFields } = splitTranslatableFields(
99
+ concept.template
100
+ );
101
+
102
+ const noTranslatableContent = _.pick(_.keys(noTranslatableFields))(
103
+ concept.content
104
+ );
105
+ const i18nConcept = _.flow(
106
+ _.filter(({ is_enabled, is_required }) => is_enabled && is_required),
107
+ _.map(({ id, lang, is_default, is_required, local_name }) => {
108
+ const contentLang = _.get(lang)(i18nContent);
109
+
110
+ const langContent = is_default
111
+ ? content
112
+ : { ...noTranslatableContent, ...contentLang?.content };
113
+ const langName = is_default ? concept.name : contentLang?.name || "";
114
+ const langCompleteness = is_default
115
+ ? completeness
116
+ : contentLang?.completeness;
117
+
118
+ return {
119
+ id,
120
+ lang,
121
+ isDefault: is_default,
122
+ isRequired: is_required,
123
+ name: langName,
124
+ langLocalName: local_name,
125
+ completeness: langCompleteness,
126
+ content: langContent,
127
+ };
128
+ }),
129
+ _.orderBy(["isDefault", "isRequired", "lang"], ["desc", "desc", "asc"]),
130
+ _.keyBy("lang")
131
+ )(locales);
132
+
133
+ return (
134
+ <Segment className="completeness">
135
+ {_.map(({ completeness, lang, langLocalName, content, isRequired }) => {
136
+ return isRequired == true ? (
137
+ <Completeness
138
+ key={lang}
139
+ status={status}
140
+ completeness={completeness}
141
+ langLocalName={langLocalName}
142
+ concept={concept}
143
+ content={content}
144
+ />
145
+ ) : null;
146
+ })(i18nConcept)}
69
147
  </Segment>
70
148
  );
71
149
  };
@@ -0,0 +1,116 @@
1
+ import _ from "lodash/fp";
2
+ import React, { useState, useEffect } from "react";
3
+ import PropTypes from "prop-types";
4
+ import { connect } from "react-redux";
5
+ import { useLocales } from "@truedat/core/hooks";
6
+ import {
7
+ splitTranslatableFields,
8
+ formatLocales,
9
+ } from "@truedat/core/services/i18nContent";
10
+ import { conceptAction } from "../routines";
11
+ import ConceptForms from "./ConceptForms";
12
+
13
+ const actionKey = "create";
14
+
15
+ const ConceptCreate = ({ action, conceptActionLoading, conceptAction }) => {
16
+ const { locales, loading: localesLoading } = useLocales(false);
17
+
18
+ const [i18nConcept, setI18nConcept] = useState({});
19
+ const [domainId, setDomainId] = useState();
20
+ const [template, setTemplate] = useState();
21
+ const [translatableKeys, setTranslatableKeys] = useState([]);
22
+ const [noTranslatableKeys, setNoTranslatableKeys] = useState([]);
23
+
24
+ useEffect(() => {
25
+ const templateFields = splitTranslatableFields(template);
26
+
27
+ setTranslatableKeys(_.keys(templateFields.translatable));
28
+ setNoTranslatableKeys(_.keys(templateFields.noTranslatable));
29
+
30
+ const updateI18nConcept = _.flow(
31
+ _.map((langContent) => ({
32
+ ...langContent,
33
+ content: langContent.is_default
34
+ ? {
35
+ ...templateFields.translatable,
36
+ ...templateFields.noTranslatable,
37
+ }
38
+ : { ...templateFields.translatable },
39
+ })),
40
+ _.keyBy("lang")
41
+ )(i18nConcept);
42
+
43
+ setI18nConcept(updateI18nConcept);
44
+ }, [template]);
45
+
46
+ if (_.isEmpty(i18nConcept)) {
47
+ const i18nConceptInit = _.flow(
48
+ _.filter(({ is_enabled }) => is_enabled),
49
+ _.map(({ id, lang, is_default, is_required }) => {
50
+ return { id, lang, is_default, is_required, name: "", content: {} };
51
+ }),
52
+ _.keyBy("lang")
53
+ )(locales);
54
+
55
+ setI18nConcept(i18nConceptInit);
56
+ }
57
+
58
+ const langs = formatLocales(locales);
59
+
60
+ const handleSubmit = (e) => {
61
+ e.preventDefault();
62
+ const defaultContent = _.flow(_.find("is_default"))(i18nConcept);
63
+
64
+ const i18nSendConcept = _.flow(
65
+ _.omit(defaultContent.lang),
66
+ _.mapValues((langConcept) =>
67
+ _.flow(_.pick(["name", "content"]), ({ name, content }) => {
68
+ return { name, content: _.pick(translatableKeys)(content) };
69
+ })(langConcept)
70
+ )
71
+ )(i18nConcept);
72
+
73
+ const business_concept_version = {
74
+ name: defaultContent.name,
75
+ domain_id: domainId,
76
+ type: template.name,
77
+ content: defaultContent.content,
78
+ i18n_content: i18nSendConcept,
79
+ };
80
+
81
+ conceptAction({
82
+ action: actionKey,
83
+ ...action,
84
+ business_concept_version,
85
+ });
86
+ };
87
+ return (
88
+ <ConceptForms
89
+ actionKey={actionKey}
90
+ domainId={domainId}
91
+ template={template}
92
+ i18nConcept={i18nConcept}
93
+ loading={localesLoading || conceptActionLoading}
94
+ langs={langs}
95
+ noTranslatableFields={noTranslatableKeys}
96
+ translatableFields={translatableKeys}
97
+ setI18nConcept={setI18nConcept}
98
+ setDomainId={setDomainId}
99
+ setTemplate={setTemplate}
100
+ onSubmit={handleSubmit}
101
+ />
102
+ );
103
+ };
104
+
105
+ ConceptCreate.propTypes = {
106
+ action: PropTypes.object,
107
+ conceptAction: PropTypes.func,
108
+ conceptActionLoading: PropTypes.string,
109
+ };
110
+
111
+ const mapStateToProps = ({ conceptActionLoading, conceptsActions }) => ({
112
+ action: _.prop(actionKey)(conceptsActions),
113
+ conceptActionLoading,
114
+ });
115
+
116
+ export default connect(mapStateToProps, { conceptAction })(ConceptCreate);
@@ -1,24 +1,34 @@
1
+ import _ from "lodash/fp";
1
2
  import React from "react";
2
3
  import PropTypes from "prop-types";
3
4
  import { Breadcrumb } from "semantic-ui-react";
4
5
  import { useActiveRoute } from "@truedat/core/hooks";
5
6
  import { connect } from "react-redux";
6
7
  import { Link, useHistory } from "react-router-dom";
7
- import { FormattedMessage } from "react-intl";
8
+ import { FormattedMessage, useIntl } from "react-intl";
8
9
  import {
9
10
  CONCEPTS_BULK_UPDATE,
10
11
  CONCEPTS,
11
12
  CONCEPTS_PENDING,
12
- linkTo
13
+ linkTo,
13
14
  } from "@truedat/core/routes";
14
15
 
15
16
  export const ConceptCrumbs = ({
16
- name,
17
+ name: defaultName,
17
18
  status,
18
19
  conceptAction,
19
20
  business_concept_id,
20
- id
21
+ id,
22
+ i18n_content,
21
23
  }) => {
24
+ const { locale } = useIntl();
25
+
26
+ const i18n_lang_content = _.get(locale)(i18n_content);
27
+
28
+ const name = _.isUndefined(i18n_lang_content)
29
+ ? defaultName
30
+ : i18n_lang_content?.name;
31
+
22
32
  const bulkUpdateActive = useActiveRoute(CONCEPTS_BULK_UPDATE);
23
33
  const history = useHistory();
24
34
  const fromStatus = () =>
@@ -65,7 +75,7 @@ ConceptCrumbs.propTypes = {
65
75
  name: PropTypes.string,
66
76
  conceptAction: PropTypes.string,
67
77
  id: PropTypes.number,
68
- status: PropTypes.string
78
+ status: PropTypes.string,
69
79
  };
70
80
 
71
81
  const mapStateToProps = ({ concept }) => ({ ...concept });
@@ -1,19 +1,90 @@
1
- import React from "react";
1
+ import _ from "lodash/fp";
2
+ import React, { useEffect, useState } from "react";
2
3
  import PropTypes from "prop-types";
3
4
  import { connect } from "react-redux";
4
- import { Segment } from "semantic-ui-react";
5
+ import { Button, ButtonGroup, Segment } from "semantic-ui-react";
6
+ import { useIntl } from "react-intl";
7
+ import { useLocales } from "@truedat/core/hooks";
8
+ import { splitTranslatableFields } from "@truedat/core/services/i18nContent";
5
9
 
6
10
  const DynamicFormViewer = React.lazy(() =>
7
11
  import("@truedat/df/components/DynamicFormViewer")
8
12
  );
9
13
 
10
- export const ConceptDetails = ({
11
- concept: { content, description, template },
12
- }) => (
13
- <Segment attached="bottom">
14
- <DynamicFormViewer template={template} content={content} />
15
- </Segment>
16
- );
14
+ export const ConceptDetails = ({ concept }) => {
15
+ const { locale } = useIntl();
16
+ const { locales } = useLocales(false);
17
+ const [selectedLang, setSelectedLang] = useState(locale);
18
+
19
+ useEffect(() => {
20
+ setSelectedLang(locale);
21
+ }, [locale]);
22
+
23
+ const content = _.getOr({}, "content")(concept);
24
+
25
+ const { noTranslatable: noTranslatableFields } = splitTranslatableFields(
26
+ concept.template
27
+ );
28
+ const noTranslatableContent = _.pick(_.keys(noTranslatableFields))(
29
+ concept.content
30
+ );
31
+
32
+ const i18nContent = concept.i18n_content || {};
33
+
34
+ const i18nConcept = _.flow(
35
+ _.filter(({ is_enabled, is_required }) => is_enabled && is_required),
36
+ _.map(({ id, lang, is_default, is_required, local_name }) => {
37
+ const contentLang = _.get(lang)(i18nContent);
38
+ const langContent = is_default
39
+ ? content
40
+ : _.isEmpty(contentLang?.content)
41
+ ? {}
42
+ : { ...noTranslatableContent, ...contentLang?.content };
43
+ const langName = is_default ? concept.name : contentLang?.name || "";
44
+
45
+ return {
46
+ id,
47
+ lang,
48
+ isDefault: is_default,
49
+ isRequired: is_required,
50
+ name: langName,
51
+ langLocalName: local_name,
52
+ content: langContent,
53
+ };
54
+ }),
55
+ _.orderBy(["isDefault", "isRequired", "lang"], ["desc", "desc", "asc"]),
56
+ _.keyBy("lang")
57
+ )(locales);
58
+
59
+ return (
60
+ <Segment attached="bottom">
61
+ {_.size(i18nConcept > 1) ? (
62
+ <ButtonGroup floated="right">
63
+ {_.flow(
64
+ _.filter(
65
+ ({ lang, is_default }) => i18nConcept[lang]?.content || is_default
66
+ ),
67
+ _.map(({ lang, content }) => {
68
+ return !_.isEmpty(content) ? (
69
+ <Button
70
+ key={lang}
71
+ content={lang}
72
+ size="mini"
73
+ onClick={() => setSelectedLang(lang)}
74
+ active={selectedLang == lang}
75
+ />
76
+ ) : null;
77
+ })
78
+ )(i18nConcept)}
79
+ </ButtonGroup>
80
+ ) : null}
81
+ <DynamicFormViewer
82
+ template={concept.template}
83
+ content={i18nConcept[selectedLang]?.content}
84
+ />
85
+ </Segment>
86
+ );
87
+ };
17
88
 
18
89
  ConceptDetails.propTypes = {
19
90
  concept: PropTypes.object,