strapi-plugin-navigation 2.2.3 → 2.2.5

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 (66) hide show
  1. package/README.md +35 -6
  2. package/admin/src/components/AdditionalFieldInput/index.js +2 -2
  3. package/admin/src/components/AdditionalFieldInput/types.d.ts +1 -0
  4. package/admin/src/components/Item/ItemCardHeader/icons.d.ts +1 -1
  5. package/admin/src/components/Item/ItemCardHeader/icons.js +2 -1
  6. package/admin/src/components/Item/ItemCardHeader/index.d.ts +1 -0
  7. package/admin/src/components/Item/ItemCardHeader/index.js +10 -8
  8. package/admin/src/components/Item/index.js +7 -6
  9. package/admin/src/components/NavigationItemList/index.d.ts +2 -1
  10. package/admin/src/components/NavigationItemList/index.js +2 -2
  11. package/admin/src/hooks/useNavigationManager.d.ts +0 -1
  12. package/admin/src/index.js +1 -1
  13. package/admin/src/pages/DataManagerProvider/index.js +2 -0
  14. package/admin/src/pages/DataManagerProvider/reducer.d.ts +1 -1
  15. package/admin/src/pages/NoAccessPage/index.d.ts +3 -0
  16. package/admin/src/pages/NoAccessPage/index.js +31 -0
  17. package/admin/src/pages/SettingsPage/index.d.ts +0 -1
  18. package/admin/src/pages/SettingsPage/index.js +9 -0
  19. package/admin/src/pages/SettingsPage/types.d.ts +1 -1
  20. package/admin/src/pages/View/components/NavigationHeader/index.d.ts +2 -1
  21. package/admin/src/pages/View/components/NavigationHeader/index.js +9 -8
  22. package/admin/src/pages/View/components/NavigationItemForm/index.js +36 -24
  23. package/admin/src/pages/View/components/NavigationItemForm/types.d.ts +8 -1
  24. package/admin/src/pages/View/components/NavigationItemPopup/NavigationItemPopupFooter.d.ts +4 -2
  25. package/admin/src/pages/View/components/NavigationItemPopup/NavigationItemPopupFooter.js +5 -1
  26. package/admin/src/pages/View/components/NavigationItemPopup/NavigationItemPopupHeader.d.ts +2 -1
  27. package/admin/src/pages/View/components/NavigationItemPopup/NavigationItemPopupHeader.js +6 -2
  28. package/admin/src/pages/View/components/NavigationItemPopup/index.d.ts +4 -1
  29. package/admin/src/pages/View/components/NavigationItemPopup/index.js +5 -3
  30. package/admin/src/pages/View/components/NavigationManager/AllNavigations/icons.d.ts +0 -1
  31. package/admin/src/pages/View/components/NavigationManager/AllNavigations/index.d.ts +0 -1
  32. package/admin/src/pages/View/components/NavigationManager/DeletionConfirm/index.d.ts +0 -1
  33. package/admin/src/pages/View/components/NavigationManager/ErrorDetails/index.d.ts +0 -1
  34. package/admin/src/pages/View/components/NavigationManager/Form/index.d.ts +0 -1
  35. package/admin/src/pages/View/components/NavigationManager/NavigationUpdate/index.d.ts +0 -1
  36. package/admin/src/pages/View/components/NavigationManager/NewNavigation/index.d.ts +0 -1
  37. package/admin/src/pages/View/components/NavigationManager/index.d.ts +0 -1
  38. package/admin/src/pages/View/index.js +40 -20
  39. package/admin/src/permissions.d.ts +4 -0
  40. package/admin/src/permissions.js +1 -0
  41. package/admin/src/translations/en.json +8 -0
  42. package/package.json +2 -2
  43. package/permissions.d.ts +1 -0
  44. package/permissions.js +1 -0
  45. package/server/bootstrap/index.js +6 -0
  46. package/server/config/index.js +0 -1
  47. package/server/config/setupStrategy.js +0 -9
  48. package/server/content-types/index.d.ts +2 -0
  49. package/server/content-types/navigation/index.d.ts +1 -0
  50. package/server/content-types/navigation/schema.d.ts +1 -0
  51. package/server/content-types/navigation/schema.js +2 -1
  52. package/server/content-types/navigation-item/index.d.ts +1 -0
  53. package/server/content-types/navigation-item/schema.d.ts +1 -0
  54. package/server/content-types/navigation-item/schema.js +2 -1
  55. package/server/controllers/admin.js +13 -0
  56. package/server/index.d.ts +2 -0
  57. package/server/routes/admin.js +10 -0
  58. package/server/services/admin.js +2 -10
  59. package/server/services/client.js +10 -14
  60. package/server/services/common.js +27 -1
  61. package/strapi-server.d.ts +2 -0
  62. package/tsconfig.tsbuildinfo +1 -1
  63. package/types/config.d.ts +0 -1
  64. package/types/controllers.d.ts +5 -0
  65. package/types/services.d.ts +1 -0
  66. package/types/utils.d.ts +5 -0
package/README.md CHANGED
@@ -118,7 +118,7 @@ Complete installation requirements are exact same as for Strapi itself and can b
118
118
 
119
119
  **Supported Strapi versions**:
120
120
 
121
- - Strapi v4.2.0 (recently tested)
121
+ - Strapi v4.9.0 (recently tested)
122
122
  - Strapi v4.x
123
123
 
124
124
  > This plugin is designed for **Strapi v4** and is not working with v3.x. To get version for **Strapi v3** install version [v1.x](https://github.com/VirtusLab-Open-Source/strapi-plugin-navigation/tree/strapi-v3).
@@ -149,6 +149,9 @@ Config for this plugin is stored as a part of the `config/plugins.js` or `config
149
149
  > *Note v2.0.3 and newer only*
150
150
  > Changing this file will not automatically change plugin configuration. To synchronize plugin's config with plugins.js file, it is necessary to restore configuration through the settings page
151
151
 
152
+ > *Note for newer than v2.2.0*
153
+ > `slugify` as been removed. **THIS A BREAKING CHANGE**
154
+
152
155
  ```js
153
156
  module.exports = ({ env }) => ({
154
157
  // ...
@@ -160,13 +163,11 @@ Config for this plugin is stored as a part of the `config/plugins.js` or `config
160
163
  contentTypesNameFields: {
161
164
  'api::page.page': ['title']
162
165
  },
166
+ pathDefaultFields: {
167
+ 'api::page.page': ['slug']
168
+ },
163
169
  allowedLevels: 2,
164
170
  gql: {...},
165
- slugify: {
166
- customReplacements: [
167
- ["🤔", "thinking"],
168
- ],
169
- }
170
171
  }
171
172
  }
172
173
  });
@@ -177,6 +178,7 @@ Config for this plugin is stored as a part of the `config/plugins.js` or `config
177
178
  - `allowedLevels` - Maximum level for which you're able to mark item as "Menu attached"
178
179
  - `contentTypes` - UIDs of related content types
179
180
  - `contentTypesNameFields` - Definition of content type title fields like `'api::<collection name>.<content type name>': ['field_name_1', 'field_name_2']`, if not set titles are pulled from fields like `['title', 'subject', 'name']`. **TIP** - Proper content type uid you can find in the URL of Content Manager where you're managing relevant entities like: `admin/content-manager/collectionType/< THE UID HERE >?page=1&pageSize=10&sort=Title:ASC&plugins[i18n][locale]=en`
181
+ - `pathDefaultFields` - The attribute to copy the default path from per content type. Syntax: `'api::<collection name>.<content type name>': ['url_slug', 'path']`
180
182
  - `gql` - If you're using GraphQL that's the right place to put all necessary settings. More **[ here ](#gql-configuration)**
181
183
  - `i18nEnabled` - should you want to manage multi-locale content via navigation set this value `Enabled`. More **[ here ](#i18n-internationalization)**
182
184
  - `cascadeMenuAttached` - If you don't want "Menu attached" to cascade on child items set this value `Disabled`.
@@ -632,6 +634,33 @@ module.exports = {
632
634
 
633
635
  If you already got it, make sure that `navigation` plugin is inserted before `graphql`. That should do the job.
634
636
 
637
+ ### Slug generation
638
+
639
+ #### Customisation
640
+
641
+ Slug generation is available as a controller and service. If you have custom requirements outside of what this plugin provides you can add your own logic with [plugins extensions](https://docs.strapi.io/developer-docs/latest/development/plugins-extension.html).
642
+
643
+ For example:
644
+
645
+ ```ts
646
+ // path: ./src/index.js
647
+
648
+ module.exports = {
649
+ // ...
650
+ bootstrap({ strapi }) {
651
+ const navigationCommonService = strapi.plugin("navigation").service("common");
652
+ const originalGetSlug = navigationCommonService.getSlug;
653
+ const preprocess = (q) => {
654
+ return q + "suffix";
655
+ };
656
+
657
+ navigationCommonService.getSlug = (query) => {
658
+ return originalGetSlug(preprocess(query));
659
+ };
660
+ },
661
+ };
662
+ ```
663
+
635
664
  ## 🤝 Contributing
636
665
 
637
666
  <div>
@@ -36,14 +36,14 @@ const DEFAULT_STRING_VALUE = "";
36
36
  const handlerFactory = ({ field, prop, onChange }) => ({ target }) => {
37
37
  onChange(field.name, target[prop]);
38
38
  };
39
- const AdditionalFieldInput = ({ field, isLoading, onChange, value, error }) => {
39
+ const AdditionalFieldInput = ({ field, isLoading, onChange, value, disabled, error }) => {
40
40
  const toggleNotification = (0, helper_plugin_1.useNotification)();
41
41
  const { formatMessage } = (0, react_intl_1.useIntl)();
42
42
  const defaultInputProps = (0, react_1.useMemo)(() => ({
43
43
  id: field.name,
44
44
  name: field.name,
45
45
  label: field.label,
46
- disabled: isLoading,
46
+ disabled: isLoading || disabled,
47
47
  error: error && formatMessage(error),
48
48
  }), [field, isLoading, error]);
49
49
  const handleBoolean = (0, react_1.useMemo)(() => handlerFactory({ field, onChange, prop: "checked" }), [onChange, field]);
@@ -5,6 +5,7 @@ export declare type AdditionalFieldInputProps = {
5
5
  isLoading: boolean;
6
6
  onChange: (name: string, value: string) => void;
7
7
  value: string | boolean | string[] | null;
8
+ disabled: boolean;
8
9
  error: MessageDescriptor | null;
9
10
  };
10
11
  export declare type TargetProp = "value" | "checked";
@@ -1,5 +1,5 @@
1
- /// <reference types="react" />
2
1
  export declare const pencilIcon: JSX.Element;
3
2
  export declare const refreshIcon: JSX.Element;
4
3
  export declare const trashIcon: JSX.Element;
4
+ export declare const eyeIcon: JSX.Element;
5
5
  //# sourceMappingURL=icons.d.ts.map
@@ -3,10 +3,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.trashIcon = exports.refreshIcon = exports.pencilIcon = void 0;
6
+ exports.eyeIcon = exports.trashIcon = exports.refreshIcon = exports.pencilIcon = void 0;
7
7
  const react_1 = __importDefault(require("react"));
8
8
  const icons_1 = require("@strapi/icons");
9
9
  exports.pencilIcon = react_1.default.createElement(icons_1.Pencil, null);
10
10
  exports.refreshIcon = react_1.default.createElement(icons_1.Refresh, null);
11
11
  exports.trashIcon = react_1.default.createElement(icons_1.Trash, null);
12
+ exports.eyeIcon = react_1.default.createElement(icons_1.Eye, null);
12
13
  //# sourceMappingURL=icons.js.map
@@ -5,6 +5,7 @@ interface IProps {
5
5
  path: string;
6
6
  icon: ToBeFixed;
7
7
  removed: boolean;
8
+ canUpdate: boolean;
8
9
  onItemRemove: VoidEffect;
9
10
  onItemEdit: VoidEffect;
10
11
  onItemRestore: VoidEffect;
@@ -14,18 +14,20 @@ const ItemCardBadge_1 = __importDefault(require("../ItemCardBadge"));
14
14
  const utils_1 = require("../../../utils");
15
15
  const icons_1 = require("./icons");
16
16
  const wrapperStyle = { zIndex: 2 };
17
- const ItemCardHeader = ({ title, path, icon, removed, onItemRemove, onItemEdit, onItemRestore, dragRef }) => (react_1.default.createElement(Wrapper_1.default, null,
17
+ const pathWrapperStyle = { maxWidth: "425px" };
18
+ const ItemCardHeader = ({ title, path, icon, removed, canUpdate, onItemRemove, onItemEdit, onItemRestore, dragRef }) => (react_1.default.createElement(Wrapper_1.default, null,
18
19
  react_1.default.createElement(Flex_1.Flex, { alignItems: "center" },
19
- react_1.default.createElement(DragButton_1.default, { ref: dragRef }),
20
+ canUpdate && (react_1.default.createElement(DragButton_1.default, { ref: dragRef })),
20
21
  react_1.default.createElement(Typography_1.Typography, { variant: "omega", fontWeight: "bold" }, title),
21
- react_1.default.createElement(Typography_1.Typography, { variant: "omega", fontWeight: "bold", textColor: 'neutral500' }, path),
22
- react_1.default.createElement(Icon_1.Icon, { as: icon })),
22
+ react_1.default.createElement(Typography_1.Typography, { variant: "omega", fontWeight: "bold", textColor: 'neutral500', ellipsis: true, style: pathWrapperStyle }, path),
23
+ react_1.default.createElement(Flex_1.Flex, null,
24
+ react_1.default.createElement(Icon_1.Icon, { as: icon }))),
23
25
  react_1.default.createElement(Flex_1.Flex, { alignItems: "center", style: wrapperStyle },
24
26
  removed &&
25
27
  (react_1.default.createElement(ItemCardBadge_1.default, { borderColor: "danger200", backgroundColor: "danger100", textColor: "danger600" }, (0, utils_1.getMessage)("components.navigationItem.badge.removed"))),
26
- react_1.default.createElement(IconButton_1.IconButton, { disabled: removed, onClick: onItemEdit, label: "Edit", icon: icons_1.pencilIcon }),
27
- removed ?
28
- react_1.default.createElement(IconButton_1.IconButton, { onClick: onItemRestore, label: "Restore", icon: icons_1.refreshIcon }) :
29
- react_1.default.createElement(IconButton_1.IconButton, { onClick: onItemRemove, label: "Remove", icon: icons_1.trashIcon }))));
28
+ react_1.default.createElement(IconButton_1.IconButton, { disabled: removed, onClick: onItemEdit, label: (0, utils_1.getMessage)(`components.navigationItem.action.${canUpdate ? 'edit' : 'view'}`, canUpdate ? 'Edit' : 'View'), icon: canUpdate ? icons_1.pencilIcon : icons_1.eyeIcon }),
29
+ canUpdate && (react_1.default.createElement(react_1.default.Fragment, null, removed ?
30
+ react_1.default.createElement(IconButton_1.IconButton, { onClick: onItemRestore, label: (0, utils_1.getMessage)('components.navigationItem.action.restore', "Restore"), icon: icons_1.refreshIcon }) :
31
+ react_1.default.createElement(IconButton_1.IconButton, { onClick: onItemRemove, label: (0, utils_1.getMessage)('components.navigationItem.action.remove', "Remove"), icon: icons_1.trashIcon }))))));
30
32
  exports.default = ItemCardHeader;
31
33
  //# sourceMappingURL=index.js.map
@@ -46,7 +46,7 @@ const ItemCardRemovedOverlay_1 = require("./ItemCardRemovedOverlay");
46
46
  const utils_1 = require("../../utils");
47
47
  const CollapseButton_1 = __importDefault(require("../CollapseButton"));
48
48
  const Item = (props) => {
49
- const { item, isLast = false, level = 0, levelPath = '', allowedLevels, relatedRef, isParentAttachedToMenu, onItemLevelAdd, onItemRemove, onItemRestore, onItemEdit, onItemReOrder, onItemToggleCollapse, error, displayChildren, config = {}, } = props;
49
+ const { item, isLast = false, level = 0, levelPath = '', allowedLevels, relatedRef, isParentAttachedToMenu, onItemLevelAdd, onItemRemove, onItemRestore, onItemEdit, onItemReOrder, onItemToggleCollapse, error, displayChildren, config = {}, permissions = {}, } = props;
50
50
  const { viewId, title, type, path, removed, externalPath, menuAttached, collapsed, structureId, items = [], } = item;
51
51
  const { contentTypes = [], contentTypesNameFields } = config;
52
52
  const isExternal = type === utils_1.navigationItemType.EXTERNAL;
@@ -60,6 +60,7 @@ const Item = (props) => {
60
60
  const relatedItemLabel = !isExternal ? (0, parsers_1.extractRelatedItemLabel)(relatedRef, contentTypesNameFields, { contentTypes }) : '';
61
61
  const relatedTypeLabel = relatedRef?.labelSingular;
62
62
  const relatedBadgeColor = isPublished ? 'success' : 'secondary';
63
+ const { canUpdate } = permissions;
63
64
  const dragRef = (0, react_1.useRef)(null);
64
65
  const dropRef = (0, react_1.useRef)(null);
65
66
  const previewRef = (0, react_1.useRef)(null);
@@ -110,7 +111,7 @@ const Item = (props) => {
110
111
  const { isSingle } = contentType;
111
112
  return `/content-manager/${isSingle ? 'singleType' : 'collectionType'}/${entity?.__collectionUid}${!isSingle ? '/' + entity?.id : ''}`;
112
113
  };
113
- const onNewItemClick = (0, react_1.useCallback)((event) => onItemLevelAdd(event, viewId, isNextMenuAllowedLevel, absolutePath, menuAttached, `${structureId}.${items.length}`), [viewId, isNextMenuAllowedLevel, absolutePath, menuAttached, structureId, items]);
114
+ const onNewItemClick = (0, react_1.useCallback)((event) => canUpdate && onItemLevelAdd(event, viewId, isNextMenuAllowedLevel, absolutePath, menuAttached, `${structureId}.${items.length}`), [viewId, isNextMenuAllowedLevel, absolutePath, menuAttached, structureId, items, canUpdate]);
114
115
  return (react_1.default.createElement(Wrapper_1.default, { level: level, isLast: isLast, style: { opacity: isDragging ? 0.2 : 1 }, ref: refs ? refs.dropRef : null },
115
116
  react_1.default.createElement(Card_1.Card, { style: { width: "728px", zIndex: 1, position: "relative", overflow: 'hidden' } },
116
117
  removed && (react_1.default.createElement(ItemCardRemovedOverlay_1.ItemCardRemovedOverlay, null)),
@@ -120,14 +121,14 @@ const Item = (props) => {
120
121
  ...item,
121
122
  isMenuAllowedLevel,
122
123
  isParentAttachedToMenu,
123
- }, levelPath, isParentAttachedToMenu), onItemRestore: () => onItemRestore(item), dragRef: refs.dragRef, removed: removed })),
124
+ }, levelPath, isParentAttachedToMenu), onItemRestore: () => onItemRestore(item), dragRef: refs.dragRef, removed: removed, canUpdate: canUpdate })),
124
125
  react_1.default.createElement(Divider_1.Divider, null),
125
126
  !isExternal && (react_1.default.createElement(Card_1.CardBody, { style: { padding: '8px' } },
126
127
  react_1.default.createElement(Flex_1.Flex, { style: { width: '100%' }, direction: "row", alignItems: "center", justifyContent: "space-between" },
127
128
  react_1.default.createElement(Flex_1.Flex, null,
128
129
  !(0, lodash_1.isEmpty)(item.items) && react_1.default.createElement(CollapseButton_1.default, { toggle: () => onItemToggleCollapse(item), collapsed: collapsed, itemsCount: item.items.length }),
129
- react_1.default.createElement(TextButton_1.TextButton, { disabled: removed, startIcon: react_1.default.createElement(icons_1.Plus, null), onClick: onNewItemClick },
130
- react_1.default.createElement(Typography_1.Typography, { variant: "pi", fontWeight: "bold", textColor: removed ? "neutral600" : "primary600" }, (0, utils_1.getMessage)("components.navigationItem.action.newItem")))),
130
+ canUpdate && (react_1.default.createElement(TextButton_1.TextButton, { disabled: removed, startIcon: react_1.default.createElement(icons_1.Plus, null), onClick: onNewItemClick },
131
+ react_1.default.createElement(Typography_1.Typography, { variant: "pi", fontWeight: "bold", textColor: removed ? "neutral600" : "primary600" }, (0, utils_1.getMessage)("components.navigationItem.action.newItem"))))),
131
132
  relatedItemLabel && (react_1.default.createElement(Flex_1.Flex, { justifyContent: 'center', alignItems: 'center' },
132
133
  isHandledByPublishFlow && (react_1.default.createElement(ItemCardBadge_1.default, { borderColor: `${relatedBadgeColor}200`, backgroundColor: `${relatedBadgeColor}100`, textColor: `${relatedBadgeColor}600`, className: "action", small: true }, (0, utils_1.getMessage)({ id: `components.navigationItem.badge.${isPublished ? 'published' : 'draft'}` }))),
133
134
  react_1.default.createElement(Typography_1.Typography, { variant: "omega", textColor: 'neutral600' },
@@ -135,7 +136,7 @@ const Item = (props) => {
135
136
  "\u00A0/\u00A0"),
136
137
  react_1.default.createElement(Typography_1.Typography, { variant: "omega", textColor: 'neutral800' }, relatedItemLabel),
137
138
  react_1.default.createElement(Link_1.Link, { to: `/content-manager/collectionType/${relatedRef?.__collectionUid}/${relatedRef?.id}`, endIcon: react_1.default.createElement(icons_1.ArrowRight, null) }, "\u00A0")))))))),
138
- hasChildren && !removed && !collapsed && react_1.default.createElement(NavigationItemList_1.default, { onItemLevelAdd: onItemLevelAdd, onItemRemove: onItemRemove, onItemEdit: onItemEdit, onItemRestore: onItemRestore, onItemReOrder: onItemReOrder, onItemToggleCollapse: onItemToggleCollapse, error: error, allowedLevels: allowedLevels, isParentAttachedToMenu: menuAttached, items: item.items, level: level + 1, levelPath: absolutePath, contentTypes: contentTypes, contentTypesNameFields: contentTypesNameFields })));
139
+ hasChildren && !removed && !collapsed && react_1.default.createElement(NavigationItemList_1.default, { onItemLevelAdd: onItemLevelAdd, onItemRemove: onItemRemove, onItemEdit: onItemEdit, onItemRestore: onItemRestore, onItemReOrder: onItemReOrder, onItemToggleCollapse: onItemToggleCollapse, error: error, allowedLevels: allowedLevels, isParentAttachedToMenu: menuAttached, items: item.items, level: level + 1, levelPath: absolutePath, contentTypes: contentTypes, contentTypesNameFields: contentTypesNameFields, permissions: permissions })));
139
140
  };
140
141
  Item.propTypes = {
141
142
  item: prop_types_1.default.shape({
@@ -1,5 +1,5 @@
1
1
  export default List;
2
- declare function List({ allowedLevels, error, isParentAttachedToMenu, items, level, levelPath, onItemEdit, onItemLevelAdd, onItemRemove, onItemRestore, onItemReOrder, onItemToggleCollapse, displayFlat, contentTypes, contentTypesNameFields, }: {
2
+ declare function List({ allowedLevels, error, isParentAttachedToMenu, items, level, levelPath, onItemEdit, onItemLevelAdd, onItemRemove, onItemRestore, onItemReOrder, onItemToggleCollapse, displayFlat, contentTypes, contentTypesNameFields, permissions, }: {
3
3
  allowedLevels: any;
4
4
  error: any;
5
5
  isParentAttachedToMenu?: boolean | undefined;
@@ -15,6 +15,7 @@ declare function List({ allowedLevels, error, isParentAttachedToMenu, items, lev
15
15
  displayFlat: any;
16
16
  contentTypes: any;
17
17
  contentTypesNameFields: any;
18
+ permissions: any;
18
19
  }): JSX.Element;
19
20
  declare namespace List {
20
21
  namespace propTypes {
@@ -7,12 +7,12 @@ const react_1 = __importDefault(require("react"));
7
7
  const prop_types_1 = __importDefault(require("prop-types"));
8
8
  const Item_1 = __importDefault(require("../Item"));
9
9
  const Wrapper_1 = __importDefault(require("./Wrapper"));
10
- const List = ({ allowedLevels, error, isParentAttachedToMenu = false, items, level = 0, levelPath = '', onItemEdit, onItemLevelAdd, onItemRemove, onItemRestore, onItemReOrder, onItemToggleCollapse, displayFlat, contentTypes, contentTypesNameFields, }) => (react_1.default.createElement(Wrapper_1.default, { level: level }, items.map((item, n) => {
10
+ const List = ({ allowedLevels, error, isParentAttachedToMenu = false, items, level = 0, levelPath = '', onItemEdit, onItemLevelAdd, onItemRemove, onItemRestore, onItemReOrder, onItemToggleCollapse, displayFlat, contentTypes, contentTypesNameFields, permissions, }) => (react_1.default.createElement(Wrapper_1.default, { level: level }, items.map((item, n) => {
11
11
  const { relatedRef, ...itemProps } = item;
12
12
  return (react_1.default.createElement(Item_1.default, { key: `list-item-${item.viewId || n}`, item: itemProps, isLast: n === items.length - 1, relatedRef: relatedRef, level: level, levelPath: levelPath, isParentAttachedToMenu: isParentAttachedToMenu, allowedLevels: allowedLevels, onItemRestore: onItemRestore, onItemLevelAdd: onItemLevelAdd, onItemRemove: onItemRemove, onItemEdit: onItemEdit, onItemReOrder: onItemReOrder, onItemToggleCollapse: onItemToggleCollapse, error: error, displayChildren: displayFlat, config: {
13
13
  contentTypes,
14
14
  contentTypesNameFields
15
- } }));
15
+ }, permissions: permissions }));
16
16
  })));
17
17
  List.propTypes = {
18
18
  allowedLevels: prop_types_1.default.number,
@@ -1,4 +1,3 @@
1
- /// <reference types="react" />
2
1
  export declare const useNavigationManager: () => {
3
2
  navigationManagerModal: JSX.Element | null;
4
3
  openNavigationManagerModal: () => void;
@@ -50,7 +50,7 @@ exports.default = {
50
50
  const component = await Promise.resolve().then(() => __importStar(require('./pages/SettingsPage')));
51
51
  return component;
52
52
  },
53
- permissions: permissions_1.default.access,
53
+ permissions: permissions_1.default.settings,
54
54
  }
55
55
  ]);
56
56
  app.addMenuLink({
@@ -287,6 +287,7 @@ const DataManagerProvider = ({ children }) => {
287
287
  method: "DELETE",
288
288
  signal,
289
289
  });
290
+ const slugify = (query) => (0, helper_plugin_1.request)(`/${pluginId_1.default}/slug?q=${query}`, { method: "GET", signal });
290
291
  const hardReset = () => getDataRef.current();
291
292
  return (react_1.default.createElement(DataManagerContext_1.default.Provider, { value: {
292
293
  items: passedActiveItems,
@@ -316,6 +317,7 @@ const DataManagerProvider = ({ children }) => {
316
317
  readNavigationItemFromLocale,
317
318
  handleNavigationsDeletion,
318
319
  hardReset,
320
+ slugify,
319
321
  } }, isLoading ? react_1.default.createElement(helper_plugin_1.LoadingIndicatorPage, null) : children));
320
322
  };
321
323
  DataManagerProvider.propTypes = {
@@ -1,5 +1,5 @@
1
1
  export default reducer;
2
- declare function reducer(state: any, action: any): any;
2
+ declare function reducer(state: any, action: any): (base?: ((draftState: any) => any) | undefined, ...args: unknown[]) => ((draftState: any) => any) | Promise<(draftState: any) => any>;
3
3
  export namespace initialState {
4
4
  const items: never[];
5
5
  const activeItem: undefined;
@@ -0,0 +1,3 @@
1
+ declare const NoAcccessPage: () => JSX.Element;
2
+ export default NoAcccessPage;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const react_1 = __importDefault(require("react"));
7
+ const helper_plugin_1 = require("@strapi/helper-plugin");
8
+ const Main_1 = require("@strapi/design-system/Main");
9
+ const Layout_1 = require("@strapi/design-system/Layout");
10
+ const EmptyStateLayout_1 = require("@strapi/design-system/EmptyStateLayout");
11
+ const icons_1 = require("@strapi/icons");
12
+ const react_intl_1 = require("react-intl");
13
+ const NoAcccessPage = () => {
14
+ const { formatMessage } = (0, react_intl_1.useIntl)();
15
+ (0, helper_plugin_1.useFocusWhenNavigate)();
16
+ return (react_1.default.createElement(Main_1.Main, { labelledBy: "title" },
17
+ react_1.default.createElement(Layout_1.HeaderLayout, { id: "title", title: formatMessage({
18
+ id: 'page.auth.noAccess',
19
+ defaultMessage: 'No access',
20
+ }) }),
21
+ react_1.default.createElement(Layout_1.ContentLayout, null,
22
+ react_1.default.createElement(EmptyStateLayout_1.EmptyStateLayout, { action: react_1.default.createElement(helper_plugin_1.LinkButton, { variant: "secondary", endIcon: react_1.default.createElement(icons_1.ArrowRight, null), to: "/" }, formatMessage({
23
+ id: 'components.notAccessPage.back',
24
+ defaultMessage: 'Back to homepage',
25
+ })), content: formatMessage({
26
+ id: 'page.auth.not.allowed',
27
+ defaultMessage: "Oops! It seems like You do not have access to this page...",
28
+ }), hasRadius: true, icon: react_1.default.createElement(icons_1.EmptyPictures, { width: "10rem" }), shadow: "tableShadow" }))));
29
+ };
30
+ exports.default = NoAcccessPage;
31
+ //# sourceMappingURL=index.js.map
@@ -1,4 +1,3 @@
1
- /// <reference types="react" />
2
1
  declare const SettingsPage: () => JSX.Element;
3
2
  export default SettingsPage;
4
3
  //# sourceMappingURL=index.d.ts.map
@@ -55,6 +55,8 @@ const styles_1 = require("../../components/Alert/styles");
55
55
  const DisableI18nModal_1 = require("./components/DisableI18nModal");
56
56
  const CustomFieldModal_1 = __importDefault(require("./components/CustomFieldModal"));
57
57
  const CustomFieldTable_1 = __importDefault(require("./components/CustomFieldTable"));
58
+ const permissions_2 = __importDefault(require("../../permissions"));
59
+ const NoAccessPage_1 = __importDefault(require("../NoAccessPage"));
58
60
  const RESTART_NOT_REQUIRED = { required: false };
59
61
  const RESTART_REQUIRED = { required: true, reasons: [] };
60
62
  const RELATION_ATTRIBUTE_TYPES = ['relation', 'media', 'component'];
@@ -77,6 +79,10 @@ const SettingsPage = () => {
77
79
  const [contentTypeExpanded, setContentTypeExpanded] = (0, react_1.useState)(undefined);
78
80
  const { data: navigationConfigData, isLoading: isConfigLoading, error: configErr, submitMutation, restoreMutation, restartMutation } = (0, useNavigationConfig_1.default)();
79
81
  const { data: allContentTypesData, isLoading: isContentTypesLoading, error: contentTypesErr } = (0, useAllContentTypes_1.default)();
82
+ const viewPermissions = (0, react_1.useMemo)(() => ({
83
+ settings: permissions_2.default.settings
84
+ }), []);
85
+ const { isLoading: isLoadingForPermissions, allowedActions: { canManageSettings, }, } = (0, helper_plugin_1.useRBAC)(viewPermissions);
80
86
  const isLoading = isConfigLoading || isContentTypesLoading;
81
87
  const isError = configErr || contentTypesErr;
82
88
  const configContentTypes = navigationConfigData?.contentTypes || [];
@@ -198,6 +204,9 @@ const SettingsPage = () => {
198
204
  const filteredFields = customFields.filter(f => f.name !== field.name);
199
205
  setCustomFields([...filteredFields, updatedField]);
200
206
  };
207
+ if (!(isLoadingForPermissions || canManageSettings)) {
208
+ return (react_1.default.createElement(NoAccessPage_1.default, null));
209
+ }
201
210
  return (react_1.default.createElement(react_1.default.Fragment, null,
202
211
  react_1.default.createElement(helper_plugin_1.SettingsPageTitle, { name: (0, utils_2.getMessage)('Settings.email.plugin.title', 'Configuration') }),
203
212
  react_1.default.createElement(Main_1.Main, { labelledBy: "title" },
@@ -19,7 +19,7 @@ export declare type StrapiContentTypeSchema = StrapiContentTypeFullSchema & {
19
19
  export declare type PreparePayload = (payload: {
20
20
  form: RawPayload;
21
21
  pruneObsoleteI18nNavigations: boolean;
22
- }) => Omit<NavigationPluginConfig, "slugify">;
22
+ }) => NavigationPluginConfig;
23
23
  export declare type OnSave = Effect<RawPayload>;
24
24
  export declare type OnPopupClose = Effect<boolean>;
25
25
  export declare type HandleSetContentTypeExpanded = Effect<string | undefined>;
@@ -1,5 +1,5 @@
1
1
  export default NavigationHeader;
2
- declare function NavigationHeader({ activeNavigation, availableNavigations, structureHasErrors, structureHasChanged, handleChangeSelection, handleLocalizationSelection, handleSave, config, }: {
2
+ declare function NavigationHeader({ activeNavigation, availableNavigations, structureHasErrors, structureHasChanged, handleChangeSelection, handleLocalizationSelection, handleSave, config, permissions, }: {
3
3
  activeNavigation: any;
4
4
  availableNavigations: any;
5
5
  structureHasErrors: any;
@@ -8,5 +8,6 @@ declare function NavigationHeader({ activeNavigation, availableNavigations, stru
8
8
  handleLocalizationSelection: any;
9
9
  handleSave: any;
10
10
  config: any;
11
+ permissions?: {} | undefined;
11
12
  }): JSX.Element;
12
13
  //# sourceMappingURL=index.d.ts.map
@@ -46,7 +46,7 @@ const pickDefaultLocaleNavigation = ({ activeNavigation, config }) => config.i18
46
46
  : activeNavigation?.localizations.find(({ localeCode }) => localeCode === config.defaultLocale)
47
47
  : null
48
48
  : activeNavigation;
49
- const NavigationHeader = ({ activeNavigation, availableNavigations, structureHasErrors, structureHasChanged, handleChangeSelection, handleLocalizationSelection, handleSave, config, }) => {
49
+ const NavigationHeader = ({ activeNavigation, availableNavigations, structureHasErrors, structureHasChanged, handleChangeSelection, handleLocalizationSelection, handleSave, config, permissions = {}, }) => {
50
50
  const { formatMessage } = (0, react_intl_1.useIntl)();
51
51
  const allLocaleVersions = (0, react_1.useMemo)(() => activeNavigation?.localizations.length && config.i18nEnabled
52
52
  ? (0, lodash_1.uniqBy)([activeNavigation, ...(activeNavigation.localizations ?? [])].sort((a, b) => a.localeCode.localeCompare(b.localeCode)), 'id')
@@ -54,21 +54,22 @@ const NavigationHeader = ({ activeNavigation, availableNavigations, structureHas
54
54
  const hasLocalizations = config.i18nEnabled && allLocaleVersions.length;
55
55
  const passedActiveNavigation = pickDefaultLocaleNavigation({ activeNavigation, config });
56
56
  const { closeNavigationManagerModal, openNavigationManagerModal, navigationManagerModal } = (0, useNavigationManager_1.useNavigationManager)();
57
+ const { canUpdate } = permissions;
57
58
  return (react_1.default.createElement(Layout_1.HeaderLayout, { primaryAction: react_1.default.createElement(Stack_1.Stack, { horizontal: true, size: 2 },
58
- react_1.default.createElement(Box_1.Box, { width: "27vw", marginRight: "8px" },
59
+ react_1.default.createElement(Box_1.Box, { marginRight: "8px" },
59
60
  react_1.default.createElement(Grid_1.Grid, { gap: 4 },
60
61
  !hasLocalizations ? (react_1.default.createElement(Grid_1.GridItem, { col: 2 })) : null,
61
- react_1.default.createElement(Grid_1.GridItem, { col: 3 },
62
- react_1.default.createElement(Button_1.Button, { onClick: openNavigationManagerModal, startIcon: null, type: "button", variant: "secondary", fullWidth: true, size: "S" }, formatMessage((0, translations_1.getTrad)('header.action.manage')))),
63
- react_1.default.createElement(Grid_1.GridItem, { col: 4 },
62
+ canUpdate && (react_1.default.createElement(Grid_1.GridItem, { col: 3 },
63
+ react_1.default.createElement(Button_1.Button, { onClick: openNavigationManagerModal, startIcon: null, type: "button", variant: "secondary", fullWidth: true, size: "S" }, formatMessage((0, translations_1.getTrad)('header.action.manage'))))),
64
+ react_1.default.createElement(Grid_1.GridItem, { col: canUpdate ? 4 : 10 },
64
65
  react_1.default.createElement(Select_1.Select, { type: "select", placeholder: "Change navigation", name: "navigationSelect", onChange: handleChangeSelection, value: passedActiveNavigation?.id, size: "S", style: null }, availableNavigations.map(({ id, name }) => react_1.default.createElement(Select_1.Option, { key: id, value: id }, name)))),
65
66
  hasLocalizations
66
67
  ? react_1.default.createElement(Grid_1.GridItem, { col: 2 },
67
68
  react_1.default.createElement(Select_1.Select, { type: "select", placeholder: formatMessage((0, translations_1.getTrad)('pages.main.header.localization.select.placeholder')), name: "navigationLocalizationSelect", onChange: handleLocalizationSelection, value: activeNavigation?.id, size: "S" }, allLocaleVersions.map(({ id, localeCode }) => react_1.default.createElement(Select_1.Option, { key: id, value: id }, localeCode))))
68
69
  : null,
69
- react_1.default.createElement(Grid_1.GridItem, { col: 3 },
70
- react_1.default.createElement(Button_1.Button, { onClick: handleSave, startIcon: submitIcon, disabled: structureHasErrors || !structureHasChanged, type: "submit", fullWidth: true, size: "S" }, formatMessage((0, translations_1.getTrad)('submit.cta.save')))))),
71
- navigationManagerModal), title: formatMessage({
70
+ canUpdate && (react_1.default.createElement(Grid_1.GridItem, { col: 3 },
71
+ react_1.default.createElement(Button_1.Button, { onClick: handleSave, startIcon: submitIcon, disabled: structureHasErrors || !structureHasChanged, type: "submit", fullWidth: true, size: "S" }, formatMessage((0, translations_1.getTrad)('submit.cta.save'))))))),
72
+ canUpdate && navigationManagerModal), title: formatMessage({
72
73
  id: (0, translations_1.getTrad)('header.title'),
73
74
  defaultMessage: 'UI Navigation',
74
75
  }), subtitle: formatMessage({
@@ -28,7 +28,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  const react_1 = __importStar(require("react"));
30
30
  const lodash_1 = require("lodash");
31
- const slugify_1 = __importDefault(require("@sindresorhus/slugify"));
31
+ const fp_1 = require("lodash/fp");
32
32
  const formik_1 = require("formik");
33
33
  const ModalLayout_1 = require("@strapi/design-system/ModalLayout");
34
34
  const Select_1 = require("@strapi/design-system/Select");
@@ -44,17 +44,17 @@ const translations_1 = require("../../../../translations");
44
44
  const types_1 = require("../../../../../../types");
45
45
  const AdditionalFieldInput_1 = __importDefault(require("../../../../components/AdditionalFieldInput"));
46
46
  const utils_2 = require("../../../../utils");
47
- const appendLabelPublicationStatusFallback = () => '';
48
- const NavigationItemForm = ({ config, availableLocale, isLoading: isPreloading, inputsPrefix, data, contentTypes = [], contentTypeEntities = [], usedContentTypeEntities = [], availableAudience = [], additionalFields = [], contentTypesNameFields = {}, onSubmit, onCancel, getContentTypeEntities, usedContentTypesData, appendLabelPublicationStatus = appendLabelPublicationStatusFallback, locale, readNavigationItemFromLocale, }) => {
47
+ const NavigationItemForm = ({ config, availableLocale, isLoading: isPreloading, inputsPrefix, data, contentTypes = [], contentTypeEntities = [], usedContentTypeEntities = [], availableAudience = [], additionalFields = [], contentTypesNameFields = {}, onSubmit, onCancel, getContentTypeEntities, usedContentTypesData, appendLabelPublicationStatus = appendLabelPublicationStatusFallback, locale, readNavigationItemFromLocale, slugify, permissions = {}, }) => {
49
48
  const [isLoading, setIsLoading] = (0, react_1.useState)(isPreloading);
50
49
  const [hasBeenInitialized, setInitializedState] = (0, react_1.useState)(false);
51
50
  const [hasChanged, setChangedState] = (0, react_1.useState)(false);
52
51
  const [contentTypeSearchQuery, setContentTypeSearchQuery] = (0, react_1.useState)(undefined);
53
52
  const [contentTypeSearchInputValue, setContentTypeSearchInputValue] = (0, react_1.useState)(undefined);
53
+ const { canUpdate } = permissions;
54
54
  const formik = (0, formik_1.useFormik)({
55
55
  initialValues: formDefinition.defaultValues,
56
- onSubmit: (payload) => onSubmit(sanitizePayload(payload, data)),
57
- validate: (values) => (0, form_1.checkFormValidity)(sanitizePayload(values, {}), formDefinition.schemaFactory(isSingleSelected, additionalFields)),
56
+ onSubmit: loadingAware(async (payload) => onSubmit(await sanitizePayload(slugify, payload, data)), setIsLoading),
57
+ validate: loadingAware(async (values) => (0, form_1.checkFormValidity)(await sanitizePayload(slugify, values, {}), formDefinition.schemaFactory(isSingleSelected, additionalFields)), setIsLoading),
58
58
  validateOnChange: false,
59
59
  });
60
60
  const initialRelatedTypeSelected = (0, lodash_1.get)(data, 'relatedType.value');
@@ -120,7 +120,7 @@ const NavigationItemForm = ({ config, availableLocale, isLoading: isPreloading,
120
120
  }, contentTypesNameFields, { contentTypes });
121
121
  }
122
122
  }, [contentTypeEntities, contentTypesNameFields, contentTypes]);
123
- const sanitizePayload = (payload, data) => {
123
+ const sanitizePayload = async (slugify, payload, data) => {
124
124
  const { related, relatedType, menuAttached, type, ...purePayload } = payload;
125
125
  const relatedId = related;
126
126
  const singleRelatedItem = isSingleSelected ? (0, lodash_1.first)(contentTypeEntities) : undefined;
@@ -128,6 +128,7 @@ const NavigationItemForm = ({ config, availableLocale, isLoading: isPreloading,
128
128
  const title = !!payload.title?.trim()
129
129
  ? payload.title
130
130
  : getDefaultTitle(related, relatedType, isSingleSelected);
131
+ const uiRouterKey = await generateUiRouterKey(slugify, title, relatedId, relatedCollectionType);
131
132
  return {
132
133
  ...data,
133
134
  ...purePayload,
@@ -140,7 +141,7 @@ const NavigationItemForm = ({ config, availableLocale, isLoading: isPreloading,
140
141
  relatedType: type === utils_1.navigationItemType.INTERNAL ? relatedCollectionType : undefined,
141
142
  isSingle: isSingleSelected,
142
143
  singleRelatedItem,
143
- uiRouterKey: generateUiRouterKey(title, relatedId, relatedCollectionType),
144
+ uiRouterKey,
144
145
  };
145
146
  };
146
147
  const onChange = ({ name, value }) => {
@@ -183,17 +184,16 @@ const NavigationItemForm = ({ config, availableLocale, isLoading: isPreloading,
183
184
  value,
184
185
  });
185
186
  };
186
- const generateUiRouterKey = (title, related, relatedType) => {
187
- const { slugify: customSlugifyConfig } = config;
187
+ const generateUiRouterKey = async (slugify, title, related, relatedType) => {
188
188
  if (title) {
189
- return (0, lodash_1.isString)(title) && !(0, lodash_1.isEmpty)(title) ? (0, slugify_1.default)(title, customSlugifyConfig).toLowerCase() : undefined;
189
+ return (0, lodash_1.isString)(title) && !(0, lodash_1.isEmpty)(title) ? await slugify(title).then((0, fp_1.prop)("slug")) : undefined;
190
190
  }
191
191
  else if (related) {
192
192
  const relationTitle = (0, parsers_1.extractRelatedItemLabel)({
193
193
  ...contentTypeEntities.find(_ => _.id === related),
194
194
  __collectionUid: relatedType
195
195
  }, contentTypesNameFields, { contentTypes });
196
- return (0, lodash_1.isString)(relationTitle) && !(0, lodash_1.isEmpty)(relationTitle) ? (0, slugify_1.default)(relationTitle, customSlugifyConfig).toLowerCase() : undefined;
196
+ return (0, lodash_1.isString)(relationTitle) && !(0, lodash_1.isEmpty)(relationTitle) ? await slugify(relationTitle).then((0, fp_1.prop)("slug")) : undefined;
197
197
  }
198
198
  return undefined;
199
199
  };
@@ -236,7 +236,7 @@ const NavigationItemForm = ({ config, availableLocale, isLoading: isPreloading,
236
236
  }), item => item.metadatas.intlLabel.id);
237
237
  const isExternal = formik.values.type === utils_1.navigationItemType.EXTERNAL;
238
238
  const pathSourceName = isExternal ? 'externalPath' : 'path';
239
- const submitDisabled = (formik.values.type === utils_1.navigationItemType.INTERNAL && !isSingleSelected && (0, lodash_1.isNil)(formik.values.related));
239
+ const submitDisabled = (formik.values.type === utils_1.navigationItemType.INTERNAL && !isSingleSelected && (0, lodash_1.isNil)(formik.values.related)) || isLoading;
240
240
  const debouncedSearch = (0, react_1.useCallback)((0, lodash_1.debounce)(nextValue => setContentTypeSearchQuery(nextValue), 500), []);
241
241
  const debounceContentTypeSearchQuery = (value) => {
242
242
  setContentTypeSearchInputValue(value);
@@ -381,20 +381,20 @@ const NavigationItemForm = ({ config, availableLocale, isLoading: isPreloading,
381
381
  react_1.default.createElement(ModalLayout_1.ModalBody, null,
382
382
  react_1.default.createElement(Grid_1.Grid, { gap: 5 },
383
383
  react_1.default.createElement(Grid_1.GridItem, { key: "title", col: 12 },
384
- react_1.default.createElement(helper_plugin_1.GenericInput, { autoFocused: true, intlLabel: (0, translations_1.getTrad)('popup.item.form.title.label', 'Title'), name: "title", placeholder: (0, translations_1.getTrad)("e.g. Blog", 'e.g. Blog'), description: (0, translations_1.getTrad)('popup.item.form.title.placeholder', 'e.g. Blog'), type: "text", error: formik.errors.title, onChange: ({ target: { name, value } }) => onChange({ name, value }), value: formik.values.title })),
384
+ react_1.default.createElement(helper_plugin_1.GenericInput, { autoFocused: true, intlLabel: (0, translations_1.getTrad)('popup.item.form.title.label', 'Title'), name: "title", placeholder: (0, translations_1.getTrad)("e.g. Blog", 'e.g. Blog'), description: (0, translations_1.getTrad)('popup.item.form.title.placeholder', 'e.g. Blog'), type: "text", disabled: !canUpdate, error: formik.errors.title, onChange: ({ target: { name, value } }) => onChange({ name, value }), value: formik.values.title })),
385
385
  react_1.default.createElement(Grid_1.GridItem, { key: "type", col: 4, lg: 12 },
386
- react_1.default.createElement(helper_plugin_1.GenericInput, { intlLabel: (0, translations_1.getTrad)('popup.item.form.type.label', 'Internal link'), name: "type", options: navigationItemTypeOptions, type: "select", error: formik.errors.type, onChange: ({ target: { name, value } }) => onChange({ name, value }), value: formik.values.type })),
386
+ react_1.default.createElement(helper_plugin_1.GenericInput, { intlLabel: (0, translations_1.getTrad)('popup.item.form.type.label', 'Internal link'), name: "type", options: navigationItemTypeOptions, type: "select", disabled: !canUpdate, error: formik.errors.type, onChange: ({ target: { name, value } }) => onChange({ name, value }), value: formik.values.type })),
387
387
  react_1.default.createElement(Grid_1.GridItem, { key: "menuAttached", col: 4, lg: 12 },
388
- react_1.default.createElement(helper_plugin_1.GenericInput, { intlLabel: (0, translations_1.getTrad)('popup.item.form.menuAttached.label', 'MenuAttached'), name: "menuAttached", type: "bool", error: formik.errors.menuAttached, onChange: ({ target: { name, value } }) => onChange({ name, value }), value: formik.values.menuAttached, disabled: config.cascadeMenuAttached ? !(data.isMenuAllowedLevel && data.parentAttachedToMenu) : false })),
388
+ react_1.default.createElement(helper_plugin_1.GenericInput, { intlLabel: (0, translations_1.getTrad)('popup.item.form.menuAttached.label', 'MenuAttached'), name: "menuAttached", type: "bool", error: formik.errors.menuAttached, onChange: ({ target: { name, value } }) => onChange({ name, value }), value: formik.values.menuAttached, disabled: !canUpdate || (config.cascadeMenuAttached ? !(data.isMenuAllowedLevel && data.parentAttachedToMenu) : false) })),
389
389
  react_1.default.createElement(Grid_1.GridItem, { key: "path", col: 12 },
390
- react_1.default.createElement(helper_plugin_1.GenericInput, { intlLabel: (0, translations_1.getTrad)(`popup.item.form.${pathSourceName}.label`, 'Path'), name: pathSourceName, placeholder: (0, translations_1.getTrad)(`popup.item.form.${pathSourceName}.placeholder`, 'e.g. Blog'), type: "text", error: formik.errors[pathSourceName], onChange: ({ target: { name, value } }) => onChange({ name, value }), value: formik.values[pathSourceName], description: generatePreviewPath() })),
390
+ react_1.default.createElement(helper_plugin_1.GenericInput, { intlLabel: (0, translations_1.getTrad)(`popup.item.form.${pathSourceName}.label`, 'Path'), name: pathSourceName, placeholder: (0, translations_1.getTrad)(`popup.item.form.${pathSourceName}.placeholder`, 'e.g. Blog'), type: "text", disabled: !canUpdate, error: formik.errors[pathSourceName], onChange: ({ target: { name, value } }) => onChange({ name, value }), value: formik.values[pathSourceName], description: generatePreviewPath() })),
391
391
  formik.values.type === utils_1.navigationItemType.INTERNAL && (react_1.default.createElement(react_1.default.Fragment, null,
392
392
  react_1.default.createElement(Grid_1.GridItem, { col: 6, lg: 12 },
393
- react_1.default.createElement(helper_plugin_1.GenericInput, { type: "select", intlLabel: (0, translations_1.getTrad)('popup.item.form.relatedType.label', 'Related Type'), placeholder: (0, translations_1.getTrad)('popup.item.form.relatedType.placeholder', 'Related Type'), name: "relatedType", error: formik.errors.relatedType, onChange: onChangeRelatedType, options: relatedTypeSelectOptions, value: formik.values.relatedType, disabled: isLoading || (0, lodash_1.isEmpty)(relatedTypeSelectOptions), description: !isLoading && (0, lodash_1.isEmpty)(relatedTypeSelectOptions)
393
+ react_1.default.createElement(helper_plugin_1.GenericInput, { type: "select", intlLabel: (0, translations_1.getTrad)('popup.item.form.relatedType.label', 'Related Type'), placeholder: (0, translations_1.getTrad)('popup.item.form.relatedType.placeholder', 'Related Type'), name: "relatedType", error: formik.errors.relatedType, onChange: onChangeRelatedType, options: relatedTypeSelectOptions, value: formik.values.relatedType, disabled: isLoading || (0, lodash_1.isEmpty)(relatedTypeSelectOptions) || !canUpdate, description: !isLoading && (0, lodash_1.isEmpty)(relatedTypeSelectOptions)
394
394
  ? (0, translations_1.getTrad)('popup.item.form.relatedType.empty', 'There are no more content types')
395
395
  : undefined })),
396
396
  formik.values.relatedType && !isSingleSelected && (react_1.default.createElement(Grid_1.GridItem, { col: 6, lg: 12 },
397
- react_1.default.createElement(helper_plugin_1.GenericInput, { type: "select", intlLabel: (0, translations_1.getTrad)('popup.item.form.related.label', 'Related'), placeholder: (0, translations_1.getTrad)('popup.item.form.related.label', 'Related'), name: "related", error: formik.errors.related, onChange: ({ target: { name, value } }) => onChange({ name, value }), onInputChange: debounceContentTypeSearchQuery, inputValue: contentTypeSearchInputValue, options: relatedSelectOptions, value: formik.values.related, disabled: isLoading || thereAreNoMoreContentTypes, description: !isLoading && thereAreNoMoreContentTypes
397
+ react_1.default.createElement(helper_plugin_1.GenericInput, { type: "select", intlLabel: (0, translations_1.getTrad)('popup.item.form.related.label', 'Related'), placeholder: (0, translations_1.getTrad)('popup.item.form.related.label', 'Related'), name: "related", error: formik.errors.related, onChange: ({ target: { name, value } }) => onChange({ name, value }), onInputChange: debounceContentTypeSearchQuery, inputValue: contentTypeSearchInputValue, options: relatedSelectOptions, value: formik.values.related, disabled: isLoading || thereAreNoMoreContentTypes || !canUpdate, description: !isLoading && thereAreNoMoreContentTypes
398
398
  ? {
399
399
  id: (0, translations_1.getTradId)('popup.item.form.related.empty'),
400
400
  defaultMessage: 'There are no more entities',
@@ -406,19 +406,31 @@ const NavigationItemForm = ({ config, availableLocale, isLoading: isPreloading,
406
406
  return (react_1.default.createElement(Grid_1.GridItem, { key: "audience", col: 6, lg: 12 },
407
407
  react_1.default.createElement(Select_1.Select, { id: "audience", placeholder: (0, utils_2.getMessage)('popup.item.form.audience.placeholder'), label: (0, utils_2.getMessage)('popup.item.form.audience.label'), onChange: onAudienceChange, value: formik.values.audience, hint: !isLoading && (0, lodash_1.isEmpty)(audienceOptions)
408
408
  ? (0, utils_2.getMessage)('popup.item.form.audience.empty', 'There are no more audiences')
409
- : undefined, multi: true, withTags: true, disabled: (0, lodash_1.isEmpty)(audienceOptions) }, audienceOptions.map(({ value, label }) => react_1.default.createElement(Select_1.Option, { key: value, value: value }, label)))));
409
+ : undefined, multi: true, withTags: true, disabled: (0, lodash_1.isEmpty)(audienceOptions) || !canUpdate }, audienceOptions.map(({ value, label }) => react_1.default.createElement(Select_1.Option, { key: value, value: value }, label)))));
410
410
  }
411
411
  else {
412
412
  return (react_1.default.createElement(Grid_1.GridItem, { key: additionalField.name, col: 6, lg: 12 },
413
- react_1.default.createElement(AdditionalFieldInput_1.default, { field: additionalField, isLoading: isLoading, onChange: onAdditionalFieldChange, value: (0, lodash_1.get)(formik.values, `additionalFields.${additionalField.name}`, null), error: (0, lodash_1.get)(formik.errors, `additionalFields.${additionalField.name}`, null) })));
413
+ react_1.default.createElement(AdditionalFieldInput_1.default, { field: additionalField, isLoading: isLoading, onChange: onAdditionalFieldChange, value: (0, lodash_1.get)(formik.values, `additionalFields.${additionalField.name}`, null), disabled: !canUpdate, error: (0, lodash_1.get)(formik.errors, `additionalFields.${additionalField.name}`, null) })));
414
414
  }
415
415
  })),
416
416
  isI18nBootstrapAvailable ? (react_1.default.createElement(Grid_1.Grid, { gap: 5, paddingTop: 5 },
417
417
  react_1.default.createElement(Grid_1.GridItem, { col: 6, lg: 12 },
418
- react_1.default.createElement(helper_plugin_1.GenericInput, { ...itemCopyProps, type: "select", name: itemLocaleCopyField, error: (0, lodash_1.get)(formik.errors, itemLocaleCopyField), onChange: onChangeLocaleCopy, options: availableLocaleOptions, value: itemLocaleCopyValue, disabled: isLoading })),
419
- react_1.default.createElement(Grid_1.GridItem, { col: 6, lg: 12, paddingTop: 6 },
420
- react_1.default.createElement(Button_1.Button, { variant: "tertiary", onClick: onCopyFromLocale, disabled: isLoading || !itemLocaleCopyValue }, (0, utils_2.getMessage)('popup.item.form.i18n.locale.button'))))) : null)),
421
- react_1.default.createElement(NavigationItemPopupFooter_1.NavigationItemPopupFooter, { handleSubmit: formik.handleSubmit, handleCancel: onCancel, submitDisabled: submitDisabled })));
418
+ react_1.default.createElement(helper_plugin_1.GenericInput, { ...itemCopyProps, type: "select", name: itemLocaleCopyField, error: (0, lodash_1.get)(formik.errors, itemLocaleCopyField), onChange: onChangeLocaleCopy, options: availableLocaleOptions, value: itemLocaleCopyValue, disabled: isLoading || !canUpdate })),
419
+ canUpdate && (react_1.default.createElement(Grid_1.GridItem, { col: 6, lg: 12, paddingTop: 6 },
420
+ react_1.default.createElement(Button_1.Button, { variant: "tertiary", onClick: onCopyFromLocale, disabled: isLoading || !itemLocaleCopyValue }, (0, utils_2.getMessage)('popup.item.form.i18n.locale.button')))))) : null)),
421
+ react_1.default.createElement(NavigationItemPopupFooter_1.NavigationItemPopupFooter, { handleSubmit: formik.handleSubmit, handleCancel: onCancel, submitDisabled: submitDisabled, canUpdate: canUpdate })));
422
+ };
423
+ const appendLabelPublicationStatusFallback = () => "";
424
+ const loadingAware = (action, isLoading) => async (input) => {
425
+ try {
426
+ isLoading(true);
427
+ return await action(input);
428
+ }
429
+ catch (_) {
430
+ }
431
+ finally {
432
+ isLoading(false);
433
+ }
422
434
  };
423
435
  exports.default = NavigationItemForm;
424
436
  //# sourceMappingURL=index.js.map