@vegan-friendly/strapi-plugin-elasticsearch 0.0.11-alpha.6 → 0.0.11-alpha.7

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 (61) hide show
  1. package/.vscode/settings.json +3 -0
  2. package/README.md +0 -4
  3. package/admin/src/components/Initializer/index.js +26 -0
  4. package/admin/src/components/PluginIcon/index.js +12 -0
  5. package/admin/src/components/SubNavigation/index.js +48 -0
  6. package/admin/src/index.js +63 -0
  7. package/admin/src/pages/App/index.js +29 -0
  8. package/admin/src/pages/ConfigureCollection/index.js +225 -0
  9. package/admin/src/pages/ConfigureCollectionList/index.js +266 -0
  10. package/admin/src/pages/Homepage/index.js +168 -0
  11. package/admin/src/pages/ViewIndexingRunLog/index.js +124 -0
  12. package/admin/src/pluginId.js +5 -0
  13. package/admin/src/translations/en.json +1 -0
  14. package/admin/src/translations/fr.json +1 -0
  15. package/admin/src/utils/apiUrls.js +14 -0
  16. package/admin/src/utils/axiosInstance.js +40 -0
  17. package/admin/src/utils/getTrad.js +5 -0
  18. package/package.json +40 -80
  19. package/server/bootstrap.js +142 -0
  20. package/server/config/index.js +6 -0
  21. package/server/content-types/index.js +9 -0
  22. package/server/content-types/indexing-logs.js +35 -0
  23. package/server/content-types/name-prefix.js +0 -0
  24. package/server/content-types/tasks.js +52 -0
  25. package/server/controllers/configure-indexing.js +66 -0
  26. package/server/controllers/index.js +15 -0
  27. package/server/controllers/log-indexing.js +11 -0
  28. package/server/controllers/perform-indexing.js +28 -0
  29. package/server/controllers/perform-search.js +31 -0
  30. package/server/controllers/setup-info.js +14 -0
  31. package/server/destroy.js +5 -0
  32. package/server/index.js +25 -0
  33. package/server/middlewares/index.js +3 -0
  34. package/server/policies/index.js +3 -0
  35. package/server/register.js +5 -0
  36. package/server/routes/configure-indexing.js +42 -0
  37. package/server/routes/index.js +13 -0
  38. package/server/routes/perform-indexing.js +24 -0
  39. package/server/routes/perform-search.js +14 -0
  40. package/server/routes/run-log.js +12 -0
  41. package/server/routes/setup-info.js +12 -0
  42. package/server/services/configure-indexing.js +184 -0
  43. package/server/services/es-interface.js +187 -0
  44. package/server/services/helper.js +305 -0
  45. package/server/services/index.js +19 -0
  46. package/server/services/log-indexing.js +26 -0
  47. package/server/services/perform-indexing.js +173 -0
  48. package/server/services/schedule-indexing.js +65 -0
  49. package/server/services/transform-content.js +22 -0
  50. package/strapi-admin.js +3 -0
  51. package/strapi-server.js +3 -0
  52. package/dist/_chunks/App-Br53NnT1.mjs +0 -17315
  53. package/dist/_chunks/App-C3jMSu4l.js +0 -17314
  54. package/dist/_chunks/en-B4KWt_jN.js +0 -4
  55. package/dist/_chunks/en-Byx4XI2L.mjs +0 -4
  56. package/dist/_chunks/index-BlmgBQo2.js +0 -71
  57. package/dist/_chunks/index-CRnRLV0T.mjs +0 -72
  58. package/dist/admin/index.js +0 -3
  59. package/dist/admin/index.mjs +0 -4
  60. package/dist/server/index.js +0 -7330
  61. package/dist/server/index.mjs +0 -7307
@@ -0,0 +1,3 @@
1
+ {
2
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
3
+ }
package/README.md CHANGED
@@ -16,10 +16,6 @@ via yarn:
16
16
  yarn add @geeky-biz/strapi-plugin-elasticsearch
17
17
  ```
18
18
 
19
- ## Supported Strapi version
20
-
21
- The latest version of this plugin works for Strapi v5. For Strapi v4, please install the version 0.0.8 of this plugin.
22
-
23
19
  ## Plugin Configuration
24
20
 
25
21
  Within your Strapi project's `config/plugin.js`, enable the plugin and provide the configuration details:
@@ -0,0 +1,26 @@
1
+ /**
2
+ *
3
+ * Initializer
4
+ *
5
+ */
6
+
7
+ import { useEffect, useRef } from 'react';
8
+ import PropTypes from 'prop-types';
9
+ import pluginId from '../../pluginId';
10
+
11
+ const Initializer = ({ setPlugin }) => {
12
+ const ref = useRef();
13
+ ref.current = setPlugin;
14
+
15
+ useEffect(() => {
16
+ ref.current(pluginId);
17
+ }, []);
18
+
19
+ return null;
20
+ };
21
+
22
+ Initializer.propTypes = {
23
+ setPlugin: PropTypes.func.isRequired,
24
+ };
25
+
26
+ export default Initializer;
@@ -0,0 +1,12 @@
1
+ /**
2
+ *
3
+ * PluginIcon
4
+ *
5
+ */
6
+
7
+ import React from 'react';
8
+ import { Search } from '@strapi/icons';
9
+
10
+ const PluginIcon = () => <Search />;
11
+
12
+ export default PluginIcon;
@@ -0,0 +1,48 @@
1
+ import React from 'react';
2
+ import { Connector } from '@strapi/icons';
3
+ import { Box } from '@strapi/design-system';
4
+ import {
5
+ SubNav,
6
+ SubNavHeader,
7
+ SubNavSection,
8
+ SubNavSections,
9
+ SubNavLink,
10
+ } from '@strapi/design-system/v2';
11
+ import { NavLink } from 'react-router-dom';
12
+ import pluginId from "../../pluginId";
13
+
14
+ export const SubNavigation = ({activeUrl}) => {
15
+ const links = [ {
16
+ id: 1,
17
+ label : 'Setup Information',
18
+ icon : Connector,
19
+ to : `/plugins/${pluginId}/home`,
20
+ },
21
+ {
22
+ id: 2,
23
+ label : 'Configure Collections',
24
+ icon : Connector,
25
+ to : `/plugins/${pluginId}/configure-collections`,
26
+ },
27
+ {
28
+ id: 3,
29
+ label : 'Indexing Run Logs',
30
+ icon : Connector,
31
+ to : `/plugins/${pluginId}/view-indexing-logs`,
32
+ }];
33
+ return (<Box style={{
34
+ height: '100vh'
35
+ }} background="neutral200">
36
+ <SubNav ariaLabel="Settings sub nav">
37
+ <SubNavHeader label="Strapi Elasticsearch" />
38
+ <SubNavSections>
39
+ <SubNavSection>
40
+ {links.map(link => link.icon && <SubNavLink
41
+ as={NavLink} to={link.to} icon={link.icon} key={link.id} >
42
+ {link.label}
43
+ </SubNavLink>)}
44
+ </SubNavSection>
45
+ </SubNavSections>
46
+ </SubNav>
47
+ </Box>);
48
+ }
@@ -0,0 +1,63 @@
1
+ import { prefixPluginTranslations } from '@strapi/helper-plugin';
2
+ import pluginPkg from '../../package.json';
3
+ import pluginId from './pluginId';
4
+ import Initializer from './components/Initializer';
5
+ import PluginIcon from './components/PluginIcon';
6
+
7
+ const name = pluginPkg.strapi.name;
8
+
9
+ export default {
10
+ register(app) {
11
+ app.addMenuLink({
12
+ to: `/plugins/${pluginId}`,
13
+ icon: PluginIcon,
14
+ intlLabel: {
15
+ id: `${pluginId}.plugin.name`,
16
+ defaultMessage: 'Elasticsearch',
17
+ },
18
+ Component: async () => {
19
+ const component = await import(/* webpackChunkName: "[request]" */ './pages/App');
20
+
21
+ return component;
22
+ },
23
+ permissions: [
24
+ // Uncomment to set the permissions of the plugin here
25
+ // {
26
+ // action: '', // the action name should be plugin::plugin-name.actionType
27
+ // subject: null,
28
+ // },
29
+ ],
30
+ });
31
+ app.registerPlugin({
32
+ id: pluginId,
33
+ initializer: Initializer,
34
+ isReady: false,
35
+ name,
36
+ });
37
+ },
38
+
39
+ bootstrap(app) {},
40
+ async registerTrads({ locales }) {
41
+ const importedTrads = await Promise.all(
42
+ locales.map((locale) => {
43
+ return import(
44
+ /* webpackChunkName: "translation-[request]" */ `./translations/${locale}.json`
45
+ )
46
+ .then(({ default: data }) => {
47
+ return {
48
+ data: prefixPluginTranslations(data, pluginId),
49
+ locale,
50
+ };
51
+ })
52
+ .catch(() => {
53
+ return {
54
+ data: {},
55
+ locale,
56
+ };
57
+ });
58
+ })
59
+ );
60
+
61
+ return Promise.resolve(importedTrads);
62
+ },
63
+ };
@@ -0,0 +1,29 @@
1
+ /**
2
+ *
3
+ * This component is the skeleton around the actual pages, and should only
4
+ * contain code that should be seen on all pages. (e.g. navigation bar)
5
+ *
6
+ */
7
+
8
+ import React from 'react';
9
+ import { Switch, Route, Redirect } from 'react-router-dom';
10
+ import { AnErrorOccurred } from '@strapi/helper-plugin';
11
+ import pluginId from '../../pluginId';
12
+ import ConfigureCollectionList from '../ConfigureCollectionList';
13
+ import ConfigureCollection from '../ConfigureCollection';
14
+ import ViewIndexingRunLog from '../ViewIndexingRunLog';
15
+ import Homepage from '../Homepage';
16
+ const App = () => {
17
+ return (
18
+ <Switch>
19
+ <Route path={`/plugins/${pluginId}`} render={() => (<Redirect to={`/plugins/${pluginId}/home`} />)} exact />
20
+ <Route path={`/plugins/${pluginId}/home`} component={Homepage} exact />
21
+ <Route path={`/plugins/${pluginId}/configure-collections`} component={ConfigureCollectionList} exact />
22
+ <Route path={`/plugins/${pluginId}/configure-collections/:collectionName`} component={ConfigureCollection} exact />
23
+ <Route path={`/plugins/${pluginId}/view-indexing-logs`} component={ViewIndexingRunLog} />
24
+ <Route component={AnErrorOccurred} />
25
+ </Switch>
26
+ );
27
+ };
28
+
29
+ export default App;
@@ -0,0 +1,225 @@
1
+ /*
2
+ *
3
+ * HomePage
4
+ *
5
+ */
6
+
7
+ import React, { useState, useEffect } from 'react';
8
+ import { useParams, useHistory } from 'react-router-dom';
9
+ import axiosInstance from '../../utils/axiosInstance';
10
+ import { SubNavigation } from '../../components/SubNavigation';;
11
+ import { Box, Flex } from '@strapi/design-system';
12
+ import { ToggleInput } from '@strapi/design-system';
13
+ import { Link, useTracking } from '@strapi/helper-plugin';
14
+ import pluginId from '../../pluginId';
15
+ import { apiGetCollectionConfig, apiSaveCollectionConfig } from "../../utils/apiUrls";
16
+ import { Alert } from '@strapi/design-system';
17
+ import { Button } from '@strapi/design-system';
18
+ import { ArrowLeft } from '@strapi/icons';
19
+ import { Typography } from '@strapi/design-system';
20
+ import { Loader, Textarea, TextInput } from '@strapi/design-system';
21
+ import {LoadingIndicatorPage, useNotification} from '@strapi/helper-plugin';
22
+
23
+ const loadConfigForCollection = (collectionName) => {
24
+ return axiosInstance.get(apiGetCollectionConfig(collectionName))
25
+ .then((resp) => resp.data);
26
+ }
27
+
28
+ const saveConfigForCollection = (collectionName, data) => {
29
+ return axiosInstance.post(apiSaveCollectionConfig(collectionName), {
30
+ data
31
+ })
32
+ }
33
+
34
+ const ConfigureField = ({config, index, setFieldConfig}) => {
35
+
36
+ const validateSubfieldsConfig = (conf) => {
37
+ if (conf && conf.length > 0)
38
+ {
39
+ try {
40
+ JSON.parse(conf);
41
+ return true;
42
+ } catch (e) {
43
+ return false;
44
+ }
45
+ }
46
+ else
47
+ return true;
48
+ }
49
+
50
+ const updateIndex = (checked) => {
51
+ setFieldConfig({index, config: {...config, index: checked}})
52
+ }
53
+
54
+ const updateSubfieldConfig = (subfields) => {
55
+ const subfieldsConfigValid = validateSubfieldsConfig(subfields);
56
+ setFieldConfig({index, config: {...config, subfields, subfieldsConfigValid}})
57
+ }
58
+
59
+ const updateMappedFieldName = (mappedName) => {
60
+ setFieldConfig({index, config: {...config, searchFieldName: mappedName}})
61
+ }
62
+
63
+ return (
64
+ <Box background="neutral100" borderColor="neutral200" hasRadius index={index}
65
+ padding={4}>
66
+ <Box paddingTop={2} paddingBottom={2}>
67
+ <Typography fontWeight="bold" textColor="neutral600">{config.name}</Typography>
68
+ </Box>
69
+ <Box paddingTop={2} paddingBottom={2}>
70
+ <ToggleInput label="Index" onLabel="Yes" offLabel="No"
71
+ checked={config.index} onChange={(e) => updateIndex(e.target.checked)} />
72
+ </Box>
73
+ <Box width="50%" paddingTop={2} paddingBottom={2}>
74
+ <TextInput label="Maps to search field" placeholder="Enter field name" name="Search field" onChange={e => updateMappedFieldName(e.target.value)} value={config.searchFieldName || ""} />
75
+ </Box>
76
+ {
77
+ config.index && config.type && config.type === "dynamiczone" ? (
78
+ <Box paddingTop={2} paddingBottom={2}>
79
+ <Textarea
80
+ label="Dynamic zone fields to index"
81
+ error={config.subfieldsConfigValid === false ? 'Invalid indexing configuration' : undefined}
82
+ onChange={e => updateSubfieldConfig(e.target.value)}
83
+ >{config.subfields || ""}</Textarea>
84
+ </Box>
85
+ ) : null
86
+ }
87
+ {
88
+ config.index && config.type && config.type === "component" ? (
89
+ <Box paddingTop={2} paddingBottom={2}>
90
+ <Textarea
91
+ label="Component fields to index"
92
+ error={config.subfieldsConfigValid === false ? 'Invalid indexing configuration' : undefined}
93
+ onChange={e => updateSubfieldConfig(e.target.value)}
94
+ >{config.subfields || ""}</Textarea>
95
+ </Box>
96
+ ) : null
97
+ }
98
+ </Box>
99
+ )
100
+ }
101
+
102
+ const ConfigureCollection = () => {
103
+ const [isInProgress, setIsInProgress] = useState(false);
104
+ const [selectedCollection, setSelectedCollection] = useState(null);
105
+ const [collectionConfig, setCollectionConfig] = useState(null);
106
+
107
+ const params = useParams();
108
+ const toggleNotification = useNotification();
109
+ const updateCollectionsConfig = ({index, config}) => {
110
+ setCollectionConfig({
111
+ collectionName: collectionConfig.collectionName,
112
+ attributes: collectionConfig.attributes.map((e, idx) => index === idx ? config : e)
113
+ });
114
+ }
115
+
116
+ const saveCollectionConfig = () => {
117
+ if (collectionConfig && collectionConfig.collectionName)
118
+ {
119
+ const data = {}
120
+ data[collectionConfig.collectionName] = {}
121
+ for (let k=0; k<collectionConfig.attributes.length; k++)
122
+ {
123
+ const {name, ...attribs} = collectionConfig.attributes[k]
124
+ data[collectionConfig.collectionName][name] = attribs
125
+ }
126
+ setIsInProgress(true);
127
+ saveConfigForCollection(collectionConfig.collectionName, data)
128
+ .then((resp) => {
129
+ toggleNotification({
130
+ type: "success", message: "The collection configuration is saved.", timeout: 5000
131
+ });
132
+ })
133
+ .catch((err) => {
134
+ toggleNotification({
135
+ type: "warning", message: err.message || "An error was encountered.", timeout: 5000
136
+ });
137
+ console.log(err);
138
+ })
139
+ .finally(() => setIsInProgress(false));
140
+ }
141
+ }
142
+
143
+ useEffect(() => {
144
+ if (params && params.collectionName)
145
+ setSelectedCollection(params.collectionName)
146
+ }, [params]);
147
+
148
+ useEffect(() => {
149
+ if (selectedCollection)
150
+ {
151
+ loadConfigForCollection(selectedCollection)
152
+ .then((resp) => {
153
+ if (Object.keys(resp).length === 0)
154
+ {
155
+ toggleNotification({
156
+ type: "warning", message: 'No collection with the selected name exists.', timeout: 5000
157
+ });
158
+ }
159
+ else
160
+ {
161
+ const collectionName = Object.keys(resp)[0];
162
+ const attributeNames = Object.keys(resp[collectionName]);
163
+ const attributes = [];
164
+ for (let s = 0; s<attributeNames.length; s++)
165
+ attributes.push({name: attributeNames[s], ...resp[collectionName][attributeNames[s]]})
166
+ const item = {collectionName, attributes};
167
+ setCollectionConfig(item);
168
+ }
169
+ })
170
+ .catch((err) => {
171
+ toggleNotification({
172
+ type: "warning", message: err.message || "An error was encountered.", timeout: 5000
173
+ });
174
+ console.log(err);
175
+ });
176
+ }
177
+ }, [selectedCollection]);
178
+
179
+ if (collectionConfig === null)
180
+ return <LoadingIndicatorPage />;
181
+ else
182
+ return (
183
+ <Flex alignItems="stretch" gap={4}>
184
+ <SubNavigation />
185
+ <Box padding={8} background="neutral100" width="100%">
186
+ <Box paddingBottom={4}>
187
+ <Link startIcon={<ArrowLeft />} to={`/plugins/${pluginId}/configure-collections/`}>
188
+ Back
189
+ </Link>
190
+ </Box>
191
+ {
192
+ selectedCollection && (
193
+ <Box paddingBottom={4}>
194
+ <Typography variant="alpha">{selectedCollection}</Typography>
195
+ </Box>
196
+ )
197
+ }
198
+ {
199
+ collectionConfig && (
200
+ <>
201
+ <Flex alignItems="stretch" gap={4} width="100%">
202
+ <Box padding={8} background="neutral0" width="100%">
203
+ <Box paddingBottom={2}>
204
+ <Typography variant="beta">Attributes</Typography>
205
+ {
206
+ collectionConfig.attributes.map((a, idx) => {
207
+ return <Box paddingTop={4} paddingBottom={4}><ConfigureField index={idx} config={a}
208
+ setFieldConfig={updateCollectionsConfig} /></Box>
209
+ })
210
+ }
211
+ </Box>
212
+ </Box>
213
+ </Flex>
214
+ <Box paddingTop={4}>
215
+ <Button loading={isInProgress} variant="default" onClick={saveCollectionConfig} >Save Configuration Changes</Button>
216
+ </Box>
217
+ </>
218
+ )
219
+ }
220
+ </Box>
221
+ </Flex>
222
+ );
223
+ };
224
+
225
+ export default ConfigureCollection;
@@ -0,0 +1,266 @@
1
+ /*
2
+ *
3
+ * HomePage
4
+ *
5
+ */
6
+
7
+ import React, { useState } from 'react';
8
+ // import PropTypes from 'prop-types';
9
+ import pluginId from '../../pluginId';
10
+ import { SubNavigation } from '../../components/SubNavigation';;
11
+ import { Box, Flex } from '@strapi/design-system';
12
+ import { useEffect } from 'react';
13
+ import { Loader } from '@strapi/design-system';
14
+ import { apiGetContentConfig, apiRequestCollectionIndexing,
15
+ apiImportContentConfig, apiExportContentConfig } from "../../utils/apiUrls";
16
+ import axiosInstance from '../../utils/axiosInstance';
17
+ import { Alert } from '@strapi/design-system';
18
+ import { Table, Thead, Tbody, Tr, Td, Th } from '@strapi/design-system';
19
+ import { Typography } from '@strapi/design-system';
20
+ import { Pencil, Server } from '@strapi/icons';
21
+ import { IconButton } from '@strapi/design-system';
22
+ import { useHistory } from "react-router-dom";
23
+ import { Button } from '@strapi/design-system';
24
+ import { ModalLayout, ModalBody, ModalHeader, ModalFooter } from '@strapi/design-system';
25
+ import { Textarea, TwoColsLayout, Divider } from '@strapi/design-system';
26
+ import {LoadingIndicatorPage, useNotification} from '@strapi/helper-plugin';
27
+
28
+ const exportContentConfig = () => {
29
+ return axiosInstance.get(apiGetContentConfig,
30
+ {
31
+ responseType: 'blob'
32
+ })
33
+ .then((response) => {
34
+ const href = URL.createObjectURL(response.data);
35
+ const link = document.createElement('a');
36
+ link.href = href;
37
+ link.setAttribute('download', 'strapi-plugin-elasticsearch-contentconfig.json');
38
+ document.body.appendChild(link);
39
+ link.click();
40
+ document.body.removeChild(link);
41
+ URL.revokeObjectURL(href);
42
+ });
43
+ }
44
+
45
+ const importContentConfig = (conf) => {
46
+ return axiosInstance.post(apiImportContentConfig, {
47
+ data : conf
48
+ });
49
+ }
50
+
51
+ const loadContentConfig = () => {
52
+ return axiosInstance.get(apiGetContentConfig)
53
+ .then((resp) => resp.data);
54
+ }
55
+
56
+ const scheduleCollectionIndexing = (collectionName) => {
57
+ return axiosInstance.get(apiRequestCollectionIndexing(collectionName))
58
+ }
59
+
60
+ const Configure = () => {
61
+ const [isInProgress, setIsInProgress] = useState(false);
62
+ const [displayImportModal, setDisplayImportModal] = useState(false);
63
+ const [isEnteredJsonValid, setIsEnteredJsonValid] = useState(true);
64
+ const [importJson, setImportJson] = useState(null);
65
+
66
+ const [config, setConfig] = useState(null);
67
+ const history = useHistory();
68
+ const toggleNotification = useNotification();
69
+
70
+ const performImport = () => {
71
+ const conf = importJson
72
+ console.log(conf && conf.length > 0)
73
+ if (conf && conf.length > 0) {
74
+ setIsInProgress(true);
75
+ importContentConfig(conf)
76
+ .then(() => {
77
+ toggleNotification({
78
+ type: "success", message: "Collections configuration imported. Please refresh this view.", timeout: 5000
79
+ });
80
+ })
81
+ .catch((err) => {
82
+ toggleNotification({
83
+ type: "warning", message: "Importing collections configuration failed. An error was encountered.", timeout: 5000
84
+ });
85
+ console.log(err);
86
+ })
87
+ .finally(() => setIsInProgress(false));
88
+ }
89
+ }
90
+ const performExport = () => {
91
+ setIsInProgress(true);
92
+ exportContentConfig()
93
+ .then(() => {
94
+ toggleNotification({
95
+ type: "success", message: "Collections configuration exported.", timeout: 5000
96
+ });
97
+ })
98
+ .catch((err) => {
99
+ toggleNotification({
100
+ type: "warning", message: "Exporting collections configuration failed. An error was encountered.", timeout: 5000
101
+ });
102
+ console.log(err);
103
+ })
104
+ .finally(() => setIsInProgress(false));
105
+ }
106
+
107
+ useEffect(() => {
108
+ if (importJson && importJson.length > 0)
109
+ {
110
+ try {
111
+ JSON.parse(importJson);
112
+ setIsEnteredJsonValid(true);
113
+ } catch (e) {
114
+ setIsEnteredJsonValid(false);
115
+ }
116
+ }
117
+ }, [importJson]);
118
+
119
+ useEffect(() => {
120
+ setIsInProgress(true);
121
+ loadContentConfig()
122
+ .then((resp) => {
123
+ const displayConfig = [];
124
+ for (let r=0; r < Object.keys(resp).length; r++)
125
+ {
126
+ const item = {collectionName: Object.keys(resp)[r], indexed: [], notIndexed: []};
127
+ const collectionName = item.collectionName;
128
+ for (let k=0; k < Object.keys(resp[collectionName]).length; k++)
129
+ {
130
+ const attribs = resp[collectionName];
131
+ for (let s=0; s < Object.keys(attribs).length; s++)
132
+ {
133
+ const attrName = Object.keys(attribs)[s]
134
+ const attr = attribs[attrName]
135
+ if (attr.index === false && !item.notIndexed.includes(attrName))
136
+ item.notIndexed.push(attrName)
137
+ else if (attr.index && !item.indexed.includes(attrName))
138
+ item.indexed.push(attrName)
139
+ }
140
+ }
141
+ displayConfig.push(item);
142
+ }
143
+ setConfig(displayConfig);
144
+ })
145
+ .catch((err) => {
146
+ toggleNotification({
147
+ type: "warning", message: "An error was encountered while fetching the configuration.", timeout: 5000
148
+ });
149
+ console.log(err);
150
+ })
151
+ .finally (() => {
152
+ setIsInProgress(false);
153
+ })
154
+ }, []);
155
+ if (config === null)
156
+ return <LoadingIndicatorPage />;
157
+ else
158
+ {
159
+ return (
160
+ <>
161
+ <Flex alignItems="stretch" gap={4}>
162
+ <SubNavigation activeUrl={'/configure-collections/'}/>
163
+ <Box padding={8} background="neutral100">
164
+ <Box paddingBottom={4}>
165
+ <Typography variant="alpha">Configure Collections</Typography>
166
+ </Box>
167
+ {
168
+ config && (
169
+ <TwoColsLayout startCol={
170
+ <Table colCount={4} rowCount={config.length}>
171
+ <Thead>
172
+ <Tr>
173
+ <Th style={{width: "250px"}}>
174
+ <Typography variant="sigma">Collection</Typography>
175
+ </Th>
176
+ <Th style={{width: "250px"}}>
177
+ <Typography variant="sigma">Index</Typography>
178
+ </Th>
179
+ <Th style={{width: "250px"}}>
180
+ <Typography variant="sigma">Do not Index</Typography>
181
+ </Th>
182
+ <Th>
183
+ <Typography variant="sigma">Actions</Typography>
184
+ </Th>
185
+ </Tr>
186
+ </Thead>
187
+ <Tbody>
188
+ {
189
+ config.map((collection, idx) => {
190
+ return (
191
+ <Tr key={idx}>
192
+ <Td>
193
+ <Typography textColor="neutral600">{collection.collectionName}
194
+ </Typography>
195
+ </Td>
196
+ <Td>
197
+ {collection.indexed.map((i) =>
198
+ <Box paddingBottom={2}>
199
+ <Typography textColor="neutral600">{i}
200
+ </Typography></Box>
201
+ )}
202
+ </Td>
203
+ <Td>
204
+ {collection.notIndexed.map((i) =>
205
+ <Box paddingBottom={2}>
206
+ <Typography textColor="neutral600">{i}
207
+ </Typography></Box>
208
+ )}
209
+ </Td>
210
+ <Td>
211
+ <IconButton onClick={() => history.push(`/plugins/${pluginId}/configure-collections/${collection.collectionName}`)} label="Edit collection configuration" noBorder icon={<Pencil />} />
212
+ <IconButton onClick={() => scheduleCollectionIndexing(collection.collectionName)} label="Schedule indexing for all items in this collection" noBorder icon={<Server />} />
213
+ </Td>
214
+ </Tr>
215
+ );
216
+ })
217
+ }
218
+ </Tbody>
219
+ </Table>}
220
+ endCol={<>
221
+ <Box paddingLeft={2} paddingRight={2} paddingTop={4} paddingBottom={4} >
222
+ <Box paddingTop={4} paddingBottom={4}>
223
+ <Typography variant="pi" fontWeight="bold" textColor="neutral600" >CONFIG ACTIONS</Typography>
224
+ </Box>
225
+ <Divider />
226
+ <Box paddingTop={4} paddingBottom={4}>
227
+ <Box paddingTop={2} paddingBottom={2}>
228
+ <Button loading={isInProgress} fullWidth variant="secondary" onClick={performExport}>Export</Button>
229
+ </Box>
230
+ <Box paddingTop={2} paddingBottom={2}>
231
+ <Button loading={isInProgress} fullWidth variant="secondary" onClick={() => setDisplayImportModal(true)}>Import</Button>
232
+ </Box>
233
+ </Box>
234
+ </Box>
235
+ </>
236
+ } />
237
+ )
238
+ }
239
+ {displayImportModal &&
240
+ <ModalLayout onClose={() => setDisplayImportModal(false)} labelledBy="title">
241
+ <ModalHeader>
242
+ <Typography fontWeight="bold" textColor="neutral800" as="h2" id="title">
243
+ Import Search Configuration
244
+ </Typography>
245
+ </ModalHeader>
246
+ <ModalBody>
247
+ <Textarea
248
+ label="Configuration Json"
249
+ error={!isEnteredJsonValid ? 'Invalid Json' : undefined}
250
+ onChange={e => setImportJson(e.target.value)}
251
+ >{importJson}</Textarea>
252
+ </ModalBody>
253
+ <ModalFooter startActions={<Button onClick={() => setDisplayImportModal(false)} variant="tertiary">
254
+ Cancel
255
+ </Button>} endActions={<>
256
+ <Button loading={isInProgress} onClick={performImport} disabled={!isEnteredJsonValid && !importJson.length>0}>Import</Button>
257
+ </>} />
258
+ </ModalLayout>}
259
+ </Box>
260
+ </Flex>
261
+ </>
262
+ );
263
+ }
264
+ };
265
+
266
+ export default Configure;