strapi-plugin-navigation 2.0.1 → 2.0.2
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 +19 -3
- package/__mocks__/strapi.js +29 -18
- package/admin/src/components/ConfirmationDialog/index.js +56 -0
- package/admin/src/components/Item/ItemCardHeader/index.js +6 -12
- package/admin/src/components/Item/index.js +8 -9
- package/admin/src/components/RestartAlert/index.js +8 -0
- package/admin/src/hooks/useAllContentTypes.js +13 -0
- package/admin/src/hooks/useNavigationConfig.js +58 -0
- package/admin/src/index.js +24 -1
- package/admin/src/pages/SettingsPage/index.js +311 -0
- package/admin/src/pages/View/components/NavigationItemForm/index.js +4 -5
- package/admin/src/pages/View/components/NavigationItemPopup/NavigationItemPopupFooter.js +3 -6
- package/admin/src/pages/View/components/NavigationItemPopup/NavigationItemPopupHeader.js +2 -4
- package/admin/src/pages/View/components/NavigationItemPopup/index.js +2 -4
- package/admin/src/translations/en.json +47 -7
- package/admin/src/translations/fr.json +4 -4
- package/admin/src/utils/api.js +51 -0
- package/admin/src/utils/index.js +20 -0
- package/package.json +4 -4
- package/server/bootstrap.js +30 -1
- package/server/controllers/navigation.js +26 -2
- package/server/graphql/index.js +3 -4
- package/server/graphql/types/content-types-name-fields.js +4 -2
- package/server/graphql/types/navigation-related.js +2 -2
- package/server/routes/admin.js +24 -1
- package/server/services/navigation.js +38 -7
- package/strapi-server.js +0 -2
- package/yarn-error.log +5263 -0
- package/server/register.js +0 -5
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Formik } from 'formik';
|
|
3
|
+
import { isEmpty, capitalize, isEqual } from 'lodash';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
CheckPermissions,
|
|
7
|
+
LoadingIndicatorPage,
|
|
8
|
+
Form,
|
|
9
|
+
useOverlayBlocker,
|
|
10
|
+
useAutoReloadOverlayBlocker,
|
|
11
|
+
} from '@strapi/helper-plugin';
|
|
12
|
+
import { Main } from '@strapi/design-system/Main';
|
|
13
|
+
import { ContentLayout, HeaderLayout } from '@strapi/design-system/Layout';
|
|
14
|
+
import { Button } from '@strapi/design-system/Button';
|
|
15
|
+
import { Box } from '@strapi/design-system/Box';
|
|
16
|
+
import { Stack } from '@strapi/design-system/Stack';
|
|
17
|
+
import { Typography } from '@strapi/design-system/Typography';
|
|
18
|
+
import { Grid, GridItem } from '@strapi/design-system/Grid';
|
|
19
|
+
import { ToggleInput } from '@strapi/design-system/ToggleInput';
|
|
20
|
+
import { NumberInput } from '@strapi/design-system/NumberInput';
|
|
21
|
+
import { Select, Option } from '@strapi/design-system/Select';
|
|
22
|
+
import { Check, Refresh, Play } from '@strapi/icons';
|
|
23
|
+
import { SettingsPageTitle } from '@strapi/helper-plugin';
|
|
24
|
+
import {
|
|
25
|
+
Card,
|
|
26
|
+
CardBody,
|
|
27
|
+
CardContent,
|
|
28
|
+
} from '@strapi/design-system/Card';
|
|
29
|
+
|
|
30
|
+
import permissions from '../../permissions';
|
|
31
|
+
import useNavigationConfig from '../../hooks/useNavigationConfig';
|
|
32
|
+
import useAllContentTypes from '../../hooks/useAllContentTypes';
|
|
33
|
+
import { navigationItemAdditionalFields } from '../View/utils/enums';
|
|
34
|
+
import ConfirmationDialog from '../../components/ConfirmationDialog';
|
|
35
|
+
import RestartAlert from '../../components/RestartAlert';
|
|
36
|
+
import { getMessage } from '../../utils';
|
|
37
|
+
|
|
38
|
+
const SettingsPage = () => {
|
|
39
|
+
const { lockApp, unlockApp } = useOverlayBlocker();
|
|
40
|
+
const { lockAppWithAutoreload, unlockAppWithAutoreload } = useAutoReloadOverlayBlocker();
|
|
41
|
+
const [isRestorePopupOpen, setIsRestorePopupOpen] = useState(false);
|
|
42
|
+
const [isRestartRequired, setIsRestartRequired] = useState(false);
|
|
43
|
+
const { data: navigationConfigData, isLoading: isConfigLoading, err: configErr, submitMutation, restoreMutation, restartMutation } = useNavigationConfig();
|
|
44
|
+
const { data: allContentTypesData, isLoading: isContentTypesLoading, err: contentTypesErr } = useAllContentTypes();
|
|
45
|
+
const isLoading = isConfigLoading || isContentTypesLoading;
|
|
46
|
+
const isError = configErr || contentTypesErr;
|
|
47
|
+
|
|
48
|
+
const preparePayload = ({ selectedContentTypes, nameFields, audienceFieldChecked, allowedLevels }) => ({
|
|
49
|
+
contentTypes: selectedContentTypes,
|
|
50
|
+
contentTypesNameFields: nameFields,
|
|
51
|
+
additionalFields: audienceFieldChecked ? [navigationItemAdditionalFields.AUDIENCE] : [],
|
|
52
|
+
allowedLevels: allowedLevels,
|
|
53
|
+
gql: {
|
|
54
|
+
navigationItemRelated: selectedContentTypes.map(uid => allContentTypes.find(ct => ct.uid === uid).info.displayName)
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
const onSave = async (form) => {
|
|
58
|
+
lockApp();
|
|
59
|
+
const payload = preparePayload(form);
|
|
60
|
+
await submitMutation({ body: payload });
|
|
61
|
+
const isContentTypesChanged = !isEqual(payload.contentTypes, navigationConfigData.contentTypes);
|
|
62
|
+
if (isContentTypesChanged && navigationConfigData.isGQLPluginEnabled) {
|
|
63
|
+
setIsRestartRequired(true);
|
|
64
|
+
}
|
|
65
|
+
unlockApp();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const onPopupClose = async (isConfirmed) => {
|
|
69
|
+
setIsRestorePopupOpen(false);
|
|
70
|
+
if (isConfirmed) {
|
|
71
|
+
lockApp();
|
|
72
|
+
await restoreMutation();
|
|
73
|
+
unlockApp();
|
|
74
|
+
setIsRestartRequired(true);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const handleRestart = async () => {
|
|
79
|
+
lockAppWithAutoreload();
|
|
80
|
+
await restartMutation();
|
|
81
|
+
setIsRestartRequired(false);
|
|
82
|
+
unlockAppWithAutoreload();
|
|
83
|
+
};
|
|
84
|
+
const handleRestartDiscard = () => setIsRestartRequired(false);
|
|
85
|
+
|
|
86
|
+
const prepareNameFieldFor = (uid, current, value) => ({
|
|
87
|
+
...current,
|
|
88
|
+
[uid]: value && !isEmpty(value) ? [...value] : undefined,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
if (isLoading || isError) {
|
|
92
|
+
return (
|
|
93
|
+
<>
|
|
94
|
+
<SettingsPageTitle
|
|
95
|
+
name={getMessage('Settings.email.plugin.title', 'Configuration')}
|
|
96
|
+
/>
|
|
97
|
+
<LoadingIndicatorPage>
|
|
98
|
+
Fetching plugin config...
|
|
99
|
+
</LoadingIndicatorPage>
|
|
100
|
+
</>
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const allContentTypes = !isLoading && Object.values(allContentTypesData).filter(item => item.uid.includes('api::'));
|
|
105
|
+
const selectedContentTypes = navigationConfigData?.contentTypes.map(item => item.uid);
|
|
106
|
+
const audienceFieldChecked = navigationConfigData?.additionalFields.includes(navigationItemAdditionalFields.AUDIENCE);
|
|
107
|
+
const allowedLevels = navigationConfigData?.allowedLevels;
|
|
108
|
+
const nameFields = navigationConfigData?.contentTypesNameFields
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<>
|
|
112
|
+
<SettingsPageTitle
|
|
113
|
+
name={getMessage('Settings.email.plugin.title', 'Configuration')}
|
|
114
|
+
/>
|
|
115
|
+
<Main labelledBy="title">
|
|
116
|
+
<Formik
|
|
117
|
+
initialValues={{
|
|
118
|
+
selectedContentTypes,
|
|
119
|
+
audienceFieldChecked,
|
|
120
|
+
allowedLevels,
|
|
121
|
+
nameFields,
|
|
122
|
+
}}
|
|
123
|
+
onSubmit={onSave}
|
|
124
|
+
>
|
|
125
|
+
{({ handleSubmit, setFieldValue, values }) => (
|
|
126
|
+
<Form noValidate onSubmit={handleSubmit}>
|
|
127
|
+
<HeaderLayout
|
|
128
|
+
title={getMessage('pages.settings.header.title')}
|
|
129
|
+
subtitle={getMessage('pages.settings.header.description')}
|
|
130
|
+
primaryAction={
|
|
131
|
+
<CheckPermissions permissions={permissions.access}>
|
|
132
|
+
<Button type="submit" startIcon={<Check />} disabled={isRestartRequired}>
|
|
133
|
+
{getMessage('pages.settings.actions.submit')}
|
|
134
|
+
</Button>
|
|
135
|
+
</CheckPermissions>
|
|
136
|
+
}
|
|
137
|
+
/>
|
|
138
|
+
<ContentLayout>
|
|
139
|
+
<Stack size={7}>
|
|
140
|
+
{isRestartRequired && (
|
|
141
|
+
<RestartAlert
|
|
142
|
+
closeLabel={getMessage('pages.settings.actions.restart.alert.cancel')}
|
|
143
|
+
title={getMessage('pages.settings.actions.restart.alert.title')}
|
|
144
|
+
action={<Box><Button onClick={handleRestart} startIcon={<Play />}>{getMessage('pages.settings.actions.restart')}</Button></Box>}
|
|
145
|
+
onClose={handleRestartDiscard}>
|
|
146
|
+
{getMessage('pages.settings.actions.restart.alert.description')}
|
|
147
|
+
</RestartAlert>)}
|
|
148
|
+
<Box
|
|
149
|
+
background="neutral0"
|
|
150
|
+
hasRadius
|
|
151
|
+
shadow="filterShadow"
|
|
152
|
+
padding={6}
|
|
153
|
+
>
|
|
154
|
+
<Stack size={4}>
|
|
155
|
+
<Typography variant="delta" as="h2">
|
|
156
|
+
{getMessage('pages.settings.general.title')}
|
|
157
|
+
</Typography>
|
|
158
|
+
<Grid gap={4}>
|
|
159
|
+
<GridItem col={12} s={12} xs={12}>
|
|
160
|
+
<Select
|
|
161
|
+
name="selectedContentTypes"
|
|
162
|
+
label={getMessage('pages.settings.form.contentTypes.label')}
|
|
163
|
+
placeholder={getMessage('pages.settings.form.contentTypes.placeholder')}
|
|
164
|
+
hint={getMessage('pages.settings.form.contentTypes.hint')}
|
|
165
|
+
onClear={() => setFieldValue('selectedContentTypes', [], false)}
|
|
166
|
+
value={values.selectedContentTypes}
|
|
167
|
+
onChange={(value) => setFieldValue('selectedContentTypes', value, false)}
|
|
168
|
+
multi
|
|
169
|
+
withTags
|
|
170
|
+
disabled={isRestartRequired}
|
|
171
|
+
>
|
|
172
|
+
{allContentTypes.map((item) => <Option key={item.uid} value={item.uid}>{item.info.displayName}</Option>)}
|
|
173
|
+
</Select>
|
|
174
|
+
</GridItem>
|
|
175
|
+
<GridItem col={3} s={6} xs={12}>
|
|
176
|
+
<NumberInput
|
|
177
|
+
name="allowedLevels"
|
|
178
|
+
label={getMessage('pages.settings.form.allowedLevels.label')}
|
|
179
|
+
placeholder={getMessage('pages.settings.form.allowedLevels.placeholder')}
|
|
180
|
+
hint={getMessage('pages.settings.form.allowedLevels.hint')}
|
|
181
|
+
onValueChange={(value) => setFieldValue('allowedLevels', value, false)}
|
|
182
|
+
value={values.allowedLevels}
|
|
183
|
+
disabled={isRestartRequired}
|
|
184
|
+
/>
|
|
185
|
+
</GridItem>
|
|
186
|
+
</Grid>
|
|
187
|
+
</Stack>
|
|
188
|
+
</Box>
|
|
189
|
+
<Box
|
|
190
|
+
background="neutral0"
|
|
191
|
+
hasRadius
|
|
192
|
+
shadow="filterShadow"
|
|
193
|
+
padding={6}
|
|
194
|
+
>
|
|
195
|
+
<Stack size={4}>
|
|
196
|
+
<Typography variant="delta" as="h2">
|
|
197
|
+
{getMessage('pages.settings.additional.title')}
|
|
198
|
+
</Typography>
|
|
199
|
+
<Grid gap={4}>
|
|
200
|
+
<GridItem col={6} s={12} xs={12}>
|
|
201
|
+
<ToggleInput
|
|
202
|
+
name="audienceFieldChecked"
|
|
203
|
+
label={getMessage('pages.settings.form.audience.label')}
|
|
204
|
+
hint={getMessage('pages.settings.form.audience.hint')}
|
|
205
|
+
checked={values.audienceFieldChecked}
|
|
206
|
+
onChange={({ target: { checked } }) => setFieldValue('audienceFieldChecked', checked, false)}
|
|
207
|
+
onLabel="Enabled"
|
|
208
|
+
offLabel="Disabled"
|
|
209
|
+
disabled={isRestartRequired}
|
|
210
|
+
/>
|
|
211
|
+
</GridItem>
|
|
212
|
+
</Grid>
|
|
213
|
+
</Stack>
|
|
214
|
+
</Box>
|
|
215
|
+
{!isEmpty(values.selectedContentTypes) && (
|
|
216
|
+
<Box
|
|
217
|
+
background="neutral0"
|
|
218
|
+
hasRadius
|
|
219
|
+
shadow="filterShadow"
|
|
220
|
+
padding={6}
|
|
221
|
+
>
|
|
222
|
+
<Stack size={4}>
|
|
223
|
+
<Typography variant="delta" as="h2">
|
|
224
|
+
{getMessage('pages.settings.nameField.title')}
|
|
225
|
+
</Typography>
|
|
226
|
+
<Grid gap={4}>
|
|
227
|
+
{values.selectedContentTypes.map(uid => {
|
|
228
|
+
const { attributes, info: { displayName } } = allContentTypes.find(item => item.uid == uid);
|
|
229
|
+
const stringAttributes = Object.keys(attributes).filter(_ => attributes[_].type === 'string');
|
|
230
|
+
|
|
231
|
+
return !isEmpty(stringAttributes) && (
|
|
232
|
+
<GridItem key={`collectionSettings-${uid}`} col={6} s={12} xs={12}>
|
|
233
|
+
<Card background="primary100" borderColor="primary200">
|
|
234
|
+
<CardBody>
|
|
235
|
+
<CardContent style={{ width: '100%' }}>
|
|
236
|
+
<Stack size={4}>
|
|
237
|
+
<Typography variant="epsilon" fontWeight="semibold" as="h3">{displayName}</Typography>
|
|
238
|
+
<Select
|
|
239
|
+
name={`collectionSettings-${uid}-entryLabel`}
|
|
240
|
+
label={getMessage('pages.settings.form.nameField.label')}
|
|
241
|
+
hint={getMessage('pages.settings.form.nameField.hint')}
|
|
242
|
+
placeholder={getMessage('pages.settings.form.nameField.placeholder')}
|
|
243
|
+
onClear={() => null}
|
|
244
|
+
value={values.nameFields[uid] || []}
|
|
245
|
+
onChange={(value) => setFieldValue('nameFields', prepareNameFieldFor(uid, values.nameFields, value))}
|
|
246
|
+
multi
|
|
247
|
+
withTags
|
|
248
|
+
disabled={isRestartRequired}
|
|
249
|
+
>
|
|
250
|
+
{stringAttributes.map(key =>
|
|
251
|
+
(<Option key={uid + key} value={key}>{capitalize(key.split('_').join(' '))}</Option>))}
|
|
252
|
+
</Select>
|
|
253
|
+
</Stack>
|
|
254
|
+
</CardContent>
|
|
255
|
+
</CardBody>
|
|
256
|
+
</Card>
|
|
257
|
+
</GridItem>
|
|
258
|
+
);
|
|
259
|
+
})
|
|
260
|
+
}
|
|
261
|
+
</Grid>
|
|
262
|
+
</Stack>
|
|
263
|
+
</Box>
|
|
264
|
+
)}
|
|
265
|
+
<Box
|
|
266
|
+
background="neutral0"
|
|
267
|
+
hasRadius
|
|
268
|
+
shadow="filterShadow"
|
|
269
|
+
padding={6}
|
|
270
|
+
>
|
|
271
|
+
<Stack size={4}>
|
|
272
|
+
<Typography variant="delta" as="h2">
|
|
273
|
+
{getMessage('pages.settings.restoring.title')}
|
|
274
|
+
</Typography>
|
|
275
|
+
<Grid gap={4}>
|
|
276
|
+
<GridItem col={12} s={12} xs={12}>
|
|
277
|
+
<Typography>
|
|
278
|
+
{getMessage('pages.settings.actions.restore.description')}
|
|
279
|
+
</Typography>
|
|
280
|
+
</GridItem>
|
|
281
|
+
<GridItem col={6} s={12} xs={12}>
|
|
282
|
+
<CheckPermissions permissions={permissions.access}>
|
|
283
|
+
<Button variant="danger-light" startIcon={<Refresh />} onClick={() => setIsRestorePopupOpen(true)}>
|
|
284
|
+
{getMessage('pages.settings.actions.restore')}
|
|
285
|
+
</Button>
|
|
286
|
+
</CheckPermissions>
|
|
287
|
+
<ConfirmationDialog
|
|
288
|
+
isVisible={isRestorePopupOpen}
|
|
289
|
+
header={getMessage('pages.settings.actions.restore.confirmation.header')}
|
|
290
|
+
labelConfirm={getMessage('pages.settings.actions.restore.confirmation.confirm')}
|
|
291
|
+
iconConfirm={<Refresh />}
|
|
292
|
+
onConfirm={() => onPopupClose(true)}
|
|
293
|
+
onCancel={() => onPopupClose(false)}>
|
|
294
|
+
{getMessage('pages.settings.actions.restore.confirmation.description')}
|
|
295
|
+
</ConfirmationDialog>
|
|
296
|
+
</GridItem>
|
|
297
|
+
</Grid>
|
|
298
|
+
</Stack>
|
|
299
|
+
</Box>
|
|
300
|
+
</Stack>
|
|
301
|
+
</ContentLayout>
|
|
302
|
+
</Form>
|
|
303
|
+
)}
|
|
304
|
+
</Formik>
|
|
305
|
+
</Main>
|
|
306
|
+
</>
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
export default SettingsPage;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import React, { useEffect, useMemo, useState, useCallback } from 'react';
|
|
2
|
-
import { useIntl } from 'react-intl';
|
|
3
2
|
import { debounce, find, get, isEmpty, isEqual, isNil, isString } from 'lodash';
|
|
4
3
|
import PropTypes from 'prop-types';
|
|
5
4
|
import { Formik } from 'formik'
|
|
@@ -18,7 +17,8 @@ import slugify from 'slugify';
|
|
|
18
17
|
import { extractRelatedItemLabel } from '../../utils/parsers';
|
|
19
18
|
import { form as formDefinition } from './utils/form';
|
|
20
19
|
import { checkFormValidity } from '../../utils/form';
|
|
21
|
-
import { getTradId
|
|
20
|
+
import { getTradId } from '../../../../translations';
|
|
21
|
+
import { getMessage } from '../../../../utils';
|
|
22
22
|
|
|
23
23
|
const NavigationItemForm = ({
|
|
24
24
|
isLoading,
|
|
@@ -43,7 +43,6 @@ const NavigationItemForm = ({
|
|
|
43
43
|
const [form, setFormState] = useState({});
|
|
44
44
|
const [formErrors, setFormErrorsState] = useState({});
|
|
45
45
|
const { relatedType } = form;
|
|
46
|
-
const { formatMessage } = useIntl();
|
|
47
46
|
|
|
48
47
|
const relatedFieldName = `${inputsPrefix}related`;
|
|
49
48
|
|
|
@@ -397,8 +396,8 @@ const NavigationItemForm = ({
|
|
|
397
396
|
<GridItem key={`${inputsPrefix}audience`} col={6} lg={12}>
|
|
398
397
|
<Select
|
|
399
398
|
id={`${inputsPrefix}audience`}
|
|
400
|
-
placeholder={
|
|
401
|
-
label={
|
|
399
|
+
placeholder={getMessage('popup.item.form.audience.placeholder')}
|
|
400
|
+
label={getMessage('popup.item.form.audience.label')}
|
|
402
401
|
onChange={onAudienceChange}
|
|
403
402
|
value={audience}
|
|
404
403
|
multi
|
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
-
import { useIntl } from 'react-intl';
|
|
4
3
|
|
|
5
4
|
import { Button } from '@strapi/design-system/Button';
|
|
6
5
|
import { ModalFooter } from '@strapi/design-system/ModalLayout';
|
|
7
|
-
|
|
8
|
-
import { getTrad } from '../../../../translations';
|
|
6
|
+
import { getMessage } from '../../../../utils';
|
|
9
7
|
|
|
10
8
|
export const NavigationItemPopupFooter = ({ handleCancel, handleSubmit, submitDisabled, formViewId }) => {
|
|
11
|
-
const { formatMessage } = useIntl();
|
|
12
9
|
|
|
13
10
|
return (
|
|
14
11
|
<ModalFooter
|
|
15
12
|
startActions={
|
|
16
13
|
<Button onClick={handleCancel} variant="tertiary">
|
|
17
|
-
{
|
|
14
|
+
{getMessage('popup.item.form.button.cancel')}
|
|
18
15
|
</Button>
|
|
19
16
|
}
|
|
20
17
|
endActions={
|
|
21
18
|
<Button onClick={handleSubmit} disabled={submitDisabled}>
|
|
22
|
-
{
|
|
19
|
+
{getMessage(`popup.item.form.button.save`)}
|
|
23
20
|
</Button>
|
|
24
21
|
}
|
|
25
22
|
/>
|
|
@@ -3,15 +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 {
|
|
7
|
-
import { getTrad } from '../../../../translations';
|
|
6
|
+
import { getMessage } from '../../../../utils';
|
|
8
7
|
|
|
9
8
|
export const NavigationItemPopupHeader = ({isNewItem}) => {
|
|
10
|
-
const { formatMessage } = useIntl();
|
|
11
9
|
return (
|
|
12
10
|
<ModalHeader>
|
|
13
11
|
<ButtonText textColor="neutral800" as="h2" id="asset-dialog-title">
|
|
14
|
-
{
|
|
12
|
+
{getMessage(`popup.item.header.${isNewItem ? 'new' : 'edit'}`)}
|
|
15
13
|
</ButtonText>
|
|
16
14
|
</ModalHeader>
|
|
17
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
|
-
}) ? '' : `[${
|
|
52
|
+
}) ? '' : `[${getMessage('notification.navigation.item.relation.status.draft')}] `.toUpperCase();
|
|
55
53
|
return `${appendix}${label}`;
|
|
56
54
|
};
|
|
57
55
|
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"popup.item.form.path.preview": "Preview:",
|
|
19
19
|
"popup.item.form.externalPath.label": "External URL",
|
|
20
20
|
"popup.item.form.externalPath.placeholder": "Link to the external source",
|
|
21
|
-
"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.",
|
|
22
22
|
"popup.item.form.menuAttached.label": "Attach to menu",
|
|
23
23
|
"popup.item.form.type.label": "Internal link",
|
|
24
24
|
"popup.item.form.type.internal.label": "Internal source",
|
|
@@ -43,9 +43,49 @@
|
|
|
43
43
|
"notification.navigation.item.relation": "Entity relation does not exist!",
|
|
44
44
|
"notification.navigation.item.relation.status.draft": "draft",
|
|
45
45
|
"notification.navigation.item.relation.status.published": "published",
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "strapi-plugin-navigation",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "Strapi - Navigation plugin",
|
|
5
5
|
"strapi": {
|
|
6
6
|
"name": "navigation",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"url": "https://github.com/VirtusLab/strapi-plugin-navigation"
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
|
-
"publish": "npm publish --tag latest",
|
|
16
|
+
"publish:latest": "npm publish --tag latest",
|
|
17
17
|
"test:unit": "jest --verbose --coverage"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"@strapi/strapi": "4.x"
|
|
45
45
|
},
|
|
46
46
|
"author": {
|
|
47
|
-
"name": "VirtusLab
|
|
48
|
-
"email": "
|
|
47
|
+
"name": "VirtusLab",
|
|
48
|
+
"email": "strapi@virtuslab.com",
|
|
49
49
|
"url": "https://virtuslab.com"
|
|
50
50
|
},
|
|
51
51
|
"maintainers": [
|
package/server/bootstrap.js
CHANGED
|
@@ -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
|
-
|
|
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
|
};
|