strapi-plugin-navigation 1.1.3 → 2.0.0-beta.4

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 (149) hide show
  1. package/README.md +61 -87
  2. package/admin/src/components/EmptyView/index.js +7 -16
  3. package/admin/src/components/Item/ItemCardBadge/index.js +8 -0
  4. package/admin/src/components/Item/ItemCardHeader/Wrapper.js +21 -0
  5. package/admin/src/components/Item/ItemCardHeader/index.js +59 -0
  6. package/admin/src/components/Item/Wrapper.js +39 -0
  7. package/admin/src/components/Item/index.js +78 -124
  8. package/admin/src/components/NavigationItemList/Wrapper.js +22 -0
  9. package/admin/src/components/NavigationItemList/index.js +56 -0
  10. package/admin/src/components/PluginIcon/index.js +6 -0
  11. package/admin/src/components/Search/index.js +46 -83
  12. package/admin/src/index.js +49 -45
  13. package/admin/src/pages/App/index.js +31 -0
  14. package/admin/src/{containers → pages}/DataManagerProvider/actions.js +0 -0
  15. package/admin/src/{containers → pages}/DataManagerProvider/index.js +77 -84
  16. package/admin/src/{containers → pages}/DataManagerProvider/init.js +0 -0
  17. package/admin/src/pages/DataManagerProvider/reducer.js +125 -0
  18. package/admin/src/pages/View/components/NavigationContentHeader/index.js +18 -0
  19. package/admin/src/pages/View/components/NavigationHeader/index.js +60 -0
  20. package/admin/src/pages/View/components/NavigationItemForm/index.js +403 -0
  21. package/admin/src/{containers → pages}/View/components/NavigationItemForm/utils/form.js +2 -2
  22. package/admin/src/pages/View/components/NavigationItemPopup/NavigationItemPopupFooter.js +40 -0
  23. package/admin/src/pages/View/components/NavigationItemPopup/NavigationItemPopupHeader.js +20 -0
  24. package/admin/src/{containers → pages}/View/components/NavigationItemPopup/index.js +16 -16
  25. package/admin/src/pages/View/index.js +221 -0
  26. package/admin/src/{containers → pages}/View/utils/enums.js +0 -0
  27. package/admin/src/{containers → pages}/View/utils/form.js +1 -1
  28. package/admin/src/{containers → pages}/View/utils/index.js +0 -0
  29. package/admin/src/{containers → pages}/View/utils/parsers.js +13 -12
  30. package/admin/src/pluginId.js +3 -2
  31. package/admin/src/translations/en.json +47 -38
  32. package/admin/src/translations/fr.json +7 -1
  33. package/admin/src/utils/getTrad.js +2 -2
  34. package/package.json +18 -7
  35. package/server/bootstrap.js +41 -0
  36. package/server/config/index.js +8 -0
  37. package/server/config.js +8 -0
  38. package/server/content-types/audience/index.js +9 -0
  39. package/{models/audience.js → server/content-types/audience/lifecycle.js} +0 -0
  40. package/{models/audience.settings.json → server/content-types/audience/schema.json} +4 -2
  41. package/server/content-types/index.js +13 -0
  42. package/server/content-types/navigation/index.js +9 -0
  43. package/{models/navigation.js → server/content-types/navigation/lifecycle.js} +0 -0
  44. package/server/content-types/navigation/schema.js +45 -0
  45. package/server/content-types/navigation-item/index.js +9 -0
  46. package/{models/navigationItem.js → server/content-types/navigation-item/lifecycle.js} +0 -0
  47. package/{models/navigationItem.settings.json → server/content-types/navigation-item/schema.json} +16 -12
  48. package/server/content-types/navigations-items-related/index.js +9 -0
  49. package/{models/navigations_items_related.js → server/content-types/navigations-items-related/lifecycle.js} +0 -0
  50. package/{models/navigations_items_related.settings.json → server/content-types/navigations-items-related/schema.json} +4 -2
  51. package/server/controllers/index.js +7 -0
  52. package/{controllers → server/controllers}/navigation.js +20 -31
  53. package/server/graphql/index.js +23 -0
  54. package/server/graphql/queries/index.js +17 -0
  55. package/server/graphql/queries/render-navigation-child.js +16 -0
  56. package/server/graphql/queries/render-navigation.js +15 -0
  57. package/server/graphql/resolvers-config.js +4 -0
  58. package/server/graphql/types/content-types-name-fields.js +8 -0
  59. package/server/graphql/types/content-types.js +16 -0
  60. package/server/graphql/types/create-navigation-item.js +17 -0
  61. package/server/graphql/types/create-navigation-related.js +8 -0
  62. package/server/graphql/types/create-navigation.js +7 -0
  63. package/server/graphql/types/index.js +15 -0
  64. package/server/graphql/types/navigation-config.js +9 -0
  65. package/server/graphql/types/navigation-details.js +10 -0
  66. package/server/graphql/types/navigation-item.js +29 -0
  67. package/server/graphql/types/navigation-related.js +23 -0
  68. package/server/graphql/types/navigation-render-type.js +4 -0
  69. package/server/graphql/types/navigation.js +9 -0
  70. package/server/register.js +5 -0
  71. package/server/routes/admin.js +38 -0
  72. package/server/routes/client.js +21 -0
  73. package/server/routes/index.js +4 -0
  74. package/{services → server/services}/__tests__/navigation.test.js +0 -0
  75. package/server/services/index.js +7 -0
  76. package/server/services/navigation.js +729 -0
  77. package/{services → server/services}/utils/constant.js +3 -1
  78. package/server/services/utils/functions.js +185 -0
  79. package/strapi-admin.js +1 -0
  80. package/strapi-server.js +20 -0
  81. package/__mocks__/helpers/another-plugin/blog-post.settings.json +0 -31
  82. package/__mocks__/helpers/another-plugin/pages.settings.json +0 -28
  83. package/__mocks__/helpers/blog-post.settings.json +0 -31
  84. package/__mocks__/helpers/home-page.settings.json +0 -4
  85. package/__mocks__/helpers/my-homepage.settings.json +0 -27
  86. package/__mocks__/helpers/pages.settings.json +0 -27
  87. package/__mocks__/helpers/strapi.js +0 -101
  88. package/admin/src/assets/images/icon-cross-blue.svg +0 -1
  89. package/admin/src/assets/images/icon_remove.svg +0 -19
  90. package/admin/src/components/Container/index.js +0 -7
  91. package/admin/src/components/Input/index.js +0 -41
  92. package/admin/src/components/Item/CardItem.js +0 -46
  93. package/admin/src/components/Item/CardItemLevelAdd.js +0 -41
  94. package/admin/src/components/Item/CardItemLevelWrapper.js +0 -27
  95. package/admin/src/components/Item/CardItemPath.js +0 -9
  96. package/admin/src/components/Item/CardItemRestore.js +0 -19
  97. package/admin/src/components/Item/CardItemTitle.js +0 -5
  98. package/admin/src/components/Item/CardWrapper.js +0 -78
  99. package/admin/src/components/ItemFooter/CardItemError.js +0 -11
  100. package/admin/src/components/ItemFooter/CardItemRelation.js +0 -18
  101. package/admin/src/components/ItemFooter/CardItemRelationStatus.js +0 -17
  102. package/admin/src/components/ItemFooter/CardItemType.js +0 -18
  103. package/admin/src/components/ItemFooter/Wrapper.js +0 -26
  104. package/admin/src/components/ItemFooter/index.js +0 -66
  105. package/admin/src/components/ItemOrdering/CardOrderingButton.js +0 -24
  106. package/admin/src/components/ItemOrdering/Wrapper.js +0 -24
  107. package/admin/src/components/ItemOrdering/index.js +0 -36
  108. package/admin/src/components/List/Container.js +0 -34
  109. package/admin/src/components/List/ListLevelRoot.js +0 -18
  110. package/admin/src/components/List/index.js +0 -81
  111. package/admin/src/components/Option/OptionButton.js +0 -18
  112. package/admin/src/components/Option/OptionSet.js +0 -14
  113. package/admin/src/components/Option/Wrapper.js +0 -15
  114. package/admin/src/components/Option/index.js +0 -47
  115. package/admin/src/components/Select/ClearIndicator.js +0 -15
  116. package/admin/src/components/Select/DropdownIndicator.js +0 -39
  117. package/admin/src/components/Select/ErrorMessage.js +0 -10
  118. package/admin/src/components/Select/IndicatorSeparator.js +0 -3
  119. package/admin/src/components/Select/MultiValueContainer.js +0 -43
  120. package/admin/src/components/Select/StyledOption.js +0 -11
  121. package/admin/src/components/Select/index.js +0 -68
  122. package/admin/src/components/Select/utils/styles.js +0 -92
  123. package/admin/src/containers/App/Wrapper.js +0 -14
  124. package/admin/src/containers/App/index.js +0 -34
  125. package/admin/src/containers/DataManagerProvider/reducer.js +0 -136
  126. package/admin/src/containers/DetailsView/Wrapper.js +0 -21
  127. package/admin/src/containers/DetailsView/index.js +0 -111
  128. package/admin/src/containers/Initializer/index.js +0 -26
  129. package/admin/src/containers/ListView/Footer.js +0 -56
  130. package/admin/src/containers/ListView/components.js +0 -138
  131. package/admin/src/containers/ListView/index.js +0 -54
  132. package/admin/src/containers/View/FadedWrapper.js +0 -51
  133. package/admin/src/containers/View/HeaderForm.js +0 -9
  134. package/admin/src/containers/View/HeaderFormCell.js +0 -25
  135. package/admin/src/containers/View/Wrapper.js +0 -17
  136. package/admin/src/containers/View/components/NavigationItemForm/ModalFooter.js +0 -45
  137. package/admin/src/containers/View/components/NavigationItemForm/index.js +0 -427
  138. package/admin/src/containers/View/components/NavigationItemPopup/MediumPopup.js +0 -6
  139. package/admin/src/containers/View/index.js +0 -240
  140. package/admin/src/lifecycles.js +0 -3
  141. package/admin/src/permissions.js +0 -14
  142. package/config/functions/bootstrap.js +0 -138
  143. package/config/routes.json +0 -60
  144. package/config/schema.graphql.js +0 -209
  145. package/examples/audit-log-integrations.js.md +0 -38
  146. package/models/navigation.settings.json +0 -43
  147. package/public/assets/preview.png +0 -0
  148. package/services/navigation.js +0 -732
  149. package/services/utils/functions.js +0 -186
@@ -1,427 +0,0 @@
1
- import React, { useEffect, useMemo, useState, useCallback } from 'react';
2
- import { Button, Enumeration, Flex, Label, Text, Toggle } from '@buffetjs/core';
3
- import { useIntl } from 'react-intl';
4
- import { debounce, find, get, isEmpty, isEqual, isNil, isString } from 'lodash';
5
- import PropTypes from 'prop-types';
6
- import { ButtonModal, ModalBody, ModalForm } from 'strapi-helper-plugin';
7
- import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
8
- import { faEye, faInfoCircle } from '@fortawesome/free-solid-svg-icons';
9
- import ModalFooter from './ModalFooter';
10
- import Input from '../../../../components/Input';
11
- import { navigationItemAdditionalFields, navigationItemType } from '../../utils/enums';
12
- import slugify from 'slugify';
13
- import Select from '../../../../components/Select';
14
- import { extractRelatedItemLabel } from '../../utils/parsers';
15
- import { form as formDefinition } from './utils/form';
16
- import { checkFormValidity } from '../../utils/form';
17
- import { getTrad, getTradId } from '../../../../translations';
18
-
19
- const NavigationItemForm = ({
20
- isLoading,
21
- inputsPrefix,
22
- data = {},
23
- contentTypes = [],
24
- contentTypeEntities = [],
25
- usedContentTypeEntities = [],
26
- availableAudience = [],
27
- additionalFields = [],
28
- contentTypesNameFields = {},
29
- onSubmit,
30
- getContentTypeEntities,
31
- usedContentTypesData,
32
- appendLabelPublicationStatus = () => '',
33
- }) => {
34
- const [hasBeenInitialized, setInitializedState] = useState(false);
35
- const [hasChanged, setChangedState] = useState(false);
36
- const [contentTypeSearchQuery, setContentTypeSearchQuery] = useState(undefined);
37
- const [contentTypeSearchInputValue, setContentTypeSearchInputValue] = useState(undefined);
38
- const [form, setFormState] = useState({});
39
- const [formErrors, setFormErrorsState] = useState({});
40
- const { relatedType } = form;
41
- const { formatMessage } = useIntl();
42
-
43
- const relatedFieldName = `${inputsPrefix}related`;
44
-
45
- if (!hasBeenInitialized && !isEmpty(data)) {
46
- setInitializedState(true);
47
- setFormState({ ...data });
48
- }
49
-
50
- const sanitizePayload = (payload = {}) => {
51
- const { onItemClick, onItemLevelAddClick, related, relatedType, menuAttached, ...purePayload } = payload;
52
- const sanitizedType = purePayload.type || navigationItemType.INTERNAL;
53
- const relatedId = related?.value
54
- const relatedCollectionType = relatedType?.value;
55
- return {
56
- ...purePayload,
57
- menuAttached: isNil(menuAttached) ? false : menuAttached,
58
- type: sanitizedType,
59
- path: sanitizedType === navigationItemType.INTERNAL ? purePayload.path : undefined,
60
- externalPath: sanitizedType === navigationItemType.EXTERNAL ? purePayload.externalPath : undefined,
61
- related: relatedId,
62
- relatedType: relatedCollectionType,
63
- isSingle: isSingleSelected,
64
- uiRouterKey: generateUiRouterKey(purePayload.title, relatedId, relatedCollectionType),
65
- };
66
- };
67
-
68
- const handleSubmit = async e => {
69
- if (e) {
70
- e.preventDefault();
71
- }
72
-
73
- const payload = sanitizePayload(form);
74
- const errors = await checkFormValidity(payload, formDefinition.schema(isSingleSelected));
75
- if (!errors || isEmpty(errors)) {
76
- return onSubmit(payload);
77
- } else {
78
- setFormErrorsState(errors);
79
- }
80
- };
81
-
82
- const handleRemove = e => {
83
- if (e) {
84
- e.preventDefault();
85
- }
86
- return onSubmit(sanitizePayload({
87
- ...form,
88
- removed: true,
89
- }));
90
- };
91
-
92
- const onChange = ({ target: { name, value } }) => {
93
- setFormState(prevState => ({
94
- ...prevState,
95
- updated: true,
96
- [name]: value,
97
- }));
98
- if (!hasChanged) {
99
- setChangedState(true);
100
- }
101
- };
102
-
103
- const onChangeRelatedType = ({ target: { name, value } }) => {
104
- const relatedTypeBeingReverted = data.relatedType && (data.relatedType.value === get(value, 'value', value));
105
- setContentTypeSearchQuery(undefined);
106
- setContentTypeSearchInputValue(undefined);
107
- setFormState(prevState => ({
108
- ...prevState,
109
- updated: true,
110
- related: relatedTypeBeingReverted ? {
111
- ...data.related
112
- } : undefined,
113
- [name]: value,
114
- }));
115
- if (!hasChanged) {
116
- setChangedState(true);
117
- }
118
- };
119
-
120
-
121
- const generateUiRouterKey = (title, related, relatedType) => {
122
- if (title) {
123
- return isString(title) && !isEmpty(title) ? slugify(title).toLowerCase() : undefined;
124
- } else if (related) {
125
- const relationTitle = extractRelatedItemLabel({
126
- ...contentTypeEntities.find(_ => _.id === related),
127
- __collectionName: relatedType }, contentTypesNameFields, { contentTypes });
128
- return isString(relationTitle) && !isEmpty(relationTitle) ? slugify(relationTitle).toLowerCase() : undefined;
129
- }
130
- return undefined;
131
- };
132
-
133
- const typeSelectValue = form.type || navigationItemType.INTERNAL;
134
- const relatedTypeSelectValue = form.relatedType;
135
- const relatedSelectValue = form.related;
136
-
137
- const typeSelectOptions = useMemo(
138
- () => Object.keys(navigationItemType).map((key) => ({
139
- value: key,
140
- label: formatMessage(getTrad(`popup.item.form.type.${key.toLowerCase()}.label`)),
141
- })),
142
- [],
143
- );
144
-
145
- const isSingleSelected = useMemo(
146
- () => relatedTypeSelectValue ? contentTypes.find(_ => _.uid === relatedType.value)?.isSingle : false,
147
- [relatedTypeSelectValue, contentTypes],
148
- );
149
-
150
- const relatedTypeSelectOptions = useMemo(
151
- () => contentTypes
152
- .filter((contentType) => {
153
- if (contentType.isSingle) {
154
- return !usedContentTypesData.some((_) => _.__collectionName === contentType.uid);
155
- }
156
- return true;
157
- })
158
- .map((item) => ({
159
- value: get(item, 'uid'),
160
- label: appendLabelPublicationStatus(get(item, 'label', get(item, 'name')), item, true),
161
- })),
162
- [contentTypes, usedContentTypesData],
163
- );
164
-
165
- const relatedSelectOptions = contentTypeEntities
166
- .filter((item) => {
167
- const usedContentTypeEntitiesOfSameType = usedContentTypeEntities
168
- .filter(uctItem => (get(relatedTypeSelectValue, 'value') === uctItem.__collectionName) && (uctItem.id !== get(relatedSelectValue, 'value')));
169
- return !find(usedContentTypeEntitiesOfSameType, uctItem => item.id === uctItem.id);
170
- })
171
- .map((item) => ({
172
- value: item.id,
173
- label: appendLabelPublicationStatus(
174
- extractRelatedItemLabel({
175
- ...item,
176
- __collectionName: get(relatedTypeSelectValue, 'value', relatedTypeSelectValue),
177
- }, contentTypesNameFields, { contentTypes }),
178
- item
179
- ),
180
- }));
181
-
182
- const isExternal = form.type === navigationItemType.EXTERNAL;
183
- const pathSourceName = isExternal ? 'externalPath' : 'path';
184
-
185
- const audience = get(form, `${inputsPrefix}audience`, []);
186
- const audienceOptions = availableAudience.map((item) => ({
187
- value: item.id,
188
- label: item.name,
189
- }));
190
-
191
- const submitDisabled = (form.type !== navigationItemType.EXTERNAL) && isNil(form.related);
192
-
193
- const generatePreviewPath = () => {
194
- if (!isExternal && data.levelPath) {
195
- return (
196
- <Text fontSize="sm" color="grey">
197
- <FontAwesomeIcon icon={faEye} />{' '}
198
- {formatMessage(getTrad('popup.item.form.path.preview'))}{' '}
199
- {data.levelPath !== '/' ? `${data.levelPath}` : ''}/{form.path}
200
- </Text>
201
- );
202
- }
203
- return null;
204
- };
205
-
206
- const debouncedSearch = useCallback(
207
- debounce(nextValue => setContentTypeSearchQuery(nextValue), 500),
208
- [],
209
- );
210
-
211
- const debounceContentTypeSearchQuery = value => {
212
- setContentTypeSearchInputValue(value);
213
- debouncedSearch(value);
214
- };
215
-
216
- const thereAreNoMoreContentTypes = isEmpty(relatedSelectOptions) && !contentTypeSearchQuery;
217
-
218
- useEffect(
219
- () => {
220
- const value = get(relatedSelectOptions, '0');
221
- if (isSingleSelected && relatedSelectOptions.length === 1 && !isEqual(value, relatedSelectValue)) {
222
- onChange({ target: { name: relatedFieldName, value} });
223
- }
224
- },
225
- [isSingleSelected, relatedSelectOptions],
226
- );
227
-
228
- useEffect(() => {
229
- const { value } = relatedType || {};
230
- const fetchContentTypeEntities = async () => {
231
- if (value) {
232
- const item = find(
233
- contentTypes,
234
- (_) => _.uid === value,
235
- );
236
- if (item) {
237
- await getContentTypeEntities({
238
- type: item.endpoint || item.collectionName,
239
- query: contentTypeSearchQuery,
240
- }, item.plugin);
241
- }
242
- }
243
- };
244
- fetchContentTypeEntities();
245
- }, [relatedType, contentTypeSearchQuery]);
246
-
247
- return (
248
- <form onSubmit={handleSubmit}>
249
- <ModalForm>
250
- <ModalBody>
251
- <section className="col-12">
252
- <div className="row">
253
- <div className="col-lg-9 col-md-12">
254
- <Input
255
- autoFocus
256
- error={get(formErrors, `${inputsPrefix}title`)}
257
- label={getTradId('popup.item.form.title.label')}
258
- name={`${inputsPrefix}title`}
259
- onChange={onChange}
260
- placeholder={!isExternal ? getTradId('popup.item.form.title.placeholder') : ''}
261
- type="text"
262
- validations={{ required: true }}
263
- value={get(form, `${inputsPrefix}title`, '')}
264
- />
265
- </div>
266
- <div className="col-lg-3 col-md-12">
267
- <Flex alignItems="flex-start" flexWrap="wrap">
268
- <Label
269
- htmlFor={`${inputsPrefix}menuAttached`}
270
- style={{ display: 'block' }}
271
- message={formatMessage(getTrad('popup.item.form.menuAttached.label'))}
272
- />
273
- <Toggle
274
- name={`${inputsPrefix}menuAttached`}
275
- onChange={onChange}
276
- disabled={!(data.isMenuAllowedLevel && data.parentAttachedToMenu)}
277
- value={get(form, `${inputsPrefix}menuAttached`, false)}
278
- />
279
- </Flex>
280
- </div>
281
- </div>
282
- <div className="row">
283
- <div className="col-lg-7 col-md-12">
284
- <Input
285
- error={get(formErrors, `${inputsPrefix}${pathSourceName}`)}
286
- label={getTradId(`popup.item.form.${pathSourceName}.label`)}
287
- name={`${inputsPrefix}${pathSourceName}`}
288
- onChange={onChange}
289
- placeholder={getTradId(`popup.item.form.${pathSourceName}.placeholder`)}
290
- description={generatePreviewPath()}
291
- type="text"
292
- validations={{ required: true }}
293
- value={get(form, `${inputsPrefix}${pathSourceName}`, '')}
294
- />
295
- </div>
296
- <div className="col-lg-5 col-md-12">
297
- <Label
298
- htmlFor={`${inputsPrefix}type`}
299
- message={formatMessage(getTrad('popup.item.form.type.label'))}
300
- />
301
- <Enumeration
302
- name={`${inputsPrefix}type`}
303
- onChange={onChange}
304
- options={typeSelectOptions}
305
- value={typeSelectValue}
306
- />
307
- </div>
308
- </div>
309
- {additionalFields.includes(navigationItemAdditionalFields.AUDIENCE) && (<div className="row">
310
- <div className="col-lg-12">
311
- <Label
312
- htmlFor={`${inputsPrefix}audience`}
313
- message={formatMessage(getTrad('popup.item.form.audience.label'))}
314
- />
315
- <Select
316
- name={`${inputsPrefix}audience`}
317
- onChange={onChange}
318
- options={audienceOptions}
319
- isMulti={true}
320
- value={audience}
321
- />
322
- </div>
323
- </div>)}
324
- {!isExternal && (
325
- <>
326
- <div className="row">
327
- <div className="col-lg-12">
328
- <hr />
329
- <Label
330
- message={formatMessage(getTrad('popup.item.form.relatedSection.label'))}
331
- />
332
- </div>
333
- </div>
334
- <div className="row">
335
- <div className="col-lg-6 col-md-12">
336
- <Label
337
- htmlFor={`${inputsPrefix}relatedType`}
338
- message={formatMessage(getTrad('popup.item.form.relatedType.label'))}
339
- />
340
- <Select
341
- name={`${inputsPrefix}relatedType`}
342
- error={get(formErrors, `${inputsPrefix}relatedType`)}
343
- onChange={onChangeRelatedType}
344
- options={relatedTypeSelectOptions}
345
- value={relatedTypeSelectValue}
346
- />
347
- </div>
348
- {relatedTypeSelectValue && !isSingleSelected && (
349
- <div className="col-lg-6 col-md-12">
350
- <Label
351
- htmlFor={relatedFieldName}
352
- message={formatMessage(getTrad('popup.item.form.related.label'))}
353
- />
354
- <Select
355
- name={relatedFieldName}
356
- error={get(formErrors, relatedFieldName)}
357
- onChange={onChange}
358
- onInputChange={debounceContentTypeSearchQuery}
359
- inputValue={contentTypeSearchInputValue}
360
- isLoading={isLoading}
361
- options={relatedSelectOptions}
362
- value={relatedSelectValue}
363
- />
364
- {!isLoading && thereAreNoMoreContentTypes && (
365
- <Text
366
- color="orange"
367
- fontSize="sm"
368
- >
369
- <FontAwesomeIcon icon={faInfoCircle} />{' '}
370
- {formatMessage(getTrad('popup.item.form.related.empty'), { contentTypeName: get(relatedTypeSelectValue, 'label') })}
371
- </Text>)}
372
- </div>
373
- )}
374
- </div>
375
- </>
376
- )}
377
- </section>
378
- </ModalBody>
379
- </ModalForm>
380
- <ModalFooter>
381
- <section>
382
- <Button
383
- onClick={handleRemove}
384
- color="delete"
385
- label={formatMessage(getTrad('popup.item.form.button.remove'))}
386
- />
387
- </section>
388
- <section>
389
- <ButtonModal
390
- onClick={handleSubmit}
391
- disabled={submitDisabled}
392
- message={getTradId(`popup.item.form.button.${
393
- form.viewId ? 'update' : 'create'
394
- }`)}
395
- />
396
- </section>
397
- </ModalFooter>
398
- </form>
399
- );
400
- };
401
-
402
- NavigationItemForm.defaultProps = {
403
- fieldsToDisable: [],
404
- formErrors: {},
405
- inputsPrefix: '',
406
- onSubmit: (e) => e.preventDefault(),
407
- requestError: null,
408
- };
409
-
410
- NavigationItemForm.propTypes = {
411
- isLoading: PropTypes.bool,
412
- fieldsToDisable: PropTypes.array,
413
- formErrors: PropTypes.object.isRequired,
414
- inputsPrefix: PropTypes.string,
415
- data: PropTypes.object.isRequired,
416
- onSubmit: PropTypes.func,
417
- requestError: PropTypes.object,
418
- contentTypes: PropTypes.array,
419
- contentTypeEntities: PropTypes.array,
420
- usedContentTypeEntities: PropTypes.array,
421
- availableAudience: PropTypes.array,
422
- additionalFields: PropTypes.array,
423
- getContentTypeEntities: PropTypes.func.isRequired,
424
- appendLabelPublicationStatus: PropTypes.func,
425
- };
426
-
427
- export default NavigationItemForm;
@@ -1,6 +0,0 @@
1
- import styled from "styled-components";
2
- const { Modal } = require("strapi-helper-plugin");
3
-
4
- export const MediumPopup = styled(Modal)`
5
- width: 72rem;
6
- `;
@@ -1,240 +0,0 @@
1
- /*
2
- *
3
- * Navigation View
4
- *
5
- */
6
-
7
- import React, { memo, useMemo, useState } from 'react';
8
- import { FormattedMessage, useIntl } from "react-intl";
9
- import { LoadingIndicatorPage } from "strapi-helper-plugin";
10
- import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
11
- import { faHamburger, faPlus } from "@fortawesome/free-solid-svg-icons";
12
- import useDataManager from "../../hooks/useDataManager";
13
- import { isEmpty, get } from "lodash";
14
- import { Header } from "@buffetjs/custom";
15
- import { Button, HeaderActions, Select } from "@buffetjs/core";
16
- import Wrapper from "../View/Wrapper";
17
- import EmptyView from "../../components/EmptyView";
18
- import HeaderForm from "./HeaderForm";
19
- import HeaderFormCell from "./HeaderFormCell";
20
- import NavigationItemPopUp from "./components/NavigationItemPopup";
21
- import List from "../../components/List";
22
- import {
23
- transformItemToViewPayload,
24
- transformToRESTPayload,
25
- usedContentTypes,
26
- validateNavigationStructure,
27
- } from './utils/parsers';
28
- import FadedWrapper from "./FadedWrapper";
29
- import { getTrad, getTradId } from '../../translations';
30
-
31
- const View = () => {
32
- const {
33
- items: availableNavigations,
34
- activeItem: activeNavigation,
35
- changedActiveItem: changedActiveNavigation,
36
- config,
37
- navigationItemPopupOpened,
38
- isLoading,
39
- isLoadingForAdditionalDataToBeSet,
40
- isLoadingForSubmit,
41
- handleChangeNavigationItemPopupVisibility,
42
- handleChangeSelection,
43
- handleChangeNavigationData,
44
- handleResetNavigationData,
45
- handleSubmitNavigation,
46
- getContentTypeItems,
47
- error
48
- } = useDataManager();
49
- const [activeNavigationItem, setActiveNavigationItemState] = useState({});
50
- const { formatMessage } = useIntl();
51
-
52
- const options = availableNavigations.map((item) => ({
53
- value: item.id,
54
- label: item.name,
55
- }));
56
-
57
- const structureHasErrors = !validateNavigationStructure((changedActiveNavigation || {}).items);
58
- const navigationSelectValue = get(activeNavigation, "id", null);
59
- const actions = [
60
- {
61
- label: formatMessage(getTrad('submit.cta.cancel')),
62
- onClick: () => isLoadingForSubmit ? null : handleResetNavigationData(),
63
- color: "cancel",
64
- type: "button",
65
- },
66
- {
67
- label: formatMessage(getTrad('submit.cta.save')),
68
- onClick: () =>
69
- isLoadingForSubmit || structureHasErrors ? null : handleSubmitNavigation(formatMessage, transformToRESTPayload(changedActiveNavigation, config)),
70
- color: "success",
71
- type: "submit",
72
- isLoading: isLoadingForSubmit,
73
- disabled: structureHasErrors,
74
- },
75
- ];
76
-
77
- const pullUsedContentTypeItem = (items = []) =>
78
- items.reduce((prev, curr) =>
79
- [...prev, curr.relatedRef ? {
80
- __collectionName: curr.relatedRef.__collectionName,
81
- id: curr.relatedRef.id
82
- } : undefined, ...pullUsedContentTypeItem(curr.items)].filter(item => item)
83
- , []);
84
- const usedContentTypeItems = pullUsedContentTypeItem((changedActiveNavigation || {}).items);
85
-
86
- const changeNavigationItemPopupState = (visible, editedItem = {}) => {
87
- setActiveNavigationItemState(editedItem);
88
- handleChangeNavigationItemPopupVisibility(visible);
89
- };
90
-
91
- const addNewNavigationItem = (
92
- e,
93
- viewId = null,
94
- isMenuAllowedLevel = true,
95
- levelPath = '',
96
- parentAttachedToMenu = true,
97
- ) => {
98
- e.preventDefault();
99
- e.stopPropagation();
100
- changeNavigationItemPopupState(true, {
101
- viewParentId: viewId,
102
- isMenuAllowedLevel,
103
- levelPath,
104
- parentAttachedToMenu,
105
- });
106
- };
107
-
108
- const editNavigationItem = (
109
- e,
110
- item,
111
- levelPath = '',
112
- parentAttachedToMenu = true,
113
- ) => {
114
- e.preventDefault();
115
- e.stopPropagation();
116
- changeNavigationItemPopupState(true, {
117
- ...item,
118
- levelPath,
119
- parentAttachedToMenu,
120
- });
121
- };
122
-
123
- const restoreNavigationItem = (e, item) => {
124
- e.preventDefault();
125
- e.stopPropagation();
126
-
127
- handleSubmitNavigationItem({
128
- ...item,
129
- removed: false,
130
- });
131
- };
132
-
133
- const reOrderNavigationItem = (e, item, moveBy = 0) => {
134
- e.preventDefault();
135
- e.stopPropagation();
136
-
137
- handleSubmitNavigationItem({
138
- ...item,
139
- order: item.order + (moveBy * 1.5),
140
- });
141
- }
142
-
143
- const onPopUpClose = (e) => {
144
- e.preventDefault();
145
- e.stopPropagation();
146
- changeNavigationItemPopupState(false);
147
- };
148
-
149
- const handleSubmitNavigationItem = (payload) => {
150
- const changedStructure = {
151
- ...changedActiveNavigation,
152
- items: transformItemToViewPayload(payload, changedActiveNavigation.items, config),
153
- };
154
- handleChangeNavigationData(changedStructure, true);
155
- };
156
-
157
- const usedContentTypesData = useMemo(
158
- () => changedActiveNavigation ? usedContentTypes(changedActiveNavigation.items) : [],
159
- [changedActiveNavigation],
160
- );
161
-
162
- return (
163
- <div className="container-fluid">
164
- <div className="row">
165
- <Wrapper className="col-md-12">
166
- <HeaderForm>
167
- <HeaderFormCell>
168
- <Header
169
- title={{
170
- label: formatMessage(getTrad('header.title')),
171
- }}
172
- content={formatMessage(getTrad('header.description'))}
173
- />
174
- </HeaderFormCell>
175
- { options && (options.length > 1) && (<HeaderFormCell>
176
- <Select
177
- name="activeNavigation"
178
- onChange={({ target: { value } }) =>
179
- handleChangeSelection(value)
180
- }
181
- options={options}
182
- value={navigationSelectValue}
183
- />
184
- </HeaderFormCell>) }
185
- <HeaderFormCell align="right" fill>
186
- <HeaderActions actions={actions} />
187
- </HeaderFormCell>
188
- </HeaderForm>
189
- <FadedWrapper>
190
- {isLoading && <LoadingIndicatorPage />}
191
- {changedActiveNavigation && (
192
- <>
193
- {isEmpty(changedActiveNavigation.items || []) && (
194
- <EmptyView>
195
- <FontAwesomeIcon icon={faHamburger} size="5x" />
196
- <FormattedMessage id={getTradId('empty')} />
197
- <Button
198
- color="primary"
199
- icon={<FontAwesomeIcon icon={faPlus} />}
200
- label={formatMessage(getTrad('empty.cta'))}
201
- onClick={addNewNavigationItem}
202
- />
203
- </EmptyView>
204
- )}
205
- {!isEmpty(changedActiveNavigation.items || []) && (
206
- <List
207
- items={changedActiveNavigation.items || []}
208
- onItemClick={editNavigationItem}
209
- onItemReOrder={reOrderNavigationItem}
210
- onItemRestoreClick={restoreNavigationItem}
211
- onItemLevelAddClick={addNewNavigationItem}
212
- root
213
- error={error}
214
- allowedLevels={config.allowedLevels}
215
- contentTypes={config.contentTypes}
216
- isParentAttachedToMenu={true}
217
- contentTypesNameFields={config.contentTypesNameFields}
218
- />
219
- )}
220
- </>
221
- )}
222
- </FadedWrapper>
223
- <NavigationItemPopUp
224
- isOpen={navigationItemPopupOpened}
225
- isLoading={isLoadingForAdditionalDataToBeSet}
226
- data={activeNavigationItem}
227
- config={config}
228
- usedContentTypesData={usedContentTypesData}
229
- usedContentTypeItems={usedContentTypeItems}
230
- getContentTypeItems={getContentTypeItems}
231
- onSubmit={handleSubmitNavigationItem}
232
- onClose={onPopUpClose}
233
- />
234
- </Wrapper>
235
- </div>
236
- </div>
237
- );
238
- };
239
-
240
- export default memo(View);
@@ -1,3 +0,0 @@
1
- function lifecycles() {}
2
-
3
- export default lifecycles;
@@ -1,14 +0,0 @@
1
- const pluginPermissions = {
2
- // This permission regards the main component (App) and is used to tell
3
- // If the plugin link should be displayed in the menu
4
- // And also if the plugin is accessible. This use case is found when a user types the url of the
5
- // plugin directly in the browser
6
- main: [
7
- { action: "plugins::navigation.read", subject: null },
8
- { action: "plugins::navigation.update", subject: null },
9
- ],
10
- open: [{ action: "plugins::navigation.read", subject: null }],
11
- update: [{ action: "plugins::navigation.update", subject: null }],
12
- };
13
-
14
- export default pluginPermissions;