strapi-plugin-navigation 2.0.5 → 2.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/admin/src/components/CollapseButton/index.js +31 -0
- package/admin/src/components/ConfirmationDialog/index.js +1 -1
- package/admin/src/components/Item/ItemCardBadge/index.js +4 -4
- package/admin/src/components/Item/ItemCardHeader/Wrapper.js +0 -4
- package/admin/src/components/Item/ItemCardHeader/index.js +28 -6
- package/admin/src/components/Item/Wrapper.js +1 -1
- package/admin/src/components/Item/index.js +59 -44
- package/admin/src/components/NavigationItemList/Wrapper.js +1 -1
- package/admin/src/components/NavigationItemList/index.js +3 -0
- package/admin/src/pages/SettingsPage/index.js +42 -22
- package/admin/src/pages/View/components/NavigationHeader/index.js +1 -1
- package/admin/src/pages/View/components/NavigationItemForm/index.js +47 -30
- package/admin/src/pages/View/components/NavigationItemPopup/NavigationItemPopupHeader.js +3 -3
- package/admin/src/pages/View/index.js +73 -9
- package/admin/src/pages/View/utils/enums.js +1 -0
- package/admin/src/pages/View/utils/parsers.js +6 -3
- package/admin/src/translations/en.json +12 -3
- package/package.json +2 -3
- package/server/bootstrap.js +3 -22
- package/server/config/index.js +1 -0
- package/server/config.js +1 -0
- package/server/content-types/navigation-item/lifecycle.js +1 -0
- package/server/content-types/navigation-item/schema.json +7 -1
- package/server/graphql/types/content-types-name-fields.js +2 -2
- package/server/services/navigation.js +44 -19
- package/server/services/utils/functions.js +3 -2
- package/yarn-error.log +0 -5263
package/README.md
CHANGED
|
@@ -41,6 +41,7 @@ Strapi Navigation Plugin provides a website navigation / menu builder feature fo
|
|
|
41
41
|
- **Navigation Public API:** Simple and ready for use API endpoint for consuming the navigation structure you've created
|
|
42
42
|
- **Visual builder:** Elegant and easy to use visual builder
|
|
43
43
|
- **Any Content Type relation:** Navigation can by linked to any of your Content Types by default. Simply, you're controlling it and also limiting available content types by configuration props
|
|
44
|
+
- **Different types of navigation items:** Create navigation with items linked to internal types, to external links or wrapper elements to keep structure clean
|
|
44
45
|
- **Multiple navigations:** Create as many Navigation containers as you want, setup them and use in the consumer application
|
|
45
46
|
- **Customizable:** Possibility to customize the options like: available Content Types, Maximum level for "attach to menu", Additional fields (audience)
|
|
46
47
|
- **[Audit log](https://github.com/VirtusLab/strapi-molecules/tree/master/packages/strapi-plugin-audit-log):** integration with Strapi Molecules Audit Log plugin that provides changes track record
|
|
@@ -81,7 +82,7 @@ Complete installation requirements are exact same as for Strapi itself and can b
|
|
|
81
82
|
|
|
82
83
|
**Supported Strapi versions**:
|
|
83
84
|
|
|
84
|
-
- Strapi v4.1.
|
|
85
|
+
- Strapi v4.1.5 (recently tested)
|
|
85
86
|
- Strapi v4.x
|
|
86
87
|
|
|
87
88
|
> 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).
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components'
|
|
3
|
+
import { Flex } from '@strapi/design-system/Flex';
|
|
4
|
+
import { Typography } from '@strapi/design-system/Typography';
|
|
5
|
+
import { Icon } from '@strapi/design-system/Icon';
|
|
6
|
+
import { CarretUp, CarretDown } from '@strapi/icons';
|
|
7
|
+
|
|
8
|
+
const Wrapper = styled.div`
|
|
9
|
+
border-radius: 50%;
|
|
10
|
+
background: #DCDCE4;
|
|
11
|
+
width: 25px;
|
|
12
|
+
height: 25px;
|
|
13
|
+
display: flex;
|
|
14
|
+
justify-content: center;
|
|
15
|
+
align-items: center;
|
|
16
|
+
margin-right: 8px;
|
|
17
|
+
`;
|
|
18
|
+
|
|
19
|
+
const CollapseButton = ({ toggle, collapsed, itemsCount }) => (
|
|
20
|
+
<Flex justifyContent='space-between' alignItems='center' onClick={toggle} cursor="pointer" style={{ marginRight: '16px' }}>
|
|
21
|
+
<Wrapper>
|
|
22
|
+
{ collapsed ?
|
|
23
|
+
<Icon as={CarretDown} width='7px' height='4px' /> :
|
|
24
|
+
<Icon as={CarretUp} width='7px' height='4px' />
|
|
25
|
+
}
|
|
26
|
+
</Wrapper>
|
|
27
|
+
<Typography variant="pi">{itemsCount} nested items</Typography>
|
|
28
|
+
</Flex >
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
export default CollapseButton;
|
|
@@ -27,7 +27,7 @@ const ConfirmationDialog = ({
|
|
|
27
27
|
}) => (
|
|
28
28
|
<Dialog onClose={onCancel} title={header || getMessage('components.confirmation.dialog.header', 'Confirmation')} isOpen={isVisible}>
|
|
29
29
|
<DialogBody icon={<ExclamationMarkCircle />}>
|
|
30
|
-
<Stack
|
|
30
|
+
<Stack spacing={2}>
|
|
31
31
|
<Flex justifyContent="center">
|
|
32
32
|
<Typography id="dialog-confirm-description">{children || getMessage('components.confirmation.dialog.description')}</Typography>
|
|
33
33
|
</Flex>
|
|
@@ -4,15 +4,15 @@ import { Badge } from '@strapi/design-system/Badge';
|
|
|
4
4
|
const ItemCardBadge = styled(Badge)`
|
|
5
5
|
border: 1px solid ${({ theme, borderColor }) => theme.colors[borderColor]};
|
|
6
6
|
|
|
7
|
-
${
|
|
8
|
-
padding: ${
|
|
9
|
-
margin: 0px ${
|
|
7
|
+
${ ({small, theme}) => small && `
|
|
8
|
+
padding: ${theme.spaces[1]} ${theme.spaces[2]};
|
|
9
|
+
margin: 0px ${theme.spaces[3]};
|
|
10
10
|
vertical-align: middle;
|
|
11
11
|
|
|
12
12
|
cursor: default;
|
|
13
13
|
|
|
14
14
|
span {
|
|
15
|
-
font-size: .
|
|
15
|
+
font-size: .65rem;
|
|
16
16
|
line-height: 1;
|
|
17
17
|
vertical-align: middle;
|
|
18
18
|
}
|
|
@@ -9,10 +9,6 @@ const CardItemTitle = styled(CardTitle)`
|
|
|
9
9
|
justify-content: space-between;
|
|
10
10
|
align-items: center;
|
|
11
11
|
|
|
12
|
-
color: ${({ theme }) => theme.colors.neutral800};
|
|
13
|
-
font-size: ${({ theme }) => theme.fontSizes[2]};
|
|
14
|
-
font-weight: ${({ theme }) => theme.fontWeights.bold};
|
|
15
|
-
|
|
16
12
|
> div > * {
|
|
17
13
|
margin: 0px ${({ theme }) => theme.spaces[1]};
|
|
18
14
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
2
3
|
|
|
3
4
|
import { Flex } from '@strapi/design-system/Flex';
|
|
4
5
|
import { IconButton } from '@strapi/design-system/IconButton';
|
|
@@ -10,20 +11,44 @@ import Wrapper from './Wrapper';
|
|
|
10
11
|
import ItemCardBadge from '../ItemCardBadge';
|
|
11
12
|
import { getMessage } from '../../../utils';
|
|
12
13
|
|
|
14
|
+
const IconWrapper = styled.div`
|
|
15
|
+
display: flex;
|
|
16
|
+
align-items: center;
|
|
17
|
+
justify-content: center;
|
|
18
|
+
height: ${32 / 16}rem;
|
|
19
|
+
width: ${32 / 16}rem;
|
|
20
|
+
|
|
21
|
+
cursor: pointer;
|
|
22
|
+
padding: ${({ theme }) => theme.spaces[2]};
|
|
23
|
+
border-radius: ${({ theme }) => theme.borderRadius};
|
|
24
|
+
background: ${({ theme }) => theme.colors.neutral0};
|
|
25
|
+
border: 1px solid ${({ theme }) => theme.colors.neutral200};
|
|
26
|
+
|
|
27
|
+
svg {
|
|
28
|
+
> g,
|
|
29
|
+
path {
|
|
30
|
+
fill: ${({ theme }) => theme.colors.neutral500};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
`
|
|
34
|
+
|
|
13
35
|
const ItemCardHeader = ({ title, path, icon, removed, onItemRemove, onItemEdit, onItemRestore, dragRef }) => {
|
|
14
36
|
return (
|
|
15
37
|
<Wrapper>
|
|
16
38
|
<Flex alignItems="center">
|
|
17
|
-
<
|
|
39
|
+
<IconWrapper ref={dragRef}>
|
|
40
|
+
<Icon as={Drag} />
|
|
41
|
+
</IconWrapper>
|
|
18
42
|
<Typography variant="omega" fontWeight="bold">
|
|
19
43
|
{title}
|
|
20
44
|
</Typography>
|
|
21
45
|
<Typography variant="omega" fontWeight="bold" textColor='neutral500'>
|
|
22
46
|
{path}
|
|
23
47
|
</Typography>
|
|
48
|
+
<Icon as={icon} />
|
|
24
49
|
</Flex>
|
|
25
50
|
<Flex alignItems="center" style={{ zIndex: 2 }}>
|
|
26
|
-
{removed &&
|
|
51
|
+
{removed &&
|
|
27
52
|
(<ItemCardBadge
|
|
28
53
|
borderColor={`danger200`}
|
|
29
54
|
backgroundColor={`danger100`}
|
|
@@ -36,10 +61,7 @@ const ItemCardHeader = ({ title, path, icon, removed, onItemRemove, onItemEdit,
|
|
|
36
61
|
<IconButton disabled={removed} onClick={onItemEdit} label="Edit" icon={<Pencil />} />
|
|
37
62
|
{removed ?
|
|
38
63
|
<IconButton onClick={onItemRestore} label="Restore" icon={<Refresh />} /> :
|
|
39
|
-
|
|
40
|
-
<IconButton ref={dragRef} label="Drag" icon={<Drag />} />
|
|
41
|
-
<IconButton onClick={onItemRemove} label="Remove" icon={<Trash />} />
|
|
42
|
-
</>
|
|
64
|
+
<IconButton onClick={onItemRemove} label="Remove" icon={<Trash />} />
|
|
43
65
|
}
|
|
44
66
|
</Flex>
|
|
45
67
|
</Wrapper>
|
|
@@ -3,7 +3,7 @@ import styled from "styled-components";
|
|
|
3
3
|
const Wrapper = styled.div`
|
|
4
4
|
position: relative;
|
|
5
5
|
margin-top: ${({theme}) => theme.spaces[2]};
|
|
6
|
-
margin-left: ${({
|
|
6
|
+
margin-left: ${({ level }) => level && '54px'}};
|
|
7
7
|
|
|
8
8
|
${({ level, theme, isLast }) => level && `
|
|
9
9
|
&::before {
|
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
import React, { useRef
|
|
1
|
+
import React, { useRef } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import { useDrag, useDrop } from 'react-dnd';
|
|
4
|
-
import {
|
|
5
|
-
import { drop, isEmpty, isNumber } from 'lodash';
|
|
4
|
+
import { isEmpty, isNumber } from 'lodash';
|
|
6
5
|
|
|
7
|
-
import { Box } from '@strapi/design-system/Box';
|
|
8
6
|
import { Card, CardBody } from '@strapi/design-system/Card';
|
|
9
7
|
import { Divider } from '@strapi/design-system/Divider';
|
|
10
8
|
import { Flex } from '@strapi/design-system/Flex';
|
|
11
9
|
import { Link } from '@strapi/design-system/Link';
|
|
12
10
|
import { TextButton } from '@strapi/design-system/TextButton';
|
|
13
11
|
import { Typography } from '@strapi/design-system/Typography';
|
|
14
|
-
import { ArrowRight, Link as LinkIcon, Earth, Plus } from '@strapi/icons';
|
|
12
|
+
import { ArrowRight, Link as LinkIcon, Earth, Plus, Cog } from '@strapi/icons';
|
|
15
13
|
|
|
16
14
|
import { navigationItemType } from '../../pages/View/utils/enums';
|
|
17
15
|
import ItemCardHeader from './ItemCardHeader';
|
|
@@ -21,6 +19,7 @@ import { extractRelatedItemLabel } from '../../pages/View/utils/parsers';
|
|
|
21
19
|
import ItemCardBadge from './ItemCardBadge';
|
|
22
20
|
import { ItemCardRemovedOverlay } from './ItemCardRemovedOverlay';
|
|
23
21
|
import { getMessage, ItemTypes } from '../../utils';
|
|
22
|
+
import CollapseButton from '../CollapseButton';
|
|
24
23
|
|
|
25
24
|
const Item = (props) => {
|
|
26
25
|
const {
|
|
@@ -36,6 +35,7 @@ const Item = (props) => {
|
|
|
36
35
|
onItemRestore,
|
|
37
36
|
onItemEdit,
|
|
38
37
|
onItemReOrder,
|
|
38
|
+
onItemToggleCollapse,
|
|
39
39
|
error,
|
|
40
40
|
displayChildren,
|
|
41
41
|
config = {},
|
|
@@ -49,10 +49,12 @@ const Item = (props) => {
|
|
|
49
49
|
removed,
|
|
50
50
|
externalPath,
|
|
51
51
|
menuAttached,
|
|
52
|
+
collapsed,
|
|
52
53
|
} = item;
|
|
53
54
|
|
|
54
55
|
const { contentTypes, contentTypesNameFields } = config;
|
|
55
56
|
const isExternal = type === navigationItemType.EXTERNAL;
|
|
57
|
+
const isWrapper = type === navigationItemType.WRAPPER;
|
|
56
58
|
const isPublished = relatedRef && relatedRef?.publishedAt;
|
|
57
59
|
const isNextMenuAllowedLevel = isNumber(allowedLevels) ? level < (allowedLevels - 1) : true;
|
|
58
60
|
const isMenuAllowedLevel = isNumber(allowedLevels) ? level < allowedLevels : true;
|
|
@@ -87,7 +89,16 @@ const Item = (props) => {
|
|
|
87
89
|
const isAfter = hoverClientY > hoverMiddleY;
|
|
88
90
|
const newOrder = isAfter ? item.order + 0.5 : item.order - 0.5;
|
|
89
91
|
|
|
92
|
+
if (dragIndex < dropIndex && hoverClientY < hoverMiddleY) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
// Dragging upwards
|
|
96
|
+
if (dragIndex > dropIndex && hoverClientY > hoverMiddleY) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
90
100
|
onItemReOrder({ ...hoveringItem }, newOrder);
|
|
101
|
+
hoveringItem.order = newOrder;
|
|
91
102
|
},
|
|
92
103
|
collect: monitor => ({
|
|
93
104
|
isOverCurrent: monitor.isOver({ shallow: true }),
|
|
@@ -97,7 +108,7 @@ const Item = (props) => {
|
|
|
97
108
|
const [{ isDragging }, drag, dragPreview] = useDrag({
|
|
98
109
|
type: `${ItemTypes.NAVIGATION_ITEM}_${levelPath}`,
|
|
99
110
|
item: () => {
|
|
100
|
-
return { ...item };
|
|
111
|
+
return { ...item, relatedRef };
|
|
101
112
|
},
|
|
102
113
|
collect: monitor => ({
|
|
103
114
|
isDragging: monitor.isDragging(),
|
|
@@ -119,7 +130,7 @@ const Item = (props) => {
|
|
|
119
130
|
<ItemCardHeader
|
|
120
131
|
title={title}
|
|
121
132
|
path={isExternal ? externalPath : absolutePath}
|
|
122
|
-
icon={isExternal ? Earth : LinkIcon}
|
|
133
|
+
icon={isExternal ? Earth : isWrapper ? Cog : LinkIcon}
|
|
123
134
|
onItemRemove={() => onItemRemove({
|
|
124
135
|
...item,
|
|
125
136
|
relatedRef,
|
|
@@ -128,6 +139,7 @@ const Item = (props) => {
|
|
|
128
139
|
...item,
|
|
129
140
|
isMenuAllowedLevel,
|
|
130
141
|
isParentAttachedToMenu,
|
|
142
|
+
relatedRef,
|
|
131
143
|
}, levelPath, isParentAttachedToMenu)}
|
|
132
144
|
onItemRestore={() => onItemRestore({
|
|
133
145
|
...item,
|
|
@@ -138,49 +150,50 @@ const Item = (props) => {
|
|
|
138
150
|
/>
|
|
139
151
|
</CardBody>
|
|
140
152
|
<Divider />
|
|
141
|
-
{!isExternal && (
|
|
142
|
-
<
|
|
143
|
-
<
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
153
|
+
{!isExternal && (
|
|
154
|
+
<CardBody style={{ padding: '8px' }}>
|
|
155
|
+
<Flex style={{ width: '100%' }} direction="row" alignItems="center" justifyContent="space-between">
|
|
156
|
+
<Flex>
|
|
157
|
+
{!isEmpty(item.items) && <CollapseButton toggle={() => onItemToggleCollapse({...item, relatedRef})} collapsed={collapsed} itemsCount={item.items.length}/>}
|
|
158
|
+
<TextButton
|
|
159
|
+
disabled={removed}
|
|
160
|
+
startIcon={<Plus />}
|
|
161
|
+
onClick={(e) => onItemLevelAdd(e, viewId, isNextMenuAllowedLevel, absolutePath, menuAttached)}
|
|
162
|
+
>
|
|
163
|
+
<Typography variant="pi" fontWeight="bold" textColor={removed ? "neutral600" : "primary600"}>
|
|
164
|
+
{getMessage("components.navigationItem.action.newItem")}
|
|
165
|
+
</Typography>
|
|
166
|
+
</TextButton>
|
|
167
|
+
</Flex>
|
|
168
|
+
{relatedItemLabel && (
|
|
169
|
+
<Flex justifyContent='center' alignItems='center'>
|
|
170
|
+
<ItemCardBadge
|
|
171
|
+
borderColor={`${relatedBadgeColor}200`}
|
|
172
|
+
backgroundColor={`${relatedBadgeColor}100`}
|
|
173
|
+
textColor={`${relatedBadgeColor}600`}
|
|
174
|
+
className="action"
|
|
175
|
+
small
|
|
176
|
+
>
|
|
177
|
+
{getMessage({id: `components.navigationItem.badge.${isPublished ? 'published' : 'draft'}`})}
|
|
178
|
+
</ItemCardBadge>
|
|
179
|
+
<Typography variant="omega" textColor='neutral600'>{relatedTypeLabel} / </Typography>
|
|
180
|
+
<Typography variant="omega" textColor='neutral800'>{relatedItemLabel}</Typography>
|
|
181
|
+
<Link
|
|
182
|
+
to={`/content-manager/collectionType/${relatedRef?.__collectionUid}/${relatedRef?.id}`}
|
|
183
|
+
endIcon={<ArrowRight />}> </Link>
|
|
184
|
+
</Flex>)
|
|
185
|
+
}
|
|
186
|
+
</Flex>
|
|
187
|
+
</CardBody>)}
|
|
176
188
|
</div>
|
|
177
189
|
</Card>
|
|
178
|
-
{hasChildren && !removed && <List
|
|
190
|
+
{hasChildren && !removed && !collapsed && <List
|
|
179
191
|
onItemLevelAdd={onItemLevelAdd}
|
|
180
192
|
onItemRemove={onItemRemove}
|
|
181
193
|
onItemEdit={onItemEdit}
|
|
182
194
|
onItemRestore={onItemRestore}
|
|
183
195
|
onItemReOrder={onItemReOrder}
|
|
196
|
+
onItemToggleCollapse={onItemToggleCollapse}
|
|
184
197
|
error={error}
|
|
185
198
|
allowedLevels={allowedLevels}
|
|
186
199
|
isParentAttachedToMenu={menuAttached}
|
|
@@ -204,7 +217,8 @@ Item.propTypes = {
|
|
|
204
217
|
path: PropTypes.string,
|
|
205
218
|
externalPath: PropTypes.string,
|
|
206
219
|
related: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
207
|
-
menuAttached: PropTypes.bool
|
|
220
|
+
menuAttached: PropTypes.bool,
|
|
221
|
+
collapsed: PropTypes.bool,
|
|
208
222
|
}).isRequired,
|
|
209
223
|
relatedRef: PropTypes.object,
|
|
210
224
|
level: PropTypes.number,
|
|
@@ -214,6 +228,7 @@ Item.propTypes = {
|
|
|
214
228
|
onItemLevelAdd: PropTypes.func.isRequired,
|
|
215
229
|
onItemRemove: PropTypes.func.isRequired,
|
|
216
230
|
onItemReOrder: PropTypes.func.isRequired,
|
|
231
|
+
onItemToggleCollapse: PropTypes.func.isRequired,
|
|
217
232
|
config: PropTypes.shape({
|
|
218
233
|
contentTypes: PropTypes.array.isRequired,
|
|
219
234
|
contentTypesNameFields: PropTypes.object.isRequired,
|
|
@@ -16,6 +16,7 @@ const List = ({
|
|
|
16
16
|
onItemRemove,
|
|
17
17
|
onItemRestore,
|
|
18
18
|
onItemReOrder,
|
|
19
|
+
onItemToggleCollapse,
|
|
19
20
|
displayFlat,
|
|
20
21
|
contentTypes,
|
|
21
22
|
contentTypesNameFields,
|
|
@@ -38,6 +39,7 @@ const List = ({
|
|
|
38
39
|
onItemRemove={onItemRemove}
|
|
39
40
|
onItemEdit={onItemEdit}
|
|
40
41
|
onItemReOrder={onItemReOrder}
|
|
42
|
+
onItemToggleCollapse={onItemToggleCollapse}
|
|
41
43
|
error={error}
|
|
42
44
|
displayChildren={displayFlat}
|
|
43
45
|
config={{
|
|
@@ -60,6 +62,7 @@ List.propTypes = {
|
|
|
60
62
|
onItemRestore: PropTypes.func.isRequired,
|
|
61
63
|
onItemRestore: PropTypes.func.isRequired,
|
|
62
64
|
onItemReOrder: PropTypes.func.isRequired,
|
|
65
|
+
onItemToggleCollapse: PropTypes.func.isRequired,
|
|
63
66
|
contentTypes: PropTypes.array.isRequired,
|
|
64
67
|
contentTypesNameFields: PropTypes.object.isRequired
|
|
65
68
|
};
|
|
@@ -49,13 +49,14 @@ const SettingsPage = () => {
|
|
|
49
49
|
padding: 6,
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
-
const preparePayload = ({ selectedContentTypes, nameFields, audienceFieldChecked, allowedLevels }) => ({
|
|
52
|
+
const preparePayload = ({ selectedContentTypes, nameFields, audienceFieldChecked, allowedLevels, populate }) => ({
|
|
53
53
|
contentTypes: selectedContentTypes,
|
|
54
54
|
contentTypesNameFields: nameFields,
|
|
55
|
+
contentTypesPopulate: populate,
|
|
55
56
|
additionalFields: audienceFieldChecked ? [navigationItemAdditionalFields.AUDIENCE] : [],
|
|
56
57
|
allowedLevels: allowedLevels,
|
|
57
58
|
gql: {
|
|
58
|
-
navigationItemRelated: selectedContentTypes.map(uid => allContentTypes.find(ct => ct.uid === uid).info.displayName)
|
|
59
|
+
navigationItemRelated: selectedContentTypes.map(uid => allContentTypes.find(ct => ct.uid === uid).info.displayName.replace(/\s+/g, ''))
|
|
59
60
|
}
|
|
60
61
|
});
|
|
61
62
|
|
|
@@ -110,8 +111,9 @@ const SettingsPage = () => {
|
|
|
110
111
|
const allContentTypes = !isLoading && Object.values(allContentTypesData).filter(item => item.uid.includes('api::'));
|
|
111
112
|
const selectedContentTypes = navigationConfigData?.contentTypes.map(item => item.uid);
|
|
112
113
|
const audienceFieldChecked = navigationConfigData?.additionalFields.includes(navigationItemAdditionalFields.AUDIENCE);
|
|
113
|
-
const allowedLevels = navigationConfigData?.allowedLevels;
|
|
114
|
-
const nameFields = navigationConfigData?.contentTypesNameFields
|
|
114
|
+
const allowedLevels = navigationConfigData?.allowedLevels || 2;
|
|
115
|
+
const nameFields = navigationConfigData?.contentTypesNameFields || {}
|
|
116
|
+
const populate = navigationConfigData?.contentTypesPopulate || {}
|
|
115
117
|
|
|
116
118
|
return (
|
|
117
119
|
<>
|
|
@@ -125,6 +127,7 @@ const SettingsPage = () => {
|
|
|
125
127
|
audienceFieldChecked,
|
|
126
128
|
allowedLevels,
|
|
127
129
|
nameFields,
|
|
130
|
+
populate,
|
|
128
131
|
}}
|
|
129
132
|
onSubmit={onSave}
|
|
130
133
|
>
|
|
@@ -142,7 +145,7 @@ const SettingsPage = () => {
|
|
|
142
145
|
}
|
|
143
146
|
/>
|
|
144
147
|
<ContentLayout>
|
|
145
|
-
<Stack
|
|
148
|
+
<Stack spacing={7}>
|
|
146
149
|
{isRestartRequired && (
|
|
147
150
|
<RestartAlert
|
|
148
151
|
closeLabel={getMessage('pages.settings.actions.restart.alert.cancel')}
|
|
@@ -152,7 +155,7 @@ const SettingsPage = () => {
|
|
|
152
155
|
{getMessage('pages.settings.actions.restart.alert.description')}
|
|
153
156
|
</RestartAlert>)}
|
|
154
157
|
<Box {...boxDefaultProps} >
|
|
155
|
-
<Stack
|
|
158
|
+
<Stack spacing={4}>
|
|
156
159
|
<Typography variant="delta" as="h2">
|
|
157
160
|
{getMessage('pages.settings.general.title')}
|
|
158
161
|
</Typography>
|
|
@@ -183,6 +186,7 @@ const SettingsPage = () => {
|
|
|
183
186
|
{orderBy(values.selectedContentTypes).map(uid => {
|
|
184
187
|
const { attributes, info: { displayName } } = allContentTypes.find(item => item.uid == uid);
|
|
185
188
|
const stringAttributes = Object.keys(attributes).filter(_ => attributes[_].type === 'string');
|
|
189
|
+
const relationAttributes = Object.keys(attributes).filter(_ => attributes[_].type === 'relation');
|
|
186
190
|
const key = `collectionSettings-${uid}`;
|
|
187
191
|
return (<Accordion
|
|
188
192
|
expanded={contentTypeExpanded === key}
|
|
@@ -193,24 +197,40 @@ const SettingsPage = () => {
|
|
|
193
197
|
<AccordionToggle title={displayName} togglePosition="left" />
|
|
194
198
|
<AccordionContent>
|
|
195
199
|
<Box padding={6}>
|
|
196
|
-
<Stack
|
|
200
|
+
<Stack spacing={4}>
|
|
197
201
|
<Select
|
|
198
202
|
name={`collectionSettings-${uid}-entryLabel`}
|
|
199
203
|
label={getMessage('pages.settings.form.nameField.label')}
|
|
200
|
-
hint={getMessage(
|
|
204
|
+
hint={getMessage(`pages.settings.form.populate.${isEmpty(stringAttributes) ? 'empty' : 'hint'}`)}
|
|
201
205
|
placeholder={getMessage('pages.settings.form.nameField.placeholder')}
|
|
202
206
|
onClear={() => null}
|
|
203
207
|
value={values.nameFields[uid] || []}
|
|
204
208
|
onChange={(value) => setFieldValue('nameFields', prepareNameFieldFor(uid, values.nameFields, value))}
|
|
205
209
|
multi
|
|
206
210
|
withTags
|
|
207
|
-
disabled={isRestartRequired}
|
|
211
|
+
disabled={isRestartRequired || isEmpty(stringAttributes)}
|
|
208
212
|
>
|
|
209
213
|
{stringAttributes.map(key =>
|
|
210
214
|
(<Option key={uid + key} value={key}>{capitalize(key.split('_').join(' '))}</Option>))}
|
|
215
|
+
</Select>
|
|
216
|
+
<Select
|
|
217
|
+
name={`collectionSettings-${uid}-populate`}
|
|
218
|
+
label={getMessage('pages.settings.form.populate.label')}
|
|
219
|
+
hint={getMessage(`pages.settings.form.populate.${isEmpty(relationAttributes) ? 'empty' : 'hint'}`)}
|
|
220
|
+
placeholder={getMessage('pages.settings.form.populate.placeholder')}
|
|
221
|
+
onClear={() => null}
|
|
222
|
+
value={values.populate[uid] || []}
|
|
223
|
+
onChange={(value) => setFieldValue('populate', prepareNameFieldFor(uid, values.populate, value))}
|
|
224
|
+
multi
|
|
225
|
+
withTags
|
|
226
|
+
disabled={isRestartRequired || isEmpty(relationAttributes)}
|
|
227
|
+
>
|
|
228
|
+
{relationAttributes.map(key =>
|
|
229
|
+
(<Option key={uid + key} value={key}>{capitalize(key.split('_').join(' '))}</Option>))}
|
|
211
230
|
</Select>
|
|
212
231
|
</Stack>
|
|
213
232
|
</Box>
|
|
233
|
+
|
|
214
234
|
</AccordionContent>
|
|
215
235
|
</Accordion>);
|
|
216
236
|
})}
|
|
@@ -220,22 +240,22 @@ const SettingsPage = () => {
|
|
|
220
240
|
</Stack>
|
|
221
241
|
</Box>
|
|
222
242
|
<Box {...boxDefaultProps} >
|
|
223
|
-
<Stack
|
|
243
|
+
<Stack spacing={4}>
|
|
224
244
|
<Typography variant="delta" as="h2">
|
|
225
245
|
{getMessage('pages.settings.additional.title')}
|
|
226
246
|
</Typography>
|
|
227
247
|
<Grid gap={4}>
|
|
228
248
|
<GridItem col={3} s={6} xs={12}>
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
249
|
+
<NumberInput
|
|
250
|
+
name="allowedLevels"
|
|
251
|
+
label={getMessage('pages.settings.form.allowedLevels.label')}
|
|
252
|
+
placeholder={getMessage('pages.settings.form.allowedLevels.placeholder')}
|
|
253
|
+
hint={getMessage('pages.settings.form.allowedLevels.hint')}
|
|
254
|
+
onValueChange={(value) => setFieldValue('allowedLevels', value, false)}
|
|
255
|
+
value={values.allowedLevels}
|
|
256
|
+
disabled={isRestartRequired}
|
|
257
|
+
/>
|
|
258
|
+
</GridItem>
|
|
239
259
|
<GridItem col={6} s={12} xs={12}>
|
|
240
260
|
<ToggleInput
|
|
241
261
|
name="audienceFieldChecked"
|
|
@@ -252,7 +272,7 @@ const SettingsPage = () => {
|
|
|
252
272
|
</Stack>
|
|
253
273
|
</Box>
|
|
254
274
|
<Box {...boxDefaultProps} >
|
|
255
|
-
<Stack
|
|
275
|
+
<Stack spacing={4}>
|
|
256
276
|
<Typography variant="delta" as="h2">
|
|
257
277
|
{getMessage('pages.settings.restoring.title')}
|
|
258
278
|
</Typography>
|
|
@@ -292,4 +312,4 @@ const SettingsPage = () => {
|
|
|
292
312
|
}
|
|
293
313
|
|
|
294
314
|
|
|
295
|
-
export default SettingsPage;
|
|
315
|
+
export default SettingsPage;
|