@truedat/dd 6.0.1 → 6.0.3
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 +6 -6
- package/src/api.js +2 -0
- package/src/components/BucketView.js +4 -3
- package/src/components/CatalogCustomViewCards.js +4 -1
- package/src/components/FilteredNav.js +92 -37
- package/src/components/StructureCrumbs.js +10 -8
- package/src/components/StructureItem.js +43 -6
- package/src/components/StructureItemRoot.js +6 -6
- package/src/components/StructureItems.js +6 -2
- package/src/components/StructureNav.js +10 -1
- package/src/components/StructureNavClassified.js +8 -1
- package/src/components/StructureNotesEdit.js +13 -5
- package/src/components/StructureSearch.js +16 -7
- package/src/components/StructureView.js +1 -1
- package/src/components/StructuresView.js +5 -5
- package/src/components/SystemStructures.js +7 -2
- package/src/components/__tests__/BucketView.spec.js +2 -1
- package/src/components/__tests__/FilteredNav.spec.js +76 -49
- package/src/components/__tests__/StructureItems.spec.js +2 -2
- package/src/components/__tests__/StructureNav.spec.js +1 -1
- package/src/components/__tests__/StructureNavClassified.spec.js +5 -1
- package/src/components/__tests__/StructureStructureLinks.spec.js +8 -7
- package/src/components/__tests__/StructureView.spec.js +1 -0
- package/src/components/__tests__/SystemFilteredNav.spec.js +4 -1
- package/src/hooks/useBucketPaths.js +11 -0
- package/src/hooks/useBucketStructures.js +17 -5
- package/src/reducers/navFilter.js +4 -1
- package/src/reducers/structure.js +1 -0
- package/src/reducers/structureFilters.js +1 -0
- package/src/selectors/getStructureParent.js +1 -2
- package/src/components/StructureNoteSuggestions.js +0 -179
- package/src/components/__tests__/StructureNoteSuggestions.spec.js +0 -151
- package/src/components/__tests__/StructureSearch.spec.js +0 -34
- package/src/components/__tests__/__snapshots__/StructureNoteSuggestions.spec.js.snap +0 -316
- package/src/components/__tests__/__snapshots__/StructureSearch.spec.js.snap +0 -18
- package/src/utils/bucketNav.js +0 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/dd",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.3",
|
|
4
4
|
"description": "Truedat Web Data Dictionary",
|
|
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.
|
|
37
|
+
"@truedat/test": "6.0.3",
|
|
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",
|
|
@@ -88,9 +88,9 @@
|
|
|
88
88
|
},
|
|
89
89
|
"dependencies": {
|
|
90
90
|
"@apollo/client": "^3.7.1",
|
|
91
|
-
"@truedat/auth": "6.0.
|
|
92
|
-
"@truedat/core": "6.0.
|
|
93
|
-
"@truedat/df": "6.0.
|
|
91
|
+
"@truedat/auth": "6.0.3",
|
|
92
|
+
"@truedat/core": "6.0.3",
|
|
93
|
+
"@truedat/df": "6.0.3",
|
|
94
94
|
"lodash": "^4.17.21",
|
|
95
95
|
"moment": "^2.29.4",
|
|
96
96
|
"path-to-regexp": "^1.7.0",
|
|
@@ -115,5 +115,5 @@
|
|
|
115
115
|
"react-dom": ">= 16.8.6 < 17",
|
|
116
116
|
"semantic-ui-react": ">= 2.0.3 < 2.2"
|
|
117
117
|
},
|
|
118
|
-
"gitHead": "
|
|
118
|
+
"gitHead": "c88ddc501214b8c4e2732654ca0052ebbcc802c9"
|
|
119
119
|
}
|
package/src/api.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const API_BUCKET_STRUCTURES = "/api/buckets/structures";
|
|
2
|
+
const API_BUCKET_PATHS = "/api/buckets/paths";
|
|
2
3
|
const API_BUCKET_STRUCTURE = "/api/buckets/structures/:id";
|
|
3
4
|
const API_DATA_STRUCTURE = "/api/data_structures/:id";
|
|
4
5
|
const API_DATA_STRUCTURES_BULK_UPDATE = "/api/data_structures/bulk_update";
|
|
@@ -54,6 +55,7 @@ const API_STRUCTURE_NOTES = "/api/data_structures/:data_structure_id/notes";
|
|
|
54
55
|
|
|
55
56
|
export {
|
|
56
57
|
API_BUCKET_STRUCTURES,
|
|
58
|
+
API_BUCKET_PATHS,
|
|
57
59
|
API_BUCKET_STRUCTURE,
|
|
58
60
|
API_DATA_STRUCTURE,
|
|
59
61
|
API_DATA_STRUCTURES_BULK_UPDATE,
|
|
@@ -7,7 +7,6 @@ import { useParams, useLocation } from "react-router-dom";
|
|
|
7
7
|
import searchImage from "assets/searching.png";
|
|
8
8
|
import { FormattedMessage, useIntl } from "react-intl";
|
|
9
9
|
import { saveNavFilter as saveNavFilterRoutine } from "../routines";
|
|
10
|
-
import { isBucketFilter } from "../utils/bucketNav";
|
|
11
10
|
import FilteredNav from "./FilteredNav";
|
|
12
11
|
import StructureCrumbs from "./StructureCrumbs";
|
|
13
12
|
import StructureGrantCart from "./StructureGrantCart";
|
|
@@ -53,6 +52,7 @@ export const BucketView = ({
|
|
|
53
52
|
getNavFilterURLParams,
|
|
54
53
|
_.values,
|
|
55
54
|
(keyValue) => Object.fromEntries(new Map([keyValue]).entries()),
|
|
55
|
+
(filter) => ({ view: "BUCKET_VIEW", filter }),
|
|
56
56
|
saveNavFilter
|
|
57
57
|
)(location);
|
|
58
58
|
}, [location, saveNavFilter]);
|
|
@@ -68,8 +68,8 @@ export const BucketView = ({
|
|
|
68
68
|
</Grid.Row>
|
|
69
69
|
<Grid.Column width={4}>
|
|
70
70
|
<Segment>
|
|
71
|
-
{
|
|
72
|
-
<FilteredNav
|
|
71
|
+
{navFilter?.view === "BUCKET_VIEW" ? (
|
|
72
|
+
<FilteredNav navFilter={navFilter} />
|
|
73
73
|
) : null}
|
|
74
74
|
</Segment>
|
|
75
75
|
</Grid.Column>
|
|
@@ -90,6 +90,7 @@ export const BucketView = ({
|
|
|
90
90
|
{navFilterOneProp?.propertyPath
|
|
91
91
|
? `${formatMessage({
|
|
92
92
|
id: navFilterOneProp.propertyPath,
|
|
93
|
+
defaultMessage: navFilterOneProp.propertyPath,
|
|
93
94
|
})} > ${navFilterOneProp?.propertyValue}`
|
|
94
95
|
: null}
|
|
95
96
|
</Header>
|
|
@@ -9,6 +9,7 @@ import { FormattedNumber } from "react-intl";
|
|
|
9
9
|
import { CardGroupsAccordion } from "@truedat/core/components";
|
|
10
10
|
import { linkTo } from "@truedat/core/routes";
|
|
11
11
|
import { fetchStructureFilters as fetchStructureFiltersRoutine } from "@truedat/dd/routines";
|
|
12
|
+
import { Loading } from "@truedat/core/components";
|
|
12
13
|
|
|
13
14
|
const moveMissingBucketToTheEnd = (buckets) =>
|
|
14
15
|
_.sortBy(({ key }) => (key === "_missing" || key === "" ? 1 : 0))(buckets);
|
|
@@ -29,7 +30,9 @@ export const CatalogCustomViewCards = ({
|
|
|
29
30
|
const { propertyPath } = useParams();
|
|
30
31
|
const groups = [];
|
|
31
32
|
|
|
32
|
-
return
|
|
33
|
+
return _.isEmpty(structureFilters) ? (
|
|
34
|
+
<Loading />
|
|
35
|
+
) : Object.keys(groups).length > 1 ? (
|
|
33
36
|
<CardGroupsAccordion
|
|
34
37
|
groups={groups}
|
|
35
38
|
cardComponent={CatalogCustomViewCard}
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import _ from "lodash/fp";
|
|
2
|
-
import React, { useState,
|
|
2
|
+
import React, { useState, useRef } from "react";
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
4
|
import { connect } from "react-redux";
|
|
5
|
-
import {
|
|
6
|
-
import { Button, Icon, Loader } from "semantic-ui-react";
|
|
5
|
+
import { Button, Icon } from "semantic-ui-react";
|
|
7
6
|
import { useIntl } from "react-intl";
|
|
8
|
-
|
|
9
|
-
import { toggleStructuresAliasNameMode } from "../routines";
|
|
7
|
+
|
|
10
8
|
import { getStructureParent } from "../selectors";
|
|
9
|
+
import { toggleStructuresAliasNameMode } from "../routines";
|
|
10
|
+
import { useBucketStructures } from "../hooks/useBucketStructures";
|
|
11
|
+
import { useBucketPaths } from "../hooks/useBucketPaths";
|
|
11
12
|
import StructureNav from "./StructureNav";
|
|
12
13
|
import StructureSearch from "./StructureSearch";
|
|
13
14
|
|
|
14
15
|
export const FilteredNav = ({
|
|
15
16
|
childStructures,
|
|
17
|
+
fnHasCatalogViewProp,
|
|
18
|
+
isLoading,
|
|
16
19
|
parentStructure,
|
|
20
|
+
setSearchValue,
|
|
17
21
|
structuresAliasNameMode,
|
|
18
22
|
toggleStructuresAliasNameMode,
|
|
19
23
|
totalStructures,
|
|
@@ -22,15 +26,6 @@ export const FilteredNav = ({
|
|
|
22
26
|
_.has("original_name")(structure)
|
|
23
27
|
)(childStructures);
|
|
24
28
|
const { formatMessage } = useIntl();
|
|
25
|
-
const [searchValue, setSearchValue] = useState("");
|
|
26
|
-
const onChange = (searchValue) => setSearchValue(searchValue);
|
|
27
|
-
|
|
28
|
-
const filteredStructures = (collection) => {
|
|
29
|
-
const normalizedSearchValue = lowerDeburrTrim(searchValue);
|
|
30
|
-
return _.filter((structure) =>
|
|
31
|
-
_.contains(normalizedSearchValue)(lowerDeburrTrim(structure.name))
|
|
32
|
-
)(collection);
|
|
33
|
-
};
|
|
34
29
|
|
|
35
30
|
return (
|
|
36
31
|
<>
|
|
@@ -46,6 +41,7 @@ export const FilteredNav = ({
|
|
|
46
41
|
icon
|
|
47
42
|
data-tooltip={formatMessage({
|
|
48
43
|
id: "structures.props.functional_name",
|
|
44
|
+
defaultMessage: "structures.props.functional_name",
|
|
49
45
|
})}
|
|
50
46
|
onClick={() => toggleStructuresAliasNameMode(true)}
|
|
51
47
|
>
|
|
@@ -64,12 +60,14 @@ export const FilteredNav = ({
|
|
|
64
60
|
</Button>
|
|
65
61
|
</Button.Group>
|
|
66
62
|
</div>
|
|
67
|
-
<StructureSearch onChange={
|
|
63
|
+
<StructureSearch onChange={setSearchValue} isLoading={isLoading} />
|
|
68
64
|
<StructureNav
|
|
69
|
-
childStructures={
|
|
65
|
+
childStructures={childStructures}
|
|
70
66
|
parentStructure={parentStructure}
|
|
71
67
|
totalChildStructures={totalStructures}
|
|
72
68
|
structuresAliasNameMode={structuresAliasNameMode}
|
|
69
|
+
fnHasCatalogViewProp={fnHasCatalogViewProp}
|
|
70
|
+
isLoading={isLoading}
|
|
73
71
|
/>
|
|
74
72
|
</>
|
|
75
73
|
);
|
|
@@ -77,48 +75,105 @@ export const FilteredNav = ({
|
|
|
77
75
|
|
|
78
76
|
FilteredNav.propTypes = {
|
|
79
77
|
childStructures: PropTypes.array,
|
|
78
|
+
isLoading: PropTypes.bool,
|
|
80
79
|
parentStructure: PropTypes.object,
|
|
80
|
+
setSearchValue: PropTypes.func,
|
|
81
81
|
structuresAliasNameMode: PropTypes.bool,
|
|
82
82
|
toggleStructuresAliasNameMode: PropTypes.func,
|
|
83
83
|
totalStructures: PropTypes.number,
|
|
84
|
+
fnHasCatalogViewProp: PropTypes.func,
|
|
84
85
|
};
|
|
85
86
|
|
|
87
|
+
// ElasticSearch identifier null_value
|
|
88
|
+
// It is also the root ID.
|
|
89
|
+
const ES_ID_NULL_VALUE = 0;
|
|
90
|
+
|
|
86
91
|
export const FilteredNavLoader = ({
|
|
87
|
-
|
|
92
|
+
navFilter,
|
|
88
93
|
parentStructure,
|
|
89
94
|
structure,
|
|
90
95
|
structuresAliasNameMode,
|
|
91
96
|
toggleStructuresAliasNameMode,
|
|
92
97
|
}) => {
|
|
93
|
-
const {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
98
|
+
const { data: dataBucketPaths, isLoading: isLoadingBucketPaths } =
|
|
99
|
+
useBucketPaths(navFilter);
|
|
100
|
+
|
|
101
|
+
const bucketPaths = dataBucketPaths?.data;
|
|
102
|
+
|
|
103
|
+
const initialTotalRef = useRef();
|
|
104
|
+
|
|
105
|
+
const subTreeChildren = (ancestry, dsid) =>
|
|
106
|
+
ancestry
|
|
107
|
+
? _.flow(
|
|
108
|
+
_.map(({ data_structure_id }) => data_structure_id),
|
|
109
|
+
(ancestryDsIds) => [...ancestryDsIds, dsid],
|
|
110
|
+
(idPath) => _.get(idPath, bucketPaths?.forest),
|
|
111
|
+
_.keys,
|
|
112
|
+
(keys) => keys || []
|
|
113
|
+
)(ancestry)
|
|
114
|
+
: _.keys(bucketPaths?.forest);
|
|
115
|
+
|
|
116
|
+
const children =
|
|
117
|
+
navFilter.view === "SYSTEM_STRUCTURES"
|
|
118
|
+
? structure?.children
|
|
119
|
+
: [
|
|
120
|
+
...subTreeChildren(structure?.ancestry, structure?.id),
|
|
121
|
+
...(bucketPaths?.filtered_children[structure?.id || 0] || []),
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
const parentId =
|
|
125
|
+
structure?.parents?.[0]?.data_structure_id || ES_ID_NULL_VALUE;
|
|
126
|
+
|
|
127
|
+
const childrenOrSiblings = !_.isEmpty(children)
|
|
128
|
+
? children
|
|
129
|
+
: [
|
|
130
|
+
...subTreeChildren(structure?.ancestry.slice(0, -1), structure?.id),
|
|
131
|
+
...(bucketPaths?.filtered_children[parentId] || []),
|
|
132
|
+
];
|
|
133
|
+
|
|
134
|
+
// If there are children, show children (next parent_id is structure id)
|
|
135
|
+
// If there are no children, show siblings (next parent_id is structure parent id)
|
|
136
|
+
const parentOrCurrentId = _.isEmpty(children)
|
|
137
|
+
? parentId
|
|
138
|
+
: structure?.id || ES_ID_NULL_VALUE;
|
|
139
|
+
|
|
140
|
+
const initialSearchValue = "";
|
|
141
|
+
const [searchValue, setSearchValue] = useState(initialSearchValue);
|
|
142
|
+
|
|
143
|
+
const { data: dataBucketStructures, isLoading } = useBucketStructures({
|
|
144
|
+
navFilter,
|
|
145
|
+
parentFilter: { parent_id: parentOrCurrentId },
|
|
146
|
+
ids: childrenOrSiblings,
|
|
147
|
+
query: searchValue,
|
|
148
|
+
});
|
|
149
|
+
if (searchValue === initialSearchValue) {
|
|
150
|
+
initialTotalRef.current = dataBucketStructures?.headers["x-total-count"];
|
|
151
|
+
}
|
|
152
|
+
const bucketStructures = dataBucketStructures?.data?.data;
|
|
153
|
+
|
|
154
|
+
const fnHasCatalogViewProp = (structure) =>
|
|
155
|
+
navFilter?.view === "BUCKET_VIEW"
|
|
156
|
+
? bucketPaths?.filtered_children[parentOrCurrentId]?.includes(
|
|
157
|
+
structure?.id
|
|
158
|
+
) || false
|
|
159
|
+
: undefined;
|
|
160
|
+
|
|
161
|
+
return (
|
|
110
162
|
<FilteredNav
|
|
111
|
-
childStructures={
|
|
163
|
+
childStructures={bucketStructures || []}
|
|
112
164
|
parentStructure={parentStructure}
|
|
113
|
-
totalStructures={
|
|
165
|
+
totalStructures={initialTotalRef.current}
|
|
166
|
+
fnHasCatalogViewProp={fnHasCatalogViewProp}
|
|
114
167
|
structuresAliasNameMode={structuresAliasNameMode}
|
|
115
168
|
toggleStructuresAliasNameMode={toggleStructuresAliasNameMode}
|
|
169
|
+
setSearchValue={setSearchValue}
|
|
170
|
+
isLoading={isLoading || isLoadingBucketPaths}
|
|
116
171
|
/>
|
|
117
172
|
);
|
|
118
173
|
};
|
|
119
174
|
|
|
120
175
|
FilteredNavLoader.propTypes = {
|
|
121
|
-
|
|
176
|
+
navFilter: PropTypes.object,
|
|
122
177
|
parentStructure: PropTypes.object,
|
|
123
178
|
structure: PropTypes.object,
|
|
124
179
|
structuresAliasNameMode: PropTypes.bool,
|
|
@@ -18,7 +18,7 @@ export const StructuresCrumb = () => (
|
|
|
18
18
|
|
|
19
19
|
const filterQueryParams = _.flow(
|
|
20
20
|
Object.entries,
|
|
21
|
-
_.first,
|
|
21
|
+
_.first, // only works for one filter
|
|
22
22
|
([propertyPath, propertyValue]) => ({
|
|
23
23
|
propertyPath,
|
|
24
24
|
propertyValue,
|
|
@@ -27,12 +27,12 @@ const filterQueryParams = _.flow(
|
|
|
27
27
|
);
|
|
28
28
|
|
|
29
29
|
export const SystemCrumb = ({ id, name, navFilter }) => {
|
|
30
|
-
const [propertyPath, propertyValue] = !_.isEmpty(navFilter)
|
|
31
|
-
? _.toPairs(navFilter)[0]
|
|
30
|
+
const [propertyPath, propertyValue] = !_.isEmpty(navFilter.filter)
|
|
31
|
+
? _.toPairs(navFilter.filter)[0]
|
|
32
32
|
: [null, null];
|
|
33
33
|
return (
|
|
34
34
|
<>
|
|
35
|
-
{
|
|
35
|
+
{navFilter.view === "SYSTEM_STRUCTURES" ? (
|
|
36
36
|
<Breadcrumb.Section
|
|
37
37
|
as={Link}
|
|
38
38
|
to={linkTo.SYSTEM_STRUCTURES({ id })}
|
|
@@ -40,25 +40,27 @@ export const SystemCrumb = ({ id, name, navFilter }) => {
|
|
|
40
40
|
>
|
|
41
41
|
{name}
|
|
42
42
|
</Breadcrumb.Section>
|
|
43
|
-
) : (
|
|
43
|
+
) : navFilter.view === "BUCKET_VIEW" ? (
|
|
44
44
|
<>
|
|
45
45
|
<Breadcrumb.Section
|
|
46
46
|
as={Link}
|
|
47
47
|
to={`${linkTo.BUCKETS_VIEW({ propertyPath })}`}
|
|
48
48
|
active={false}
|
|
49
49
|
>
|
|
50
|
-
<FormattedMessage id={propertyPath} />
|
|
50
|
+
<FormattedMessage id={propertyPath} defaultMessage={propertyPath} />
|
|
51
51
|
</Breadcrumb.Section>
|
|
52
52
|
<Breadcrumb.Divider icon="right angle" />
|
|
53
53
|
<Breadcrumb.Section
|
|
54
54
|
as={Link}
|
|
55
|
-
to={`${linkTo.BUCKET_VIEW()}?${filterQueryParams(
|
|
55
|
+
to={`${linkTo.BUCKET_VIEW()}?${filterQueryParams(
|
|
56
|
+
navFilter.filter
|
|
57
|
+
)}`}
|
|
56
58
|
active={false}
|
|
57
59
|
>
|
|
58
60
|
{propertyValue}
|
|
59
61
|
</Breadcrumb.Section>
|
|
60
62
|
</>
|
|
61
|
-
)}
|
|
63
|
+
) : undefined}
|
|
62
64
|
<Breadcrumb.Divider icon="right angle" />
|
|
63
65
|
</>
|
|
64
66
|
);
|
|
@@ -23,9 +23,12 @@ const filterQueryParams = _.flow(
|
|
|
23
23
|
(queryParams) => new URLSearchParams(queryParams)
|
|
24
24
|
);
|
|
25
25
|
|
|
26
|
-
const bucketToBreadcrumbs = ({
|
|
27
|
-
const [propertyPath, propertyValue] = _.toPairs(
|
|
28
|
-
return `${formatMessage({
|
|
26
|
+
const bucketToBreadcrumbs = ({ filter, formatMessage }) => {
|
|
27
|
+
const [propertyPath, propertyValue] = _.toPairs(filter)[0];
|
|
28
|
+
return `${formatMessage({
|
|
29
|
+
id: propertyPath,
|
|
30
|
+
defaultMessage: propertyPath,
|
|
31
|
+
})} > ${propertyValue}`;
|
|
29
32
|
};
|
|
30
33
|
|
|
31
34
|
export const StructureItem = ({
|
|
@@ -37,11 +40,44 @@ export const StructureItem = ({
|
|
|
37
40
|
navFilter,
|
|
38
41
|
original_name,
|
|
39
42
|
type,
|
|
43
|
+
/* hasCatalogViewProp can be:
|
|
44
|
+
* - true/false in bucket view (catalog view)
|
|
45
|
+
* - undefined in regular structure view
|
|
46
|
+
*/
|
|
47
|
+
hasCatalogViewProp,
|
|
40
48
|
}) => {
|
|
41
49
|
const { formatMessage } = useIntl();
|
|
42
50
|
|
|
43
51
|
const displayName = structuresAliasNameMode ? name : original_name || name;
|
|
44
52
|
|
|
53
|
+
const truthyToClassName = {
|
|
54
|
+
deleted_at: "deleted",
|
|
55
|
+
hasCatalogViewProp: "has-catalog-view-prop",
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const notUndefinedToClassName = {
|
|
59
|
+
hasCatalogViewProp: "has-no-catalog-view-prop",
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const toClassNames = _.flow(
|
|
63
|
+
Object.entries,
|
|
64
|
+
_.reduce(
|
|
65
|
+
(acc, [key, value]) =>
|
|
66
|
+
value
|
|
67
|
+
? [truthyToClassName[key], ...acc]
|
|
68
|
+
: value !== undefined
|
|
69
|
+
? [
|
|
70
|
+
...(notUndefinedToClassName[key]
|
|
71
|
+
? [notUndefinedToClassName[key]]
|
|
72
|
+
: []),
|
|
73
|
+
...acc,
|
|
74
|
+
]
|
|
75
|
+
: acc,
|
|
76
|
+
[]
|
|
77
|
+
),
|
|
78
|
+
_.join(" ")
|
|
79
|
+
);
|
|
80
|
+
|
|
45
81
|
return (
|
|
46
82
|
<List.Item
|
|
47
83
|
as={active ? "a" : Link}
|
|
@@ -52,19 +88,19 @@ export const StructureItem = ({
|
|
|
52
88
|
: type == "system" && id
|
|
53
89
|
? linkTo.SYSTEM_STRUCTURES({ id })
|
|
54
90
|
: type == "bucket"
|
|
55
|
-
? `${linkTo.BUCKET_VIEW()}?${filterQueryParams(navFilter)}`
|
|
91
|
+
? `${linkTo.BUCKET_VIEW()}?${filterQueryParams(navFilter.filter)}`
|
|
56
92
|
: id
|
|
57
93
|
? linkTo.STRUCTURE({ id })
|
|
58
94
|
: STRUCTURES
|
|
59
95
|
}
|
|
60
|
-
className={deleted_at
|
|
96
|
+
className={toClassNames({ deleted_at, hasCatalogViewProp })}
|
|
61
97
|
active={active}
|
|
62
98
|
>
|
|
63
99
|
<List.Icon name={getIcon(formatMessage, type)} verticalAlign="middle" />
|
|
64
100
|
<List.Content>
|
|
65
101
|
<List.Description>
|
|
66
102
|
{type == "bucket"
|
|
67
|
-
? bucketToBreadcrumbs({ navFilter, formatMessage })
|
|
103
|
+
? bucketToBreadcrumbs({ filter: navFilter.filter, formatMessage })
|
|
68
104
|
: displayName}
|
|
69
105
|
</List.Description>
|
|
70
106
|
</List.Content>
|
|
@@ -81,6 +117,7 @@ StructureItem.propTypes = {
|
|
|
81
117
|
original_name: PropTypes.string,
|
|
82
118
|
structuresAliasNameMode: PropTypes.bool,
|
|
83
119
|
type: PropTypes.string,
|
|
120
|
+
hasCatalogViewProp: PropTypes.bool,
|
|
84
121
|
};
|
|
85
122
|
|
|
86
123
|
export const mapStateToProps = (
|
|
@@ -6,7 +6,6 @@ import { useHistory } from "react-router-dom";
|
|
|
6
6
|
import PropTypes from "prop-types";
|
|
7
7
|
import { List } from "semantic-ui-react";
|
|
8
8
|
import { STRUCTURES, linkTo } from "@truedat/core/routes";
|
|
9
|
-
import { isBucketFilter } from "../utils/bucketNav";
|
|
10
9
|
|
|
11
10
|
const filterParams = _.flow(
|
|
12
11
|
Object.entries,
|
|
@@ -19,11 +18,12 @@ const filterParams = _.flow(
|
|
|
19
18
|
|
|
20
19
|
export const StructureItemRoot = ({ navFilter }) => {
|
|
21
20
|
const history = useHistory();
|
|
22
|
-
const url =
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
const url =
|
|
22
|
+
navFilter?.view === "BUCKET_VIEW"
|
|
23
|
+
? linkTo.BUCKETS_VIEW({
|
|
24
|
+
propertyPath: filterParams(navFilter.filter)?.propertyPath,
|
|
25
|
+
})
|
|
26
|
+
: STRUCTURES;
|
|
27
27
|
return (
|
|
28
28
|
<List.Item onClick={() => history.push(url)}>
|
|
29
29
|
<List.Icon name="angle double left" verticalAlign="middle" />
|
|
@@ -3,13 +3,16 @@ import PropTypes from "prop-types";
|
|
|
3
3
|
import StructureItem from "./StructureItem";
|
|
4
4
|
|
|
5
5
|
export const StructureItems = ({
|
|
6
|
-
bucketChildStructures,
|
|
7
6
|
structures,
|
|
8
7
|
structuresAliasNameMode,
|
|
8
|
+
fnHasCatalogViewProp,
|
|
9
9
|
}) => {
|
|
10
|
-
return
|
|
10
|
+
return structures.map((structure, index) => (
|
|
11
11
|
<StructureItem
|
|
12
12
|
key={index}
|
|
13
|
+
hasCatalogViewProp={
|
|
14
|
+
fnHasCatalogViewProp ? fnHasCatalogViewProp(structure) : undefined
|
|
15
|
+
}
|
|
13
16
|
structuresAliasNameMode={structuresAliasNameMode}
|
|
14
17
|
{...structure}
|
|
15
18
|
/>
|
|
@@ -19,6 +22,7 @@ export const StructureItems = ({
|
|
|
19
22
|
StructureItems.propTypes = {
|
|
20
23
|
structures: PropTypes.array,
|
|
21
24
|
structuresAliasNameMode: PropTypes.bool,
|
|
25
|
+
fnHasCatalogViewProp: PropTypes.func,
|
|
22
26
|
};
|
|
23
27
|
|
|
24
28
|
export default StructureItems;
|
|
@@ -2,6 +2,7 @@ import _ from "lodash/fp";
|
|
|
2
2
|
import React from "react";
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
4
|
import { List, Divider } from "semantic-ui-react";
|
|
5
|
+
import { Loading } from "@truedat/core/components";
|
|
5
6
|
import { useIntl } from "react-intl";
|
|
6
7
|
import StructureItemRoot from "./StructureItemRoot";
|
|
7
8
|
import StructureItem from "./StructureItem";
|
|
@@ -20,6 +21,8 @@ export const StructureNav = ({
|
|
|
20
21
|
parentStructure,
|
|
21
22
|
structuresAliasNameMode,
|
|
22
23
|
totalChildStructures,
|
|
24
|
+
fnHasCatalogViewProp,
|
|
25
|
+
isLoading,
|
|
23
26
|
}) => {
|
|
24
27
|
const { formatMessage } = useIntl();
|
|
25
28
|
const firstWithClasses = _.find(hasNavClass)(childStructures);
|
|
@@ -27,7 +30,9 @@ export const StructureNav = ({
|
|
|
27
30
|
? _.flow(navClasses, _.head)(firstWithClasses)
|
|
28
31
|
: undefined;
|
|
29
32
|
|
|
30
|
-
return (
|
|
33
|
+
return isLoading ? (
|
|
34
|
+
<Loading />
|
|
35
|
+
) : (
|
|
31
36
|
<>
|
|
32
37
|
<List relaxed selection className="structure-nav">
|
|
33
38
|
<StructureItemRoot />
|
|
@@ -42,11 +47,13 @@ export const StructureNav = ({
|
|
|
42
47
|
<StructureNavClassified
|
|
43
48
|
classifier={classifier}
|
|
44
49
|
structures={childStructures}
|
|
50
|
+
fnHasCatalogViewProp={fnHasCatalogViewProp}
|
|
45
51
|
/>
|
|
46
52
|
) : (
|
|
47
53
|
<StructureItems
|
|
48
54
|
structures={childStructures}
|
|
49
55
|
structuresAliasNameMode={structuresAliasNameMode}
|
|
56
|
+
fnHasCatalogViewProp={fnHasCatalogViewProp}
|
|
50
57
|
/>
|
|
51
58
|
)}
|
|
52
59
|
</List>
|
|
@@ -67,9 +74,11 @@ export const StructureNav = ({
|
|
|
67
74
|
|
|
68
75
|
StructureNav.propTypes = {
|
|
69
76
|
childStructures: PropTypes.array,
|
|
77
|
+
isLoading: PropTypes.bool,
|
|
70
78
|
parentStructure: PropTypes.object,
|
|
71
79
|
structuresAliasNameMode: PropTypes.bool,
|
|
72
80
|
totalChildStructures: PropTypes.number,
|
|
81
|
+
fnHasCatalogViewProp: PropTypes.func,
|
|
73
82
|
};
|
|
74
83
|
|
|
75
84
|
export default StructureNav;
|
|
@@ -11,6 +11,7 @@ export const StructureNavClassified = ({
|
|
|
11
11
|
classifier,
|
|
12
12
|
structures,
|
|
13
13
|
leafClasses,
|
|
14
|
+
fnHasCatalogViewProp,
|
|
14
15
|
}) => {
|
|
15
16
|
const { formatMessage } = useIntl();
|
|
16
17
|
|
|
@@ -43,7 +44,12 @@ export const StructureNavClassified = ({
|
|
|
43
44
|
className: "noselect",
|
|
44
45
|
}}
|
|
45
46
|
/>
|
|
46
|
-
{expanded[label] ?
|
|
47
|
+
{expanded[label] ? (
|
|
48
|
+
<StructureItems
|
|
49
|
+
structures={children}
|
|
50
|
+
fnHasCatalogViewProp={fnHasCatalogViewProp}
|
|
51
|
+
/>
|
|
52
|
+
) : null}
|
|
47
53
|
</React.Fragment>
|
|
48
54
|
));
|
|
49
55
|
};
|
|
@@ -52,6 +58,7 @@ StructureNavClassified.propTypes = {
|
|
|
52
58
|
classifier: PropTypes.string,
|
|
53
59
|
structures: PropTypes.array,
|
|
54
60
|
leafClasses: PropTypes.object,
|
|
61
|
+
fnHasCatalogViewProp: PropTypes.func,
|
|
55
62
|
};
|
|
56
63
|
|
|
57
64
|
export const mapStateToProps = (state) => ({
|
|
@@ -7,9 +7,10 @@ import { useIntl } from "react-intl";
|
|
|
7
7
|
import { HistoryBackButton } from "@truedat/core/components";
|
|
8
8
|
import { selectTemplate, selectDomains } from "@truedat/df/routines";
|
|
9
9
|
import { applyTemplate, validateContent } from "@truedat/df/utils";
|
|
10
|
+
import { apiJson } from "@truedat/core/services/api";
|
|
11
|
+
import SuggestionsWidget from "@truedat/ai/components/suggestions/SuggestionsWidget";
|
|
10
12
|
import { doStructureNoteAction } from "../routines";
|
|
11
13
|
import { getLatestDfContent } from "../selectors/getSortedStructureNotes";
|
|
12
|
-
import StructureNoteSuggestions from "./StructureNoteSuggestions";
|
|
13
14
|
|
|
14
15
|
const DynamicForm = React.lazy(() =>
|
|
15
16
|
import("@truedat/df/components/DynamicForm")
|
|
@@ -30,7 +31,9 @@ export const StructureNotesEdit = ({
|
|
|
30
31
|
latestDfContent,
|
|
31
32
|
}) => {
|
|
32
33
|
const [content, setContent] = useState(latestDfContent);
|
|
33
|
-
const
|
|
34
|
+
const aiSuggestionsUrl = _.prop("_actions.ai_suggestions.href")(
|
|
35
|
+
structureNotes
|
|
36
|
+
);
|
|
34
37
|
|
|
35
38
|
const isModification = !_.isEmpty(latestDfContent);
|
|
36
39
|
|
|
@@ -75,13 +78,18 @@ export const StructureNotesEdit = ({
|
|
|
75
78
|
setContent({ ...content, ...suggestions });
|
|
76
79
|
};
|
|
77
80
|
|
|
81
|
+
const requestAiSuggestion = (callback) => {
|
|
82
|
+
const url = `${aiSuggestionsUrl}?language=${locale}`;
|
|
83
|
+
apiJson(url).then(callback);
|
|
84
|
+
};
|
|
85
|
+
|
|
78
86
|
return (
|
|
79
87
|
<Segment attached="bottom">
|
|
80
88
|
<TemplateLoader />
|
|
81
89
|
|
|
82
|
-
{
|
|
83
|
-
<
|
|
84
|
-
|
|
90
|
+
{aiSuggestionsUrl ? (
|
|
91
|
+
<SuggestionsWidget
|
|
92
|
+
requestAiSuggestion={requestAiSuggestion}
|
|
85
93
|
template={template}
|
|
86
94
|
applySuggestions={applySuggestions}
|
|
87
95
|
isModification={isModification}
|
|
@@ -1,20 +1,29 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
1
2
|
import React from "react";
|
|
2
3
|
import PropTypes from "prop-types";
|
|
3
4
|
import { useIntl } from "react-intl";
|
|
4
5
|
import { Input } from "semantic-ui-react";
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
7
|
+
// https://stackoverflow.com/a/37312154
|
|
8
|
+
const debounceEventHandler = (...args) => {
|
|
9
|
+
const debounced = _.debounce(...args);
|
|
10
|
+
return (e) => {
|
|
11
|
+
e.persist();
|
|
12
|
+
return debounced(e);
|
|
10
13
|
};
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const StructureSearch = ({ onChange, isLoading = false }) => {
|
|
17
|
+
const { formatMessage } = useIntl();
|
|
18
|
+
const handleChange = (e) => onChange(e.target.value);
|
|
19
|
+
|
|
11
20
|
return (
|
|
12
21
|
<Input
|
|
13
|
-
onChange={handleChange}
|
|
22
|
+
onChange={debounceEventHandler(500, handleChange)}
|
|
14
23
|
icon={{ name: "search", link: true }}
|
|
15
24
|
iconPosition="left"
|
|
16
25
|
fluid
|
|
17
|
-
loading={
|
|
26
|
+
loading={isLoading}
|
|
18
27
|
placeholder={formatMessage({ id: "structure.search.placeholder" })}
|
|
19
28
|
/>
|
|
20
29
|
);
|
|
@@ -22,7 +31,7 @@ export const StructureSearch = ({ onChange, loading = false }) => {
|
|
|
22
31
|
|
|
23
32
|
StructureSearch.propTypes = {
|
|
24
33
|
onChange: PropTypes.func,
|
|
25
|
-
|
|
34
|
+
isLoading: PropTypes.bool,
|
|
26
35
|
};
|
|
27
36
|
|
|
28
37
|
export default StructureSearch;
|