strapi-plugin-navigation 2.0.0-rc.1 → 2.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.
Files changed (50) hide show
  1. package/README.md +55 -8
  2. package/__mocks__/pages.settings.json +25 -0
  3. package/__mocks__/strapi.js +207 -0
  4. package/admin/src/components/ConfirmationDialog/index.js +56 -0
  5. package/admin/src/components/Item/ItemCardBadge/index.js +2 -1
  6. package/admin/src/components/Item/ItemCardHeader/index.js +8 -13
  7. package/admin/src/components/Item/index.js +10 -13
  8. package/admin/src/components/RestartAlert/index.js +8 -0
  9. package/admin/src/components/Search/index.js +21 -23
  10. package/admin/src/hooks/useAllContentTypes.js +13 -0
  11. package/admin/src/hooks/useNavigationConfig.js +58 -0
  12. package/admin/src/index.js +24 -1
  13. package/admin/src/pages/SettingsPage/index.js +311 -0
  14. package/admin/src/pages/View/components/NavigationHeader/index.js +39 -23
  15. package/admin/src/pages/View/components/NavigationItemForm/index.js +49 -9
  16. package/admin/src/pages/View/components/NavigationItemPopup/NavigationItemPopupFooter.js +3 -6
  17. package/admin/src/pages/View/components/NavigationItemPopup/NavigationItemPopupHeader.js +3 -7
  18. package/admin/src/pages/View/components/NavigationItemPopup/index.js +3 -5
  19. package/admin/src/pages/View/index.js +29 -20
  20. package/admin/src/pages/View/utils/parsers.js +7 -3
  21. package/admin/src/translations/en.json +52 -10
  22. package/admin/src/translations/fr.json +4 -4
  23. package/admin/src/utils/api.js +51 -0
  24. package/admin/src/utils/index.js +20 -0
  25. package/package.json +7 -6
  26. package/server/bootstrap.js +30 -1
  27. package/server/content-types/navigation/schema.json +45 -0
  28. package/server/content-types/navigation-item/schema.json +1 -1
  29. package/server/controllers/navigation.js +30 -5
  30. package/server/graphql/index.js +3 -4
  31. package/server/graphql/queries/render-navigation.js +4 -3
  32. package/server/graphql/types/content-types-name-fields.js +4 -2
  33. package/server/graphql/types/navigation-related.js +2 -2
  34. package/server/routes/admin.js +24 -1
  35. package/server/services/__tests__/functions.test.js +48 -0
  36. package/server/services/__tests__/navigation.test.js +84 -77
  37. package/server/services/navigation.js +58 -18
  38. package/server/services/utils/functions.js +45 -12
  39. package/strapi-server.js +0 -2
  40. package/yarn-error.log +5263 -0
  41. package/.circleci/config.yml +0 -48
  42. package/.eslintrc +0 -35
  43. package/.github/pull_request_template.md +0 -13
  44. package/.github/stale.yml +0 -15
  45. package/.nvmrc +0 -1
  46. package/codecov.yml +0 -3
  47. package/public/assets/logo.png +0 -0
  48. package/public/assets/preview.png +0 -0
  49. package/server/content-types/navigation/schema.js +0 -45
  50. package/server/register.js +0 -5
@@ -3,17 +3,13 @@
3
3
  import React from 'react';
4
4
  import { ButtonText } from '@strapi/design-system/Text';
5
5
  import { ModalHeader } from '@strapi/design-system/ModalLayout';
6
- import { useIntl } from 'react-intl';
7
- import { getTrad } from '../../../../translations';
6
+ import { getMessage } from '../../../../utils';
8
7
 
9
- export const NavigationItemPopupHeader = () => {
10
- const { formatMessage } = useIntl();
8
+ export const NavigationItemPopupHeader = ({isNewItem}) => {
11
9
  return (
12
10
  <ModalHeader>
13
11
  <ButtonText textColor="neutral800" as="h2" id="asset-dialog-title">
14
- {formatMessage(
15
- getTrad('popup.item.header'),
16
- )}
12
+ {getMessage(`popup.item.header.${isNewItem ? 'new' : 'edit'}`)}
17
13
  </ButtonText>
18
14
  </ModalHeader>
19
15
  );
@@ -5,7 +5,6 @@
5
5
  */
6
6
 
7
7
  import React from 'react';
8
- import { useIntl } from 'react-intl';
9
8
  import PropTypes from 'prop-types';
10
9
  import { find } from 'lodash';
11
10
 
@@ -15,8 +14,8 @@ import { ModalLayout } from '@strapi/design-system/ModalLayout';
15
14
  import NavigationItemForm from '../NavigationItemForm';
16
15
  import { extractRelatedItemLabel, isRelationCorrect, isRelationPublished } from '../../utils/parsers';
17
16
  import { navigationItemType } from '../../utils/enums';
18
- import { getTrad } from '../../../../translations';
19
17
  import { NavigationItemPopupHeader } from './NavigationItemPopupHeader';
18
+ import { getMessage } from '../../../../utils';
20
19
 
21
20
  const NavigationItemPopUp = ({
22
21
  isOpen,
@@ -30,7 +29,6 @@ const NavigationItemPopUp = ({
30
29
  usedContentTypesData,
31
30
  }) => {
32
31
 
33
- const { formatMessage } = useIntl();
34
32
 
35
33
  const handleOnSubmit = (payload) => {
36
34
  onSubmit(payload);
@@ -51,7 +49,7 @@ const NavigationItemPopUp = ({
51
49
  relatedRef: item,
52
50
  type: item.isSingle ? navigationItemType.INTERNAL : item.type,
53
51
  isCollection,
54
- }) ? '' : `[${formatMessage(getTrad('notification.navigation.item.relation.status.draft'))}] `.toUpperCase();
52
+ }) ? '' : `[${getMessage('notification.navigation.item.relation.status.draft')}] `.toUpperCase();
55
53
  return `${appendix}${label}`;
56
54
  };
57
55
 
@@ -80,7 +78,7 @@ const NavigationItemPopUp = ({
80
78
 
81
79
  return (
82
80
  <ModalLayout labelledBy="condition-modal-breadcrumbs" onClose={onClose} isOpen={isOpen}>
83
- <NavigationItemPopupHeader />
81
+ <NavigationItemPopupHeader isNewItem={!data.viewId}/>
84
82
  <NavigationItemForm
85
83
  data={prepareFormData(data)}
86
84
  isLoading={isLoading}
@@ -10,11 +10,13 @@ import { isEmpty, get } from "lodash";
10
10
 
11
11
  // Design System
12
12
  import { Main } from '@strapi/design-system/Main';
13
+ import { Flex } from '@strapi/design-system/Flex';
13
14
  import { ContentLayout } from '@strapi/design-system/Layout';
15
+ import { Typography } from '@strapi/design-system/Typography';
14
16
  import { Box } from '@strapi/design-system/Box';
17
+ import { Icon } from '@strapi/design-system/Icon';
15
18
  import { Button } from '@strapi/design-system/Button';
16
19
  import { LoadingIndicatorPage } from "@strapi/helper-plugin";
17
- import { EmptyStateLayout } from '@strapi/design-system/EmptyStateLayout';
18
20
  import EmptyDocumentsIcon from '@strapi/icons/EmptyDocuments';
19
21
  import PlusIcon from "@strapi/icons/Plus";
20
22
 
@@ -23,6 +25,7 @@ import List from '../../components/NavigationItemList';
23
25
  import NavigationContentHeader from './components/NavigationContentHeader';
24
26
  import NavigationHeader from './components/NavigationHeader';
25
27
  import NavigationItemPopUp from "./components/NavigationItemPopup";
28
+ import Search from '../../components/Search';
26
29
  import useDataManager from "../../hooks/useDataManager";
27
30
  import { getTrad } from '../../translations';
28
31
  import {
@@ -31,7 +34,6 @@ import {
31
34
  usedContentTypes,
32
35
  validateNavigationStructure,
33
36
  } from './utils/parsers';
34
- import Search from '../../components/Search';
35
37
 
36
38
  const View = () => {
37
39
  const {
@@ -150,11 +152,19 @@ const View = () => {
150
152
  changeNavigationItemPopupState(false);
151
153
  };
152
154
 
155
+ const handleChangeNavigationSelection = (...args) => {
156
+ handleChangeSelection(...args);
157
+ setSearchValue('');
158
+ }
159
+
153
160
  return (
154
161
  <Main labelledBy="title" aria-busy={isLoadingForSubmit}>
155
162
  <NavigationHeader
156
163
  structureHasErrors={structureHasErrors}
157
- structureHAsChanged={structureChanged}
164
+ structureHasChanged={structureChanged}
165
+ availableNavigations={availableNavigations}
166
+ activeNavigation={activeNavigation}
167
+ handleChangeSelection={handleChangeNavigationSelection}
158
168
  handleSave={handleSave}
159
169
  />
160
170
  <ContentLayout>
@@ -162,7 +172,7 @@ const View = () => {
162
172
  {changedActiveNavigation && (
163
173
  <>
164
174
  <NavigationContentHeader
165
- startActions={<Search value={searchValue} setValue={setSearchValue}/>}
175
+ startActions={<Search value={searchValue} setValue={setSearchValue} />}
166
176
  endActions={<Button
167
177
  onClick={addNewNavigationItem}
168
178
  startIcon={<PlusIcon />}
@@ -172,22 +182,21 @@ const View = () => {
172
182
  {formatMessage(getTrad('header.action.newItem'))}
173
183
  </Button>}
174
184
  />
175
- {isEmpty(changedActiveNavigation.items || []) && (<Box paddingTop={4} >
176
- <EmptyStateLayout
177
- action={
178
- <Button
179
- variant='secondary'
180
- startIcon={<PlusIcon />}
181
- label={formatMessage(getTrad('empty.cta'))}
182
- onClick={addNewNavigationItem}
183
- >
184
- {formatMessage(getTrad('empty.cta'))}
185
- </Button>
186
- }
187
- icon={<EmptyDocumentsIcon width='10rem' />}
188
- content={formatMessage(getTrad('empty'))}
189
- />
190
- </Box>
185
+ {isEmpty(changedActiveNavigation.items || []) && (
186
+ <Flex direction="column" minHeight="400px" justifyContent="center">
187
+ <Icon as={EmptyDocumentsIcon} width="160px" height="88px" color=""/>
188
+ <Box padding={4}>
189
+ <Typography variant="beta" textColor="neutral600">{formatMessage(getTrad('empty'))}</Typography>
190
+ </Box>
191
+ <Button
192
+ variant='secondary'
193
+ startIcon={<PlusIcon />}
194
+ label={formatMessage(getTrad('empty.cta'))}
195
+ onClick={addNewNavigationItem}
196
+ >
197
+ {formatMessage(getTrad('empty.cta'))}
198
+ </Button>
199
+ </Flex>
191
200
  )}
192
201
  {
193
202
  !isEmpty(changedActiveNavigation.items || [])
@@ -7,6 +7,7 @@ export const transformItemToRESTPayload = (
7
7
  parent = undefined,
8
8
  master = undefined,
9
9
  config = {},
10
+ parentAttachedToMenu = true,
10
11
  ) => {
11
12
  const {
12
13
  id,
@@ -34,7 +35,7 @@ export const transformItemToRESTPayload = (
34
35
  find(contentTypes,
35
36
  ct => ct.uid === relatedType) :
36
37
  undefined;
37
-
38
+ const itemAttachedToMenu = menuAttached && parentAttachedToMenu
38
39
  return {
39
40
  id,
40
41
  parent,
@@ -45,7 +46,7 @@ export const transformItemToRESTPayload = (
45
46
  removed,
46
47
  order,
47
48
  uiRouterKey,
48
- menuAttached,
49
+ menuAttached: itemAttachedToMenu,
49
50
  audience: audience.map((audienceItem) =>
50
51
  isObject(audienceItem) ? audienceItem.value : audienceItem,
51
52
  ),
@@ -60,7 +61,7 @@ export const transformItemToRESTPayload = (
60
61
  field: relatedContentType && relatedContentType.relatedField ? relatedContentType.relatedField : 'navigation',
61
62
  },
62
63
  ],
63
- items: items.map((iItem) => transformItemToRESTPayload(iItem, id, master, config)),
64
+ items: items.map((iItem) => transformItemToRESTPayload(iItem, id, master, config, itemAttachedToMenu)),
64
65
  };
65
66
  };
66
67
 
@@ -251,6 +252,9 @@ export const prepareItemToViewPayload = (items = [], viewParentId = null, config
251
252
  }));
252
253
 
253
254
  export const extractRelatedItemLabel = (item = {}, fields = {}, config = {}) => {
255
+ if (get(item, 'isSingle', false)) {
256
+ return get(item, 'labelSingular', '');
257
+ }
254
258
  const { contentTypes = [] } = config;
255
259
  const { __collectionUid } = item;
256
260
  const contentType = contentTypes.find(_ => _.uid === __collectionUid)
@@ -5,9 +5,10 @@
5
5
  "header.action.newItem": "New Item",
6
6
  "submit.cta.cancel": "Cancel",
7
7
  "submit.cta.save": "Save",
8
- "empty": "Navigation container is empty",
8
+ "empty": "Your navigation is empty",
9
9
  "empty.cta": "Create first item",
10
- "popup.item.header": "Manage navigation item",
10
+ "popup.item.header.edit": "Edit navigation item",
11
+ "popup.item.header.new": "New navigation item",
11
12
  "popup.item.form.title.label": "Title",
12
13
  "popup.item.form.title.placeholder": "Enter the item title or leave blank to pull from related entity",
13
14
  "popup.item.form.uiRouterKey.label": "UI router key",
@@ -17,17 +18,18 @@
17
18
  "popup.item.form.path.preview": "Preview:",
18
19
  "popup.item.form.externalPath.label": "External URL",
19
20
  "popup.item.form.externalPath.placeholder": "Link to the external source",
20
- "popup.item.form.externalPath.validation.type": "This value is not a proper url." ,
21
+ "popup.item.form.externalPath.validation.type": "This value is not a proper url.",
21
22
  "popup.item.form.menuAttached.label": "Attach to menu",
22
23
  "popup.item.form.type.label": "Internal link",
23
24
  "popup.item.form.type.internal.label": "Internal source",
24
25
  "popup.item.form.type.external.label": "External source",
25
26
  "popup.item.form.type.external.description": "Output path: {value}",
26
27
  "popup.item.form.audience.label": "Audience",
27
- "popup.item.form.audience.placeholder": "Type to start searching...",
28
+ "popup.item.form.audience.placeholder": "Select audience...",
28
29
  "popup.item.form.relatedSection.label": "Relation to",
29
30
  "popup.item.form.relatedType.label": "Content Type",
30
31
  "popup.item.form.relatedType.placeholder": "Select content type...",
32
+ "popup.item.form.relatedType.empty": "There are no content types to select",
31
33
  "popup.item.form.related.label": "Entity",
32
34
  "popup.item.form.related.empty": "There are no more entities of \"{ contentTypeName }\" to select",
33
35
  "popup.item.form.button.create": "Create item",
@@ -41,9 +43,49 @@
41
43
  "notification.navigation.item.relation": "Entity relation does not exist!",
42
44
  "notification.navigation.item.relation.status.draft": "draft",
43
45
  "notification.navigation.item.relation.status.published": "published",
44
- "navigation.item.action.newItem": "New nested item",
45
- "navigation.item.badge.removed": "Removed",
46
- "navigation.item.badge.draft": "{type}: Draft",
47
- "navigation.item.badge.published": "{type}: Published"
48
- }
49
-
46
+ "pages.settings.general.title": "General settings",
47
+ "pages.settings.additional.title": "Additional settings",
48
+ "pages.settings.nameField.title": "Content types settings",
49
+ "pages.settings.restoring.title": "Restoring",
50
+ "pages.settings.section.title": "Navigation Plugin",
51
+ "pages.settings.section.subtitle": "Configuration",
52
+ "pages.settings.header.title": "Navigation",
53
+ "pages.settings.header.description": "Configure the navigation plugin",
54
+ "pages.settings.actions.restart": "Restart Strapi",
55
+ "pages.settings.actions.submit": "Save configuration",
56
+ "pages.settings.actions.restore": "Restore configuration",
57
+ "pages.settings.actions.restore.confirmation.header": "Do you want to continue?",
58
+ "pages.settings.actions.restore.confirmation.confirm": "Restore",
59
+ "pages.settings.actions.restore.confirmation.description": "Plugin config will be restored from plugins.js file.",
60
+ "pages.settings.actions.restore.description": "Restoring the plugin configuration will cause it to be replaced with configuration saved inside 'plugins.js' file.",
61
+ "pages.settings.actions.restart.alert.title": "Strapi requires restart",
62
+ "pages.settings.actions.restart.alert.description": "You've made a configuration changes which requires your Strapi application to be restarted to take an effect in GraphQL schema. Do it manually or by using below trigger.",
63
+ "pages.settings.actions.restart.alert.close": "Discard",
64
+ "pages.settings.actions.restart.alert.cancel": "Cancel",
65
+ "pages.settings.notification.fetch.error": "Failed to fetch configuration. Retrying...",
66
+ "pages.settings.notification.submit.success": "Config has been updated successfully",
67
+ "pages.settings.notification.restore.success": "Config has been restored successfully",
68
+ "pages.settings.notification.restart.success": "Application has been restarted successfully",
69
+ "pages.settings.notification.submit.error": "Config update has failed",
70
+ "pages.settings.notification.restore.error": "Config restore has failed",
71
+ "pages.settings.notification.restart.error": "Failed to restart your application. Try to do it manually.",
72
+ "pages.settings.form.contentTypes.label": "Enable for Collection(s)",
73
+ "pages.settings.form.contentTypes.placeholder": "eg. Pages, Posts",
74
+ "pages.settings.form.contentTypes.hint": "Content types that can be related with navigation items. This configuration is applicable both for REST & GraphQL",
75
+ "pages.settings.form.allowedLevels.label": "Allowed levels",
76
+ "pages.settings.form.allowedLevels.placeholder": "eg. 2",
77
+ "pages.settings.form.allowedLevels.hint": "Maximum level for which you're able to mark item as \"Menu attached\"",
78
+ "pages.settings.form.audience.label": "Audience",
79
+ "pages.settings.form.audience.hint": "Enable audience field",
80
+ "pages.settings.form.nameField.label": "Name fields",
81
+ "pages.settings.form.nameField.placeholder": "Select at least one or leave empty to apply defaults",
82
+ "pages.settings.form.nameField.hint": "If left empty name field is going to take following ordered fields: \"title\", \"subject\" and \"name\"",
83
+ "components.navigationItem.action.newItem": "Add nested item",
84
+ "components.navigationItem.badge.removed": "Removed",
85
+ "components.navigationItem.badge.draft": "{type}: Draft",
86
+ "components.navigationItem.badge.published": "{type}: Published",
87
+ "components.confirmation.dialog.button.cancel": "Cancel",
88
+ "components.confirmation.dialog.button.confirm": "Confirm",
89
+ "components.confirmation.dialog.description": "Do you want to continue?",
90
+ "components.confirmation.dialog.header": "Confirmation"
91
+ }
@@ -38,8 +38,8 @@
38
38
  "notification.navigation.item.relation": "L'entitée n'a pas de relations!",
39
39
  "notification.navigation.item.relation.status.draft": "brouillon",
40
40
  "notification.navigation.item.relation.status.published": "publié",
41
- "navigation.item.action.newItem": "Nouvel élément imbriqué",
42
- "navigation.item.badge.removed": "Supprimé",
43
- "navigation.item.badge.draft": "Brouillon",
44
- "navigation.item.badge.published": "Publié"
41
+ "components.navigationItem.action.newItem": "Nouvel élément imbriqué",
42
+ "components.navigationItem.badge.removed": "Supprimé",
43
+ "components.navigationItem.badge.draft": "Brouillon",
44
+ "components.navigationItem.badge.published": "Publié"
45
45
  }
@@ -0,0 +1,51 @@
1
+ import { request } from '@strapi/helper-plugin';
2
+ import pluginId from '../pluginId';
3
+
4
+ export const fetchNavigationConfig = async () => {
5
+ try {
6
+ const data = await request(`/${pluginId}/settings/config`, { method: 'GET' });
7
+ return data;
8
+ } catch (err) {
9
+ toggleNotification({
10
+ type: 'warning',
11
+ message: { id: 'notification.error' },
12
+ });
13
+
14
+ return { err };
15
+ }
16
+ }
17
+
18
+ export const updateNavigationConfig = async ({ body }) =>
19
+ request(`/${pluginId}/config`, { method: 'PUT', body }, true);
20
+
21
+ export const restoreNavigationConfig = async () =>
22
+ request(`/${pluginId}/config`, { method: 'DELETE' }, true);
23
+
24
+ export const fetchAllContentTypes = async () => {
25
+ try {
26
+ const { data } = await request('/content-manager/content-types');
27
+ return { ...data }
28
+ } catch (err) {
29
+ toggleNotification({
30
+ type: 'warning',
31
+ message: { id: 'notification.error' },
32
+ });
33
+
34
+ return { err };
35
+ }
36
+ }
37
+
38
+ export const restartStrapi = async (toggleNotification) => {
39
+ try {
40
+ const { data } = await request(`/${pluginId}/settings/restart`);
41
+
42
+ return data;
43
+ } catch (err) {
44
+ toggleNotification({
45
+ type: 'warning',
46
+ message: { id: 'notification.error' },
47
+ });
48
+
49
+ return { err };
50
+ }
51
+ };
@@ -0,0 +1,20 @@
1
+ import { useIntl } from 'react-intl';
2
+ import { isString } from 'lodash';
3
+
4
+ import pluginId from '../pluginId';
5
+
6
+ const getMessage = (input, defaultMessage = '', inPluginScope = true) => {
7
+ const { formatMessage } = useIntl();
8
+ let formattedId = ''
9
+ if (isString(input)) {
10
+ formattedId = input;
11
+ } else {
12
+ formattedId = input?.id;
13
+ }
14
+ return formatMessage({
15
+ id: `${inPluginScope ? pluginId : 'app.components'}.${formattedId}`,
16
+ defaultMessage,
17
+ }, input?.props || undefined)
18
+ };
19
+
20
+ export { getMessage };
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "strapi-plugin-navigation",
3
- "version": "2.0.0-rc.1",
3
+ "version": "2.0.3",
4
4
  "description": "Strapi - Navigation plugin",
5
5
  "strapi": {
6
6
  "name": "navigation",
7
- "icon": "hamburger",
8
- "description": "UI navigation management",
7
+ "displayName": "Navigation",
8
+ "description": "Create consumable navigation with a simple and straightforward visual builder",
9
9
  "kind": "plugin"
10
10
  },
11
11
  "repository": {
@@ -13,10 +13,11 @@
13
13
  "url": "https://github.com/VirtusLab/strapi-plugin-navigation"
14
14
  },
15
15
  "scripts": {
16
+ "publish:latest": "npm publish --tag latest",
16
17
  "test:unit": "jest --verbose --coverage"
17
18
  },
18
19
  "dependencies": {
19
- "@strapi/utils": "^4.0.5",
20
+ "@strapi/utils": "^4.1.0",
20
21
  "uuid": "^8.3.0",
21
22
  "bad-words": "^3.0.3",
22
23
  "lodash": "^4.17.11",
@@ -43,8 +44,8 @@
43
44
  "@strapi/strapi": "4.x"
44
45
  },
45
46
  "author": {
46
- "name": "VirtusLab // Mateusz Ziarko",
47
- "email": "mziarko@virtuslab.com",
47
+ "name": "VirtusLab",
48
+ "email": "strapi@virtuslab.com",
48
49
  "url": "https://virtuslab.com"
49
50
  },
50
51
  "maintainers": [
@@ -23,7 +23,9 @@ module.exports = async ({ strapi }) => {
23
23
  pluginName: "navigation",
24
24
  },
25
25
  ];
26
+ await strapi.admin.services.permission.actionProvider.registerMany(actions);
26
27
 
28
+ // Initialize first navigation
27
29
  const navigations = await strapi
28
30
  .query("plugin::navigation.navigation")
29
31
  .findMany();
@@ -38,5 +40,32 @@ module.exports = async ({ strapi }) => {
38
40
  }
39
41
  });
40
42
  }
41
- await strapi.admin.services.permission.actionProvider.registerMany(actions);
43
+
44
+ // Initialize configuration
45
+ const pluginStore = strapi.store({
46
+ environment: '',
47
+ type: 'plugin',
48
+ name: 'navigation',
49
+ });
50
+
51
+ const config = await pluginStore.get({ key: 'config' });
52
+ const pluginDefaultConfig = await strapi.plugin('navigation').config
53
+ const defaultConfigValue = {
54
+ additionalFields: pluginDefaultConfig('additionalFields'),
55
+ contentTypes: pluginDefaultConfig('contentTypes'),
56
+ contentTypesNameFields: pluginDefaultConfig('contentTypesNameFields'),
57
+ allowedLevels: pluginDefaultConfig('allowedLevels'),
58
+ gql: pluginDefaultConfig('gql'),
59
+ }
60
+
61
+ if (!config) {
62
+ pluginStore.set({
63
+ key: 'config', value: defaultConfigValue
64
+ });
65
+ }
66
+
67
+ if (strapi.plugin('graphql')) {
68
+ const graphqlConfiguration = require('./graphql')
69
+ await graphqlConfiguration({ strapi, config: config || defaultConfigValue });
70
+ }
42
71
  };
@@ -0,0 +1,45 @@
1
+ {
2
+ "collectionName": "navigations",
3
+ "info": {
4
+ "singularName": "navigation",
5
+ "pluralName": "navigations",
6
+ "displayName": "Navigation",
7
+ "name": "navigation"
8
+ },
9
+ "options": {
10
+ "increments": true,
11
+ "comment": ""
12
+ },
13
+ "pluginOptions": {
14
+ "content-manager": {
15
+ "visible": true
16
+ },
17
+ "content-type-builder": {
18
+ "visible": false
19
+ }
20
+ },
21
+ "attributes": {
22
+ "name": {
23
+ "type": "text",
24
+ "configurable": false,
25
+ "required": true
26
+ },
27
+ "slug": {
28
+ "type": "uid",
29
+ "target": "name",
30
+ "configurable": false,
31
+ "required": true
32
+ },
33
+ "visible": {
34
+ "type": "boolean",
35
+ "default": false,
36
+ "configurable": false
37
+ },
38
+ "items": {
39
+ "type": "relation",
40
+ "relation": "oneToMany",
41
+ "target": "plugin::navigation.navigation-item",
42
+ "configurable": false
43
+ }
44
+ }
45
+ }
@@ -4,7 +4,7 @@
4
4
  "singularName": "navigation-item",
5
5
  "pluralName": "navigation-items",
6
6
  "displayName": "Navigation Item",
7
- "tableName": "navigation-item"
7
+ "name": "navigation-item"
8
8
  },
9
9
  "options": {
10
10
  "increments": true,
@@ -19,10 +19,34 @@ const errorHandler = (ctx) => (error) => {
19
19
  throw error;
20
20
  };
21
21
 
22
- module.exports = {
22
+ module.exports = ({strapi}) => ({
23
23
  async config() {
24
24
  return getService().config();
25
25
  },
26
+
27
+ async updateConfig(ctx) {
28
+ await getService().updateConfig(ctx.request.body)
29
+ return ctx.send({ status: 200 });
30
+ },
31
+
32
+ async restoreConfig(ctx) {
33
+ await getService().restoreConfig()
34
+ return ctx.send({ status: 200 })
35
+ },
36
+
37
+ async settingsConfig() {
38
+ return getService().config(true);
39
+ },
40
+
41
+ async settingsRestart(ctx) {
42
+ try {
43
+ await getService().restart();
44
+ return ctx.send({ status: 200 });
45
+ } catch (e) {
46
+ errorHandler(ctx, e);
47
+ }
48
+ },
49
+
26
50
  async get() {
27
51
  return getService().get();
28
52
  },
@@ -50,13 +74,14 @@ module.exports = {
50
74
  },
51
75
  async render(ctx) {
52
76
  const { params, query = {} } = ctx;
53
- const { type, menu: menuOnly } = query;
77
+ const { type, menu: menuOnly, path: rootPath } = query;
54
78
  const { idOrSlug } = parseParams(params);
55
- return getService().render(
79
+ return getService().render({
56
80
  idOrSlug,
57
81
  type,
58
82
  menuOnly,
59
- );
83
+ rootPath
84
+ });
60
85
  },
61
86
  async renderChild(ctx) {
62
87
  const { params, query = {} } = ctx;
@@ -69,4 +94,4 @@ module.exports = {
69
94
  menuOnly
70
95
  );
71
96
  },
72
- };
97
+ });
@@ -2,16 +2,15 @@ const getTypes = require('./types');
2
2
  const getQueries = require('./queries');
3
3
  const getResolversConfig = require('./resolvers-config');
4
4
 
5
- module.exports = () => {
5
+ module.exports = async ({ strapi, config }) => {
6
6
  const extensionService = strapi.plugin('graphql').service('extension');
7
-
8
7
  extensionService.shadowCRUD('plugin::navigation.audience').disable();
9
8
  extensionService.shadowCRUD('plugin::navigation.navigation').disable();
10
9
  extensionService.shadowCRUD('plugin::navigation.navigation-item').disable();
11
10
  extensionService.shadowCRUD('plugin::navigation.navigations-items-related').disable();
12
11
 
13
- extensionService.use(({ nexus }) => {
14
- const types = getTypes({ strapi, nexus });
12
+ extensionService.use(({strapi, nexus}) => {
13
+ const types = getTypes({ strapi, nexus, config });
15
14
  const queries = getQueries({ strapi, nexus });
16
15
  const resolversConfig = getResolversConfig({ strapi });
17
16
 
@@ -5,11 +5,12 @@ module.exports = ({ strapi, nexus }) => {
5
5
  args: {
6
6
  navigationIdOrSlug: nonNull(stringArg()),
7
7
  type: 'NavigationRenderType',
8
- menuOnly: booleanArg()
8
+ menuOnly: booleanArg(),
9
+ path: stringArg(),
9
10
  },
10
11
  resolve(obj, args) {
11
- const { navigationIdOrSlug, type, menuOnly } = args;
12
- return strapi.plugin('navigation').service('navigation').render(navigationIdOrSlug, type, menuOnly);
12
+ const { navigationIdOrSlug: idOrSlug, type, menuOnly, path: rootPath } = args;
13
+ return strapi.plugin('navigation').service('navigation').render({idOrSlug, type, menuOnly, rootPath});
13
14
  },
14
15
  };
15
16
  }
@@ -1,8 +1,10 @@
1
1
  module.exports = ({ nexus }) => nexus.objectType({
2
2
  name: "ContentTypesNameFields",
3
- definition(t) {
3
+ async definition(t) {
4
4
  t.nonNull.list.nonNull.string("default")
5
- const contentTypesNameFields = strapi.plugin('navigation').config('contentTypesNameFields')
5
+ const pluginStore = strapi.store({ type: 'plugin', name: 'navigation' });
6
+ const config = await pluginStore.get({ key: 'config' });
7
+ const contentTypesNameFields = config.contentTypesNameFields;
6
8
  Object.keys(contentTypesNameFields || {}).forEach(key => t.nonNull.list.string(key))
7
9
  }
8
10
  })
@@ -1,5 +1,5 @@
1
- module.exports = ({ strapi, nexus }) => {
2
- const related = strapi.plugin('navigation').config('gql')?.navigationItemRelated;
1
+ module.exports = ({ strapi, nexus, config }) => {
2
+ const related = config.gql?.navigationItemRelated;
3
3
  const name = "NavigationRelated";
4
4
 
5
5
  if (related?.length) {