@akemona-org/strapi-plugin-content-type-builder 3.7.0

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 (223) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +19 -0
  3. package/admin/src/InjectedComponents/ContentManager/EditSettingViewButton.js +76 -0
  4. package/admin/src/InjectedComponents/ContentManager/EditViewLink.js +44 -0
  5. package/admin/src/assets/images/logo.svg +1 -0
  6. package/admin/src/components/AllowedTypesSelect/MenuList.js +115 -0
  7. package/admin/src/components/AllowedTypesSelect/Text.js +26 -0
  8. package/admin/src/components/AllowedTypesSelect/index.js +55 -0
  9. package/admin/src/components/AttributeOption/Button.js +50 -0
  10. package/admin/src/components/AttributeOption/Card.js +26 -0
  11. package/admin/src/components/AttributeOption/index.js +127 -0
  12. package/admin/src/components/BooleanBox/CT.js +21 -0
  13. package/admin/src/components/BooleanBox/Enumeration.js +10 -0
  14. package/admin/src/components/BooleanBox/EnumerationWrapper.js +14 -0
  15. package/admin/src/components/BooleanBox/Label.js +9 -0
  16. package/admin/src/components/BooleanBox/ST.js +21 -0
  17. package/admin/src/components/BooleanBox/Wrapper.js +75 -0
  18. package/admin/src/components/BooleanBox/icons/CTSelected.js +136 -0
  19. package/admin/src/components/BooleanBox/icons/CTUnselected.js +136 -0
  20. package/admin/src/components/BooleanBox/icons/STSelected.js +80 -0
  21. package/admin/src/components/BooleanBox/icons/STUnselected.js +76 -0
  22. package/admin/src/components/BooleanBox/index.js +103 -0
  23. package/admin/src/components/CheckboxWithDescription/Wrapper.js +12 -0
  24. package/admin/src/components/CheckboxWithDescription/index.js +40 -0
  25. package/admin/src/components/ComponentCard/Close.js +12 -0
  26. package/admin/src/components/ComponentCard/Wrapper.js +84 -0
  27. package/admin/src/components/ComponentCard/index.js +62 -0
  28. package/admin/src/components/ComponentIconPicker/Cell.js +40 -0
  29. package/admin/src/components/ComponentIconPicker/CellRenderer.js +36 -0
  30. package/admin/src/components/ComponentIconPicker/Search.js +12 -0
  31. package/admin/src/components/ComponentIconPicker/SearchWrapper.js +34 -0
  32. package/admin/src/components/ComponentIconPicker/Wrapper.js +60 -0
  33. package/admin/src/components/ComponentIconPicker/index.js +143 -0
  34. package/admin/src/components/ComponentList/index.js +78 -0
  35. package/admin/src/components/ComponentSelect/Category.js +20 -0
  36. package/admin/src/components/ComponentSelect/CategoryName.js +11 -0
  37. package/admin/src/components/ComponentSelect/MenuList.js +110 -0
  38. package/admin/src/components/ComponentSelect/MultipleMenuList.js +271 -0
  39. package/admin/src/components/ComponentSelect/Value.js +83 -0
  40. package/admin/src/components/ComponentSelect/index.js +83 -0
  41. package/admin/src/components/ComponentSelect/utils/HasSomeSubArray.js +5 -0
  42. package/admin/src/components/ComponentSelect/utils/hasSubArray.js +5 -0
  43. package/admin/src/components/CreatableSelect/index.js +58 -0
  44. package/admin/src/components/CustomCheckbox/StyledCustomCheckbox.js +25 -0
  45. package/admin/src/components/CustomCheckbox/index.js +71 -0
  46. package/admin/src/components/CustomLink/P.js +17 -0
  47. package/admin/src/components/CustomLink/StyledCustomLink.js +20 -0
  48. package/admin/src/components/CustomLink/index.js +31 -0
  49. package/admin/src/components/DynamicZoneList/ComponentButton.js +39 -0
  50. package/admin/src/components/DynamicZoneList/index.js +124 -0
  51. package/admin/src/components/HeaderModalNavContainer/index.js +25 -0
  52. package/admin/src/components/HeaderNavLink/Wrapper.js +21 -0
  53. package/admin/src/components/HeaderNavLink/index.js +50 -0
  54. package/admin/src/components/List/List.js +131 -0
  55. package/admin/src/components/List/index.js +295 -0
  56. package/admin/src/components/ListButton/index.js +9 -0
  57. package/admin/src/components/ListHeader/Title.js +19 -0
  58. package/admin/src/components/ListHeader/Wrapper.js +24 -0
  59. package/admin/src/components/ListHeader/index.js +37 -0
  60. package/admin/src/components/ListRow/Wrapper.js +76 -0
  61. package/admin/src/components/ListRow/index.js +334 -0
  62. package/admin/src/components/ModalHeader/ComponentIcon.js +21 -0
  63. package/admin/src/components/ModalHeader/ComponentInfos.js +22 -0
  64. package/admin/src/components/ModalHeader/ComponentInfosWrapper.js +9 -0
  65. package/admin/src/components/ModalHeader/DropdownInfos.js +50 -0
  66. package/admin/src/components/ModalHeader/Icon.js +26 -0
  67. package/admin/src/components/ModalHeader/IconWrapper.js +11 -0
  68. package/admin/src/components/ModalHeader/Item.js +19 -0
  69. package/admin/src/components/ModalHeader/Menu.js +8 -0
  70. package/admin/src/components/ModalHeader/Toggle.js +14 -0
  71. package/admin/src/components/ModalHeader/Wrapper.js +12 -0
  72. package/admin/src/components/ModalHeader/index.js +105 -0
  73. package/admin/src/components/RelationForm/Wrapper.js +9 -0
  74. package/admin/src/components/RelationForm/index.js +66 -0
  75. package/admin/src/components/RelationFormBox/Wrapper.js +34 -0
  76. package/admin/src/components/RelationFormBox/index.js +85 -0
  77. package/admin/src/components/RelationFormNaturePicker/Wrapper.js +53 -0
  78. package/admin/src/components/RelationFormNaturePicker/index.js +112 -0
  79. package/admin/src/components/RelationTargetPicker/Item.js +62 -0
  80. package/admin/src/components/RelationTargetPicker/Wrapper.js +89 -0
  81. package/admin/src/components/RelationTargetPicker/index.js +88 -0
  82. package/admin/src/components/SelectCheckbox/index.js +8 -0
  83. package/admin/src/components/SelectMenuSubUl/ToggleUl.js +23 -0
  84. package/admin/src/components/SelectMenuSubUl/index.js +23 -0
  85. package/admin/src/components/SelectMenuUl/index.js +66 -0
  86. package/admin/src/components/Td/index.js +37 -0
  87. package/admin/src/components/UpperFirst/index.js +14 -0
  88. package/admin/src/components/WrapperSelect/index.js +89 -0
  89. package/admin/src/containers/App/Wrapper.js +14 -0
  90. package/admin/src/containers/App/index.js +39 -0
  91. package/admin/src/containers/App/utils/icons.json +962 -0
  92. package/admin/src/containers/DataManagerProvider/constants.js +15 -0
  93. package/admin/src/containers/DataManagerProvider/index.js +610 -0
  94. package/admin/src/containers/DataManagerProvider/reducer.js +461 -0
  95. package/admin/src/containers/DataManagerProvider/selectors.js +25 -0
  96. package/admin/src/containers/DataManagerProvider/utils/cleanData.js +167 -0
  97. package/admin/src/containers/DataManagerProvider/utils/createDataObject.js +8 -0
  98. package/admin/src/containers/DataManagerProvider/utils/createModifiedDataSchema.js +91 -0
  99. package/admin/src/containers/DataManagerProvider/utils/retrieveComponentsFromSchema.js +55 -0
  100. package/admin/src/containers/DataManagerProvider/utils/retrieveComponentsThatHaveComponents.js +35 -0
  101. package/admin/src/containers/DataManagerProvider/utils/retrieveNestedComponents.js +34 -0
  102. package/admin/src/containers/DataManagerProvider/utils/retrieveSpecificInfoFromComponents.js +12 -0
  103. package/admin/src/containers/FormModal/CustomButton.js +12 -0
  104. package/admin/src/containers/FormModal/attributes/advancedForm.js +269 -0
  105. package/admin/src/containers/FormModal/attributes/attributeOptions.js +99 -0
  106. package/admin/src/containers/FormModal/attributes/baseForm.js +322 -0
  107. package/admin/src/containers/FormModal/attributes/commonBaseForm.js +7 -0
  108. package/admin/src/containers/FormModal/attributes/form.js +9 -0
  109. package/admin/src/containers/FormModal/attributes/index.js +4 -0
  110. package/admin/src/containers/FormModal/attributes/nameField.js +19 -0
  111. package/admin/src/containers/FormModal/attributes/types.js +309 -0
  112. package/admin/src/containers/FormModal/attributes/uiHelpers.js +11 -0
  113. package/admin/src/containers/FormModal/attributes/validation/common.js +151 -0
  114. package/admin/src/containers/FormModal/category/createCategorySchema.js +28 -0
  115. package/admin/src/containers/FormModal/category/form.js +27 -0
  116. package/admin/src/containers/FormModal/category/index.js +3 -0
  117. package/admin/src/containers/FormModal/category/regex.js +3 -0
  118. package/admin/src/containers/FormModal/component/componentField.js +25 -0
  119. package/admin/src/containers/FormModal/component/createComponentSchema.js +49 -0
  120. package/admin/src/containers/FormModal/component/form.js +67 -0
  121. package/admin/src/containers/FormModal/component/index.js +4 -0
  122. package/admin/src/containers/FormModal/constants.js +11 -0
  123. package/admin/src/containers/FormModal/contentType/createContentTypeSchema.js +44 -0
  124. package/admin/src/containers/FormModal/contentType/form.js +116 -0
  125. package/admin/src/containers/FormModal/contentType/index.js +2 -0
  126. package/admin/src/containers/FormModal/dynamicZone/form.js +45 -0
  127. package/admin/src/containers/FormModal/dynamicZone/index.js +2 -0
  128. package/admin/src/containers/FormModal/forms/index.js +164 -0
  129. package/admin/src/containers/FormModal/index.js +1552 -0
  130. package/admin/src/containers/FormModal/reducer.js +293 -0
  131. package/admin/src/containers/FormModal/selectors.js +24 -0
  132. package/admin/src/containers/FormModal/utils/canEditContentType.js +19 -0
  133. package/admin/src/containers/FormModal/utils/createHeadersArray.js +37 -0
  134. package/admin/src/containers/FormModal/utils/createHeadersObjectFromArray.js +27 -0
  135. package/admin/src/containers/FormModal/utils/createUid.js +17 -0
  136. package/admin/src/containers/FormModal/utils/getAttributesToDisplay.js +35 -0
  137. package/admin/src/containers/FormModal/utils/getModalTitleSubHeader.js +25 -0
  138. package/admin/src/containers/FormModal/utils/getNextSearch.js +17 -0
  139. package/admin/src/containers/FormModal/utils/index.js +6 -0
  140. package/admin/src/containers/FormModal/utils/relations.js +9 -0
  141. package/admin/src/containers/FormModal/utils/staticData.js +43 -0
  142. package/admin/src/containers/Initializer/index.js +26 -0
  143. package/admin/src/containers/LeftMenu/Wrapper.js +14 -0
  144. package/admin/src/containers/LeftMenu/index.js +220 -0
  145. package/admin/src/containers/ListView/Wrapper.js +16 -0
  146. package/admin/src/containers/ListView/index.js +322 -0
  147. package/admin/src/containers/NotFoundPage/index.js +20 -0
  148. package/admin/src/containers/RecursivePath/index.js +22 -0
  149. package/admin/src/contexts/DataManagerContext.js +5 -0
  150. package/admin/src/contexts/ListViewContext.js +5 -0
  151. package/admin/src/hooks/useDataManager.js +6 -0
  152. package/admin/src/hooks/useListView.js +6 -0
  153. package/admin/src/icons/Curve.js +26 -0
  154. package/admin/src/icons/ManyToMany.js +98 -0
  155. package/admin/src/icons/ManyToOne.js +64 -0
  156. package/admin/src/icons/ManyWay.js +36 -0
  157. package/admin/src/icons/OneToMany.js +65 -0
  158. package/admin/src/icons/OneToOne.js +47 -0
  159. package/admin/src/icons/OneWay.js +38 -0
  160. package/admin/src/index.js +83 -0
  161. package/admin/src/lifecycles.js +13 -0
  162. package/admin/src/permissions.js +9 -0
  163. package/admin/src/pluginId.js +5 -0
  164. package/admin/src/reducers.js +10 -0
  165. package/admin/src/translations/ar.json +48 -0
  166. package/admin/src/translations/cs.json +151 -0
  167. package/admin/src/translations/de.json +187 -0
  168. package/admin/src/translations/dk.json +181 -0
  169. package/admin/src/translations/en.json +188 -0
  170. package/admin/src/translations/es.json +188 -0
  171. package/admin/src/translations/fr.json +87 -0
  172. package/admin/src/translations/id.json +187 -0
  173. package/admin/src/translations/index.js +51 -0
  174. package/admin/src/translations/it.json +187 -0
  175. package/admin/src/translations/ja.json +50 -0
  176. package/admin/src/translations/ko.json +57 -0
  177. package/admin/src/translations/ms.json +179 -0
  178. package/admin/src/translations/nl.json +171 -0
  179. package/admin/src/translations/pl.json +152 -0
  180. package/admin/src/translations/pt-BR.json +51 -0
  181. package/admin/src/translations/pt.json +51 -0
  182. package/admin/src/translations/ru.json +188 -0
  183. package/admin/src/translations/sk.json +186 -0
  184. package/admin/src/translations/th.json +181 -0
  185. package/admin/src/translations/tr.json +51 -0
  186. package/admin/src/translations/uk.json +180 -0
  187. package/admin/src/translations/zh-Hans.json +188 -0
  188. package/admin/src/translations/zh.json +178 -0
  189. package/admin/src/utils/convertAttrObjToArray.js +7 -0
  190. package/admin/src/utils/formAPI.js +112 -0
  191. package/admin/src/utils/getAttributeDisplayedType.js +31 -0
  192. package/admin/src/utils/getComponents.js +31 -0
  193. package/admin/src/utils/getTrad.js +5 -0
  194. package/admin/src/utils/makeSearch.js +15 -0
  195. package/admin/src/utils/makeUnique.js +3 -0
  196. package/config/functions/bootstrap.js +14 -0
  197. package/config/routes.json +144 -0
  198. package/controllers/Builder.js +7 -0
  199. package/controllers/ComponentCategories.js +43 -0
  200. package/controllers/Components.js +152 -0
  201. package/controllers/Connections.js +9 -0
  202. package/controllers/ContentTypes.js +143 -0
  203. package/controllers/validation/common.js +118 -0
  204. package/controllers/validation/component-category.js +21 -0
  205. package/controllers/validation/component.js +89 -0
  206. package/controllers/validation/content-type.js +161 -0
  207. package/controllers/validation/data-transform.js +32 -0
  208. package/controllers/validation/model-schema.js +91 -0
  209. package/controllers/validation/relations.js +56 -0
  210. package/controllers/validation/types.js +277 -0
  211. package/package.json +63 -0
  212. package/services/Builder.js +7 -0
  213. package/services/ComponentCategories.js +88 -0
  214. package/services/Components.js +103 -0
  215. package/services/ContentTypes.js +263 -0
  216. package/services/api-handler.js +158 -0
  217. package/services/constants.js +59 -0
  218. package/services/schema-builder/component-builder.js +137 -0
  219. package/services/schema-builder/content-type-builder.js +256 -0
  220. package/services/schema-builder/index.js +196 -0
  221. package/services/schema-builder/schema-handler.js +297 -0
  222. package/utils/attributes.js +154 -0
  223. package/utils/helpers.js +31 -0
@@ -0,0 +1,1552 @@
1
+ import React, { useCallback, useEffect, useRef, useState, useMemo } from 'react';
2
+ import {
3
+ HeaderModal,
4
+ HeaderModalTitle,
5
+ Modal,
6
+ ModalBody,
7
+ ModalFooter,
8
+ ModalForm,
9
+ PopUpWarning,
10
+ getYupInnerErrors,
11
+ useGlobalContext,
12
+ useQuery,
13
+ useStrapi,
14
+ InputsIndex,
15
+ } from 'strapi-helper-plugin';
16
+ import { Button, Text, Padded } from '@buffetjs/core';
17
+ import { Inputs } from '@buffetjs/custom';
18
+ import { useHistory, useLocation } from 'react-router-dom';
19
+ import { FormattedMessage } from 'react-intl';
20
+ import { get, has, isEmpty, set, toLower, toString, upperFirst } from 'lodash';
21
+ import { useSelector, useDispatch, shallowEqual } from 'react-redux';
22
+ import pluginId from '../../pluginId';
23
+ import useDataManager from '../../hooks/useDataManager';
24
+ import AttributeOption from '../../components/AttributeOption';
25
+ import BooleanBox from '../../components/BooleanBox';
26
+ import ComponentIconPicker from '../../components/ComponentIconPicker';
27
+ import CheckboxWithDescription from '../../components/CheckboxWithDescription';
28
+ import CustomCheckbox from '../../components/CustomCheckbox';
29
+ import ModalHeader from '../../components/ModalHeader';
30
+ import HeaderModalNavContainer from '../../components/HeaderModalNavContainer';
31
+ import RelationForm from '../../components/RelationForm';
32
+ import HeaderNavLink from '../../components/HeaderNavLink';
33
+ import WrapperSelect from '../../components/WrapperSelect';
34
+ import getTrad from '../../utils/getTrad';
35
+ import makeSearch from '../../utils/makeSearch';
36
+ import {
37
+ canEditContentType,
38
+ createHeadersArray,
39
+ createHeadersObjectFromArray,
40
+ getAttributesToDisplay,
41
+ getModalTitleSubHeader,
42
+ getNextSearch,
43
+ } from './utils';
44
+ import forms from './forms';
45
+ import { createComponentUid, createUid } from './utils/createUid';
46
+ import { NAVLINKS, INITIAL_STATE_DATA } from './utils/staticData';
47
+ import CustomButton from './CustomButton';
48
+ import makeSelectFormModal from './selectors';
49
+ import {
50
+ SET_DATA_TO_EDIT,
51
+ SET_DYNAMIC_ZONE_DATA_SCHEMA,
52
+ SET_ATTRIBUTE_DATA_SCHEMA,
53
+ ADD_COMPONENTS_TO_DYNAMIC_ZONE,
54
+ ON_CHANGE_ALLOWED_TYPE,
55
+ SET_ERRORS,
56
+ ON_CHANGE,
57
+ RESET_PROPS_AND_SET_THE_FORM_FOR_ADDING_A_COMPO_TO_A_DZ,
58
+ RESET_PROPS_AND_SET_FORM_FOR_ADDING_AN_EXISTING_COMPO,
59
+ RESET_PROPS_AND_SAVE_CURRENT_DATA,
60
+ RESET_PROPS,
61
+ } from './constants';
62
+
63
+ /* eslint-disable indent */
64
+ /* eslint-disable react/no-array-index-key */
65
+
66
+ const FormModal = () => {
67
+ const [state, setState] = useState(INITIAL_STATE_DATA);
68
+ const [showConfirmModal, setShowConfirmModal] = useState(false);
69
+ const formModalSelector = useMemo(makeSelectFormModal, []);
70
+ const dispatch = useDispatch();
71
+ const reducerState = useSelector(state => formModalSelector(state), shallowEqual);
72
+ const { push } = useHistory();
73
+ const { search } = useLocation();
74
+ const { emitEvent, formatMessage } = useGlobalContext();
75
+ const {
76
+ strapi: { getPlugin },
77
+ } = useStrapi();
78
+ const ctbPlugin = getPlugin(pluginId);
79
+ const ctbFormsAPI = ctbPlugin.apis.forms;
80
+ const inputsFromPlugins = ctbFormsAPI.components.inputs;
81
+
82
+ const query = useQuery();
83
+ const attributeOptionRef = useRef();
84
+
85
+ const {
86
+ addAttribute,
87
+ addCreatedComponentToDynamicZone,
88
+ allComponentsCategories,
89
+ changeDynamicZoneComponents,
90
+ contentTypes,
91
+ components,
92
+ createSchema,
93
+ deleteCategory,
94
+ deleteData,
95
+ editCategory,
96
+ submitData,
97
+ modifiedData: allDataSchema,
98
+ nestedComponents,
99
+ setModifiedData,
100
+ sortedContentTypesList,
101
+ updateSchema,
102
+ reservedNames,
103
+ } = useDataManager();
104
+
105
+ const {
106
+ componentToCreate,
107
+ formErrors,
108
+ initialData,
109
+ isCreatingComponentWhileAddingAField,
110
+ modifiedData,
111
+ } = reducerState.toJS();
112
+
113
+ useEffect(() => {
114
+ if (!isEmpty(search)) {
115
+ const actionType = query.get('actionType');
116
+ // Returns 'null' if there isn't any attributeType search params
117
+ const attributeName = query.get('attributeName');
118
+ const attributeType = query.get('attributeType');
119
+ const dynamicZoneTarget = query.get('dynamicZoneTarget');
120
+ const forTarget = query.get('forTarget');
121
+ const modalType = query.get('modalType');
122
+ const kind = query.get('kind') || get(allDataSchema, ['contentType', 'schema', 'kind'], null);
123
+ const targetUid = query.get('targetUid');
124
+ const settingType = query.get('settingType');
125
+ const headerId = query.get('headerId');
126
+ const header_label_1 = query.get('header_label_1');
127
+ const header_icon_name_1 = query.get('header_icon_name_1');
128
+ const header_icon_isCustom_1 = query.get('header_icon_isCustom_1');
129
+ const header_info_category_1 = query.get('header_info_category_1');
130
+ const header_info_name_1 = query.get('header_info_name_1');
131
+ const header_label_2 = query.get('header_label_2');
132
+ const header_icon_name_2 = query.get('header_icon_name_2');
133
+ const header_icon_isCustom_2 = query.get('header_icon_isCustom_2');
134
+ const header_info_category_2 = query.get('header_info_category_2');
135
+ const header_info_name_2 = query.get('header_info_name_2');
136
+ const header_label_3 = query.get('header_label_3');
137
+ const header_icon_name_3 = query.get('header_icon_name_3');
138
+ const header_icon_isCustom_3 = query.get('header_icon_isCustom_3');
139
+ const header_info_category_3 = query.get('header_info_category_3');
140
+ const header_info_name_3 = query.get('header_info_name_3');
141
+ const header_label_4 = query.get('header_label_4');
142
+ const header_icon_name_4 = query.get('header_icon_name_4');
143
+ const header_icon_isCustom_4 = query.get('header_icon_isCustom_4');
144
+ const header_info_category_4 = query.get('header_info_category_4');
145
+ const header_info_name_4 = query.get('header_info_name_4');
146
+ const header_label_5 = query.get('header_label_5');
147
+ const header_icon_name_5 = query.get('header_icon_name_5');
148
+ const header_icon_isCustom_5 = query.get('header_icon_isCustom_5');
149
+ const header_info_category_5 = query.get('header_info_category_5');
150
+ const header_info_name_5 = query.get('header_info_name_5');
151
+ const step = query.get('step');
152
+ const pathToSchema =
153
+ forTarget === 'contentType' || forTarget === 'component'
154
+ ? [forTarget]
155
+ : [forTarget, targetUid];
156
+
157
+ setState({
158
+ actionType,
159
+ attributeName,
160
+ attributeType,
161
+ kind,
162
+ dynamicZoneTarget,
163
+ forTarget,
164
+ modalType,
165
+ pathToSchema,
166
+ settingType,
167
+ step,
168
+ targetUid,
169
+ header_label_1,
170
+ header_icon_name_1,
171
+ header_icon_isCustom_1,
172
+ header_info_name_1,
173
+ header_info_category_1,
174
+ header_label_2,
175
+ header_icon_name_2,
176
+ header_icon_isCustom_2,
177
+ header_info_name_2,
178
+ header_info_category_2,
179
+ header_label_3,
180
+ header_icon_name_3,
181
+ header_icon_isCustom_3,
182
+ header_info_name_3,
183
+ header_info_category_3,
184
+ header_label_4,
185
+ header_icon_name_4,
186
+ header_icon_isCustom_4,
187
+ header_info_name_4,
188
+ header_info_category_4,
189
+ header_label_5,
190
+ header_icon_name_5,
191
+ header_icon_isCustom_5,
192
+ header_info_name_5,
193
+ header_info_category_5,
194
+ headerId,
195
+ });
196
+
197
+ const collectionTypesForRelation = sortedContentTypesList.filter(
198
+ ({ kind }) => kind === 'collectionType'
199
+ );
200
+
201
+ // Reset all the modification when opening the edit category modal
202
+ if (modalType === 'editCategory') {
203
+ setModifiedData();
204
+ }
205
+
206
+ if (actionType === 'edit' && modalType === 'attribute' && forTarget === 'contentType') {
207
+ emitEvent('willEditFieldOfContentType');
208
+ }
209
+
210
+ // Case:
211
+ // the user opens the modal chooseAttributes
212
+ // selects dynamic zone => set the field name
213
+ // then goes to step 1 (the modal is addComponentToDynamicZone) and finally reloads the app.
214
+ // In this particular if the user tries to add components to the zone it will pop an error since the dz is unknown
215
+ const shouldCloseModalToPreventErrorWhenCreatingADZ =
216
+ get(allDataSchema, [...pathToSchema, 'schema', 'attributes', dynamicZoneTarget], null) ===
217
+ null && modalType === 'addComponentToDynamicZone';
218
+
219
+ // Similarly when creating a component on the fly if the user reloads the app
220
+ // The previous data is lost
221
+ // Since the modal uses the search it will still be opened
222
+ const shouldCloseModalChooseAttribute =
223
+ get(allDataSchema, ['components', targetUid], null) === null &&
224
+ // modalType === 'chooseAttribute' &&
225
+ forTarget === 'components';
226
+
227
+ if (shouldCloseModalToPreventErrorWhenCreatingADZ || shouldCloseModalChooseAttribute) {
228
+ push({ search: '' });
229
+ }
230
+
231
+ // Edit category
232
+ if (modalType === 'editCategory' && actionType === 'edit') {
233
+ dispatch({
234
+ type: SET_DATA_TO_EDIT,
235
+ modalType,
236
+ actionType,
237
+ data: {
238
+ name: query.get('categoryName'),
239
+ },
240
+ });
241
+ }
242
+
243
+ // Create content type we need to add the default option draftAndPublish
244
+ if (
245
+ modalType === 'contentType' &&
246
+ state.modalType !== 'contentType' && // Prevent setting the data structure when navigating from one tab to another
247
+ actionType === 'create'
248
+ ) {
249
+ dispatch({
250
+ type: SET_DATA_TO_EDIT,
251
+ modalType,
252
+ actionType,
253
+ data: {
254
+ draftAndPublish: true,
255
+ },
256
+ pluginOptions: {},
257
+ });
258
+ }
259
+
260
+ // Edit content type
261
+ if (
262
+ modalType === 'contentType' &&
263
+ state.modalType !== 'contentType' &&
264
+ actionType === 'edit'
265
+ ) {
266
+ const { name, collectionName, draftAndPublish, kind, pluginOptions } = get(
267
+ allDataSchema,
268
+ [...pathToSchema, 'schema'],
269
+ {
270
+ name: null,
271
+ collectionName: null,
272
+ pluginOptions: {},
273
+ }
274
+ );
275
+
276
+ dispatch({
277
+ type: SET_DATA_TO_EDIT,
278
+ actionType,
279
+ modalType,
280
+ data: {
281
+ name,
282
+ collectionName,
283
+ draftAndPublish,
284
+ kind,
285
+ pluginOptions,
286
+ },
287
+ });
288
+ }
289
+
290
+ // Edit component
291
+ if (modalType === 'component' && state.modalType !== 'component' && actionType === 'edit') {
292
+ const data = get(allDataSchema, pathToSchema, {});
293
+
294
+ dispatch({
295
+ type: SET_DATA_TO_EDIT,
296
+ actionType,
297
+ modalType,
298
+ data: {
299
+ name: data.schema.name,
300
+ category: data.category,
301
+ icon: data.schema.icon,
302
+ collectionName: data.schema.collectionName,
303
+ },
304
+ });
305
+ }
306
+
307
+ // Special case for the dynamic zone
308
+ if (
309
+ modalType === 'addComponentToDynamicZone' &&
310
+ state.modalType !== 'addComponentToDynamicZone' &&
311
+ actionType === 'edit'
312
+ ) {
313
+ const attributeToEditNotFormatted = get(
314
+ allDataSchema,
315
+ [...pathToSchema, 'schema', 'attributes', dynamicZoneTarget],
316
+ {}
317
+ );
318
+ const attributeToEdit = {
319
+ ...attributeToEditNotFormatted,
320
+ // We filter the available components
321
+ // Because this modal is only used for adding components
322
+ components: [],
323
+ name: dynamicZoneTarget,
324
+ createComponent: false,
325
+ componentToCreate: { type: 'component' },
326
+ };
327
+
328
+ dispatch({
329
+ type: SET_DYNAMIC_ZONE_DATA_SCHEMA,
330
+ attributeToEdit,
331
+ });
332
+ }
333
+
334
+ // Set the predefined data structure to create an attribute
335
+ if (
336
+ attributeType &&
337
+ attributeType !== 'null' &&
338
+ // This condition is added to prevent the reducer state to be cleared when navigating from the base tab to tha advanced one
339
+ state.modalType !== 'attribute'
340
+ ) {
341
+ const attributeToEditNotFormatted = get(
342
+ allDataSchema,
343
+ [...pathToSchema, 'schema', 'attributes', attributeName],
344
+ {}
345
+ );
346
+ const attributeToEdit = {
347
+ ...attributeToEditNotFormatted,
348
+ name: attributeName,
349
+ };
350
+
351
+ // We need to set the repeatable key to false when editing a component
352
+ // The API doesn't send this info
353
+ if (attributeType === 'component' && actionType === 'edit') {
354
+ if (!attributeToEdit.repeatable) {
355
+ set(attributeToEdit, 'repeatable', false);
356
+ }
357
+ }
358
+
359
+ if (attributeType === 'relation' && !has(attributeToEdit, ['targetAttribute'])) {
360
+ set(attributeToEdit, ['targetAttribute'], '-');
361
+ }
362
+
363
+ dispatch({
364
+ type: SET_ATTRIBUTE_DATA_SCHEMA,
365
+ attributeType,
366
+ nameToSetForRelation: get(collectionTypesForRelation, ['0', 'title'], 'error'),
367
+ targetUid: get(collectionTypesForRelation, ['0', 'uid'], 'error'),
368
+ isEditing: actionType === 'edit',
369
+ modifiedDataToSetForEditing: attributeToEdit,
370
+ step,
371
+ forTarget,
372
+ });
373
+ }
374
+ }
375
+ // eslint-disable-next-line react-hooks/exhaustive-deps
376
+ }, [search]);
377
+
378
+ const form = get(forms, [state.modalType, 'form', state.settingType], () => ({
379
+ items: [],
380
+ }));
381
+
382
+ const headers = createHeadersArray(state);
383
+
384
+ const isCreatingContentType = state.modalType === 'contentType';
385
+ const isCreatingComponent = state.modalType === 'component';
386
+ const isCreatingAttribute = state.modalType === 'attribute';
387
+ const isComponentAttribute = state.attributeType === 'component' && isCreatingAttribute;
388
+
389
+ const isCreating = state.actionType === 'create';
390
+ const isCreatingComponentFromAView =
391
+ get(modifiedData, 'createComponent', false) || isCreatingComponentWhileAddingAField;
392
+ const isInFirstComponentStep = state.step === '1';
393
+ const isEditingCategory = state.modalType === 'editCategory';
394
+ const isOpen = !isEmpty(search);
395
+ const isPickingAttribute = state.modalType === 'chooseAttribute';
396
+ const uid = createUid(modifiedData.name || '');
397
+ const attributes = get(allDataSchema, [...state.pathToSchema, 'schema', 'attributes'], null);
398
+
399
+ const checkFormValidity = async () => {
400
+ let schema;
401
+ const dataToValidate =
402
+ isCreatingComponentFromAView && state.step === '1'
403
+ ? get(modifiedData, 'componentToCreate', {})
404
+ : modifiedData;
405
+
406
+ // Check form validity for content type
407
+ if (isCreatingContentType) {
408
+ schema = forms.contentType.schema(
409
+ Object.keys(contentTypes),
410
+ state.actionType === 'edit',
411
+ // currentUID
412
+ get(allDataSchema, [...state.pathToSchema, 'uid'], null),
413
+ reservedNames,
414
+ ctbFormsAPI
415
+ );
416
+
417
+ // Check form validity for component
418
+ // This is happening when the user click on the link from the left menu
419
+ } else if (isCreatingComponent) {
420
+ schema = forms.component.schema(
421
+ Object.keys(components),
422
+ modifiedData.category || '',
423
+ reservedNames,
424
+ state.actionType === 'edit',
425
+ get(allDataSchema, [...state.pathToSchema, 'uid'], null),
426
+ ctbFormsAPI
427
+ );
428
+
429
+ // Check for validity for creating a component
430
+ // This is happening when the user creates a component "on the fly"
431
+ // Since we temporarily store the component info in another object
432
+ // The data is set in the componentToCreate key
433
+ } else if (isComponentAttribute && isCreatingComponentFromAView && isInFirstComponentStep) {
434
+ schema = forms.component.schema(
435
+ Object.keys(components),
436
+ get(modifiedData, 'componentToCreate.category', ''),
437
+ reservedNames,
438
+ ctbFormsAPI
439
+ );
440
+
441
+ // Check form validity for creating a 'common attribute'
442
+ // We need to make sure that it is independent from the step
443
+ } else if (isCreatingAttribute && !isInFirstComponentStep) {
444
+ const type = state.attributeType === 'relation' ? 'relation' : modifiedData.type;
445
+
446
+ let alreadyTakenTargetContentTypeAttributes = [];
447
+
448
+ if (type === 'relation') {
449
+ const targetContentTypeUID = get(modifiedData, ['target'], null);
450
+ const targetContentTypeAttributes = get(
451
+ contentTypes,
452
+ [targetContentTypeUID, 'schema', 'attributes'],
453
+ {}
454
+ );
455
+
456
+ // Create an array with all the targetContentType attributes name
457
+ // in order to prevent the user from creating a relation with a targetAttribute
458
+ // that may exist in the other content type
459
+ alreadyTakenTargetContentTypeAttributes = Object.keys(targetContentTypeAttributes).filter(
460
+ attrName => {
461
+ // Keep all the target content type attributes when creating a relation
462
+ if (state.actionType !== 'edit') {
463
+ return true;
464
+ }
465
+
466
+ // Remove the already created one when editing
467
+ return attrName !== initialData.targetAttribute;
468
+ }
469
+ );
470
+ }
471
+ schema = forms.attribute.schema(
472
+ get(allDataSchema, state.pathToSchema, {}),
473
+ type,
474
+ reservedNames,
475
+ alreadyTakenTargetContentTypeAttributes,
476
+ { modifiedData, initialData },
477
+ ctbFormsAPI
478
+ );
479
+ } else if (isEditingCategory) {
480
+ schema = forms.editCategory.schema(allComponentsCategories, initialData, ctbFormsAPI);
481
+ } else {
482
+ // The user is either in the addComponentToDynamicZone modal or
483
+ // in step 1 of the add component (modalType=attribute&attributeType=component) but not creating a component
484
+
485
+ // eslint-disable-next-line no-lonely-if
486
+ if (isInFirstComponentStep && isCreatingComponentFromAView) {
487
+ schema = forms.component.schema(
488
+ Object.keys(components),
489
+ get(modifiedData, 'componentToCreate.category', ''),
490
+ reservedNames,
491
+ ctbFormsAPI
492
+ );
493
+ } else {
494
+ // The form is valid
495
+ // The case here is being in the addComponentToDynamicZone modal and not creating a component
496
+ return;
497
+ }
498
+ }
499
+
500
+ await schema.validate(dataToValidate, { abortEarly: false });
501
+ };
502
+
503
+ // TODO this should be a util for testing
504
+ const getButtonSubmitMessage = () => {
505
+ const { attributeType, modalType } = state;
506
+ const isCreatingAComponent = get(modifiedData, 'createComponent', false);
507
+ let tradId;
508
+
509
+ switch (modalType) {
510
+ case 'contentType':
511
+ case 'component':
512
+ case 'editCategory':
513
+ tradId = !isCreating ? getTrad('form.button.finish') : getTrad('form.button.continue');
514
+ break;
515
+ case 'addComponentToDynamicZone': {
516
+ tradId = isCreatingAComponent
517
+ ? getTrad('form.button.add-first-field-to-created-component')
518
+ : getTrad('form.button.finish');
519
+ break;
520
+ }
521
+ case 'attribute': {
522
+ if (attributeType === 'dynamiczone') {
523
+ tradId = getTrad('form.button.add-components-to-dynamiczone');
524
+ } else if (attributeType === 'component') {
525
+ if (isInFirstComponentStep) {
526
+ tradId = isCreatingAComponent
527
+ ? getTrad('form.button.configure-component')
528
+ : getTrad('form.button.select-component');
529
+ } else {
530
+ tradId = isCreatingComponentWhileAddingAField
531
+ ? getTrad('form.button.add-first-field-to-created-component')
532
+ : getTrad('form.button.add-field');
533
+ }
534
+ } else {
535
+ tradId = getTrad('form.button.add-field');
536
+ }
537
+ break;
538
+ }
539
+ default:
540
+ tradId = getTrad('form.button.add-field');
541
+ }
542
+
543
+ return formatMessage({ id: tradId });
544
+ };
545
+
546
+ // TODO remove and use the utils/makeSearch
547
+ const makeNextSearch = (searchObj, shouldContinue = isCreating) => {
548
+ if (!shouldContinue) {
549
+ return '';
550
+ }
551
+
552
+ return Object.keys(searchObj).reduce((acc, current, index) => {
553
+ if (searchObj[current] !== null) {
554
+ acc = `${acc}${index === 0 ? '' : '&'}${current}=${searchObj[current]}`;
555
+ }
556
+
557
+ return acc;
558
+ }, '');
559
+ };
560
+
561
+ const handleClickAddComponentsToDynamicZone = ({
562
+ target: { name, components, shouldAddComponents },
563
+ }) => {
564
+ dispatch({
565
+ type: ADD_COMPONENTS_TO_DYNAMIC_ZONE,
566
+ name,
567
+ components,
568
+ shouldAddComponents,
569
+ });
570
+ };
571
+
572
+ const handleChangeMediaAllowedTypes = ({ target: { name, value } }) => {
573
+ dispatch({
574
+ type: ON_CHANGE_ALLOWED_TYPE,
575
+ name,
576
+ value,
577
+ });
578
+ };
579
+
580
+ const toggleConfirmModal = useCallback(() => setShowConfirmModal(prev => !prev), []);
581
+
582
+ const handleChange = useCallback(
583
+ ({ target: { name, value, type, ...rest } }) => {
584
+ const namesThatCanResetToNullValue = [
585
+ 'enumName',
586
+ 'max',
587
+ 'min',
588
+ 'maxLength',
589
+ 'minLength',
590
+ 'regex',
591
+ ];
592
+
593
+ // When toggling the draftAndPublish from true to false
594
+ // We need to display a confirmation box
595
+ if (name === 'draftAndPublish' && state.actionType === 'edit' && value === false) {
596
+ toggleConfirmModal();
597
+
598
+ return;
599
+ }
600
+
601
+ let val;
602
+
603
+ if (['default', ...namesThatCanResetToNullValue].includes(name) && value === '') {
604
+ val = null;
605
+ } else if (
606
+ type === 'radio' &&
607
+ (name === 'multiple' ||
608
+ name === 'single' ||
609
+ name === 'createComponent' ||
610
+ name === 'repeatable')
611
+ ) {
612
+ // eslint-disable-next-line no-unneeded-ternary
613
+ val = value === 'false' ? false : true;
614
+
615
+ // The boolean default accepts 3 different values
616
+ // This check has been added to allow a reset to null for the bool
617
+ } else if (type === 'radio' && name === 'default') {
618
+ if (value === 'false') {
619
+ val = false;
620
+ } else if (value === 'true') {
621
+ val = true;
622
+ } else {
623
+ val = null;
624
+ }
625
+
626
+ // We store an array for the enum
627
+ } else if (name === 'enum') {
628
+ val = value.split('\n');
629
+ } else {
630
+ val = value;
631
+ }
632
+
633
+ const clonedErrors = Object.assign({}, formErrors);
634
+
635
+ // Reset min error when modifying the max
636
+ if (name === 'max') {
637
+ delete clonedErrors.min;
638
+ }
639
+
640
+ // Same here
641
+ if (name === 'maxLength') {
642
+ delete clonedErrors.minLength;
643
+ }
644
+
645
+ // Since the onBlur is deactivated we remove the errors directly when changing an input
646
+ delete clonedErrors[name];
647
+
648
+ dispatch({
649
+ type: SET_ERRORS,
650
+ errors: clonedErrors,
651
+ });
652
+
653
+ dispatch({
654
+ type: ON_CHANGE,
655
+ keys: name.split('.'),
656
+ value: val,
657
+ ...rest,
658
+ });
659
+ },
660
+ [dispatch, formErrors, state.actionType, toggleConfirmModal]
661
+ );
662
+
663
+ const handleConfirmDisableDraftAndPublish = useCallback(() => {
664
+ dispatch({
665
+ type: ON_CHANGE,
666
+ keys: ['draftAndPublish'],
667
+ value: false,
668
+ });
669
+
670
+ toggleConfirmModal();
671
+ }, [dispatch, toggleConfirmModal]);
672
+
673
+ const handleSubmit = async (e, shouldContinue = isCreating) => {
674
+ e.preventDefault();
675
+
676
+ try {
677
+ await checkFormValidity();
678
+ sendButtonAddMoreFieldEvent(shouldContinue);
679
+ const targetUid = state.forTarget === 'components' ? state.targetUid : uid;
680
+ let headerIcon;
681
+
682
+ if (state.forTarget === 'contentType') {
683
+ const currentKind = get(allDataSchema, ['contentType', 'schema', 'kind'], 'contentType');
684
+
685
+ headerIcon = state.kind || currentKind;
686
+ } else if (state.forTarget === 'component') {
687
+ headerIcon = 'component';
688
+ } else {
689
+ headerIcon = get(allDataSchema, ['components', state.targetUid, 'schema', 'icon'], '');
690
+ }
691
+
692
+ // Remove the last header when editing
693
+ if (state.actionType === 'edit') {
694
+ headers.pop();
695
+ }
696
+
697
+ const headersObject = createHeadersObjectFromArray(headers);
698
+ const nextHeaderIndex = headers.length + 1;
699
+
700
+ if (isCreatingContentType) {
701
+ // Create the content type schema
702
+ if (isCreating) {
703
+ createSchema({ ...modifiedData, kind: state.kind }, state.modalType, uid);
704
+ } else {
705
+ if (canEditContentType(allDataSchema, modifiedData)) {
706
+ push({ search: '' });
707
+ submitData(modifiedData);
708
+ } else {
709
+ strapi.notification.toggle({
710
+ type: 'warning',
711
+ message: { id: 'notification.contentType.relations.conflict' },
712
+ });
713
+ }
714
+
715
+ return;
716
+ }
717
+
718
+ push({
719
+ pathname: `/plugins/${pluginId}/content-types/${uid}`,
720
+ search: makeNextSearch({
721
+ modalType: 'chooseAttribute',
722
+ forTarget: state.forTarget,
723
+ targetUid,
724
+ header_label_1: modifiedData.name,
725
+ header_icon_name_1: headerIcon,
726
+ header_icon_isCustom_1: null,
727
+ }),
728
+ });
729
+ } else if (isCreatingComponent) {
730
+ if (isCreating) {
731
+ // Create the component schema
732
+ const componentUid = createComponentUid(modifiedData.name, modifiedData.category);
733
+ const { category, ...rest } = modifiedData;
734
+
735
+ createSchema(rest, 'component', componentUid, category);
736
+
737
+ push({
738
+ search: makeNextSearch({
739
+ modalType: 'chooseAttribute',
740
+ forTarget: state.forTarget,
741
+ targetUid: componentUid,
742
+ header_label_1: modifiedData.name,
743
+ header_icon_name_1: 'contentType',
744
+ header_icon_isCustom_1: null,
745
+ }),
746
+ pathname: `/plugins/${pluginId}/component-categories/${category}/${componentUid}`,
747
+ });
748
+ } else {
749
+ updateSchema(modifiedData, state.modalType, state.targetUid);
750
+
751
+ // Close the modal
752
+ push({ search: '' });
753
+
754
+ return;
755
+ }
756
+ } else if (isEditingCategory) {
757
+ if (toLower(initialData.name) === toLower(modifiedData.name)) {
758
+ // Close the modal
759
+ push({ search: '' });
760
+
761
+ return;
762
+ }
763
+
764
+ editCategory(initialData.name, modifiedData);
765
+
766
+ return;
767
+ // Add/edit a field to a content type
768
+ // Add/edit a field to a created component (the end modal is not step 2)
769
+ } else if (isCreatingAttribute && !isCreatingComponentFromAView) {
770
+ // Normal fields like boolean relations or dynamic zone
771
+ if (!isComponentAttribute) {
772
+ addAttribute(
773
+ modifiedData,
774
+ state.forTarget,
775
+ state.targetUid,
776
+ state.actionType === 'edit',
777
+ initialData
778
+ );
779
+
780
+ const isDynamicZoneAttribute = state.attributeType === 'dynamiczone';
781
+ // Adding a component to a dynamiczone is not the same logic as creating a simple field
782
+ // so the search is different
783
+
784
+ const dzSearch = makeNextSearch({
785
+ modalType: 'addComponentToDynamicZone',
786
+ forTarget: 'contentType',
787
+ targetUid: state.targetUid,
788
+
789
+ dynamicZoneTarget: modifiedData.name,
790
+ settingType: 'base',
791
+ step: '1',
792
+ actionType: 'create',
793
+ ...headersObject,
794
+ header_label_2: modifiedData.name,
795
+ header_icon_name_2: null,
796
+ header_icon_isCustom_2: false,
797
+ });
798
+ const nextSearch = isDynamicZoneAttribute
799
+ ? dzSearch
800
+ : makeNextSearch(
801
+ {
802
+ modalType: 'chooseAttribute',
803
+ forTarget: state.forTarget,
804
+ targetUid,
805
+ ...headersObject,
806
+ header_icon_isCustom_1: !['contentType', 'component'].includes(state.forTarget),
807
+ header_icon_name_1: headerIcon,
808
+ },
809
+ shouldContinue
810
+ );
811
+
812
+ // The user is creating a DZ (he had entered the name of the dz)
813
+ if (isDynamicZoneAttribute) {
814
+ // Step 1 of adding a component to a DZ, the user has the option to create a component
815
+ dispatch({
816
+ type: RESET_PROPS_AND_SET_THE_FORM_FOR_ADDING_A_COMPO_TO_A_DZ,
817
+ });
818
+
819
+ push({ search: isCreating ? nextSearch : '' });
820
+
821
+ return;
822
+ }
823
+
824
+ push({ search: nextSearch });
825
+
826
+ return;
827
+
828
+ // Adding an existing component
829
+ }
830
+ // eslint-disable-next-line no-lonely-if
831
+ if (isInFirstComponentStep) {
832
+ // Navigate the user to step 2
833
+ const nextSearchObj = {
834
+ modalType: 'attribute',
835
+ actionType: state.actionType,
836
+ settingType: 'base',
837
+ forTarget: state.forTarget,
838
+ targetUid: state.targetUid,
839
+ attributeType: 'component',
840
+ step: '2',
841
+ ...headersObject,
842
+ header_icon_isCustom_1: !['contentType', 'component'].includes(state.forTarget),
843
+ header_icon_name_1: headerIcon,
844
+ };
845
+
846
+ push({
847
+ search: makeNextSearch(nextSearchObj, shouldContinue),
848
+ });
849
+
850
+ // Clear the reducer and prepare the modified data
851
+ // This way we don't have to add some logic to re-run the useEffect
852
+ // The first step is either needed to create a component or just to navigate
853
+ // To the modal for adding a "common field"
854
+ dispatch({
855
+ type: RESET_PROPS_AND_SET_FORM_FOR_ADDING_AN_EXISTING_COMPO,
856
+ forTarget: state.forTarget,
857
+ });
858
+
859
+ // We don't want all the props to be reset
860
+ return;
861
+
862
+ // Here we are in step 2
863
+ // The step 2 is also use to edit an attribute that is a component
864
+ }
865
+ addAttribute(
866
+ modifiedData,
867
+ state.forTarget,
868
+ state.targetUid,
869
+ // This change the dispatched type
870
+ // either 'EDIT_ATTRIBUTE' or 'ADD_ATTRIBUTE' in the DataManagerProvider
871
+ state.actionType === 'edit',
872
+ // This is for the edit part
873
+ initialData,
874
+ // Passing true will add the component to the components object
875
+ // This way we can add fields to the added component (if it wasn't there already)
876
+ true
877
+ );
878
+ const nextSearch = {
879
+ modalType: 'chooseAttribute',
880
+ forTarget: state.forTarget,
881
+ targetUid: state.targetUid,
882
+ ...headersObject,
883
+ header_icon_isCustom_1: !['contentType', 'component'].includes(state.forTarget),
884
+ header_icon_name_1: headerIcon,
885
+ };
886
+
887
+ push({ search: makeSearch(nextSearch, shouldContinue) });
888
+
889
+ // We don't need to end the loop here we want the reducer to be reinitialised
890
+
891
+ // Logic for creating a component without clicking on the link in
892
+ // the left menu
893
+ // We need to separate the logic otherwise the component would be created
894
+ // even though the user didn't set any field
895
+ // We need to prevent the component from being created if the user closes the modal at step 2 without any submission
896
+ } else if (isCreatingAttribute && isCreatingComponentFromAView) {
897
+ // Step 1
898
+ if (isInFirstComponentStep) {
899
+ // Here the search could be refactored since it is the same as the case from above
900
+ // Navigate the user to step 2
901
+
902
+ let searchObj = {
903
+ modalType: 'attribute',
904
+ actionType: state.actionType,
905
+ settingType: 'base',
906
+ forTarget: state.forTarget,
907
+ targetUid: state.targetUid,
908
+ attributeType: 'component',
909
+ step: '2',
910
+ ...headersObject,
911
+ header_icon_isCustom_1: false,
912
+ header_icon_name_1: 'component',
913
+ };
914
+
915
+ emitEvent('willCreateComponentFromAttributesModal');
916
+
917
+ push({
918
+ search: makeNextSearch(searchObj, shouldContinue),
919
+ });
920
+
921
+ // Here we clear the reducer state but we also keep the created component
922
+ // If we were to create the component before
923
+ dispatch({
924
+ type: RESET_PROPS_AND_SAVE_CURRENT_DATA,
925
+ forTarget: state.forTarget,
926
+ });
927
+
928
+ // Terminate because we don't want the reducer to be entirely reset
929
+ return;
930
+
931
+ // Step 2 of creating a component (which is setting the attribute name in the parent's schema)
932
+ }
933
+ // We are destructuring because the modifiedData object doesn't have the appropriate format to create a field
934
+ const { category, type, ...rest } = componentToCreate;
935
+ // Create a the component temp UID
936
+ // This could be refactored but I think it's more understandable to separate the logic
937
+ const componentUid = createComponentUid(componentToCreate.name, category);
938
+ // Create the component first and add it to the components data
939
+ createSchema(
940
+ // Component data
941
+ rest,
942
+ // Type will always be component
943
+ // It will dispatch the CREATE_COMPONENT_SCHEMA action
944
+ // So the component will be added in the main components object
945
+ // This might not be needed if we don't allow navigation between entries while editing
946
+ type,
947
+ componentUid,
948
+ category,
949
+ // This will add the created component in the datamanager modifiedData components key
950
+ // Like explained above we will be able to modify the created component structure
951
+ isCreatingComponentFromAView
952
+ );
953
+ // Add the field to the schema
954
+ addAttribute(modifiedData, state.forTarget, state.targetUid, false);
955
+
956
+ dispatch({ type: RESET_PROPS });
957
+
958
+ // Open modal attribute for adding attr to component
959
+
960
+ const searchToOpenModalAttributeToAddAttributesToAComponent = {
961
+ modalType: 'chooseAttribute',
962
+ forTarget: 'components',
963
+ targetUid: componentUid,
964
+ ...headersObject,
965
+ header_icon_isCustom_1: true,
966
+ header_icon_name_1: componentToCreate.icon,
967
+ [`header_label_${nextHeaderIndex}`]: modifiedData.name,
968
+ [`header_icon_name_${nextHeaderIndex}`]: 'component',
969
+ [`header_icon_isCustom_${nextHeaderIndex}`]: false,
970
+ [`header_info_category_${nextHeaderIndex}`]: category,
971
+ [`header_info_name_${nextHeaderIndex}`]: componentToCreate.name,
972
+ };
973
+
974
+ push({
975
+ search: makeNextSearch(
976
+ searchToOpenModalAttributeToAddAttributesToAComponent,
977
+ shouldContinue
978
+ ),
979
+ });
980
+
981
+ return;
982
+
983
+ // The modal is addComponentToDynamicZone
984
+ } else {
985
+ // The modal is addComponentToDynamicZone
986
+ if (isInFirstComponentStep) {
987
+ if (isCreatingComponentFromAView) {
988
+ const { category, type, ...rest } = modifiedData.componentToCreate;
989
+ const componentUid = createComponentUid(modifiedData.componentToCreate.name, category);
990
+ // Create the component first and add it to the components data
991
+ createSchema(
992
+ // Component data
993
+ rest,
994
+ // Type will always be component
995
+ // It will dispatch the CREATE_COMPONENT_SCHEMA action
996
+ // So the component will be added in the main components object
997
+ // This might not be needed if we don't allow navigation between entries while editing
998
+ type,
999
+ componentUid,
1000
+ category,
1001
+ // This will add the created component in the datamanager modifiedData components key
1002
+ // Like explained above we will be able to modify the created component structure
1003
+ isCreatingComponentFromAView
1004
+ );
1005
+ // Add the created component to the DZ
1006
+ // We don't want to remove the old ones
1007
+ addCreatedComponentToDynamicZone(state.dynamicZoneTarget, [componentUid]);
1008
+
1009
+ // The Dynamic Zone and the component is created created
1010
+ // Open the modal to add fields to the created component
1011
+
1012
+ const searchToOpenAddField = {
1013
+ modalType: 'chooseAttribute',
1014
+ forTarget: 'components',
1015
+ targetUid: componentUid,
1016
+ ...headersObject,
1017
+ header_icon_isCustom_1: true,
1018
+ header_icon_name_1: modifiedData.componentToCreate.icon,
1019
+ [`header_label_${nextHeaderIndex}`]: modifiedData.name,
1020
+ [`header_icon_name_${nextHeaderIndex}`]: 'component',
1021
+ [`header_icon_isCustom_${nextHeaderIndex}`]: false,
1022
+ [`header_info_category_${nextHeaderIndex}`]: category,
1023
+ [`header_info_name_${nextHeaderIndex}`]: modifiedData.componentToCreate.name,
1024
+ };
1025
+
1026
+ push({ search: makeSearch(searchToOpenAddField, true) });
1027
+ } else {
1028
+ // Add the components to the DZ
1029
+ changeDynamicZoneComponents(state.dynamicZoneTarget, modifiedData.components);
1030
+
1031
+ // TODO nav
1032
+ // Search to open modal add fields for the main type (content type)
1033
+ push({ search: '' });
1034
+ }
1035
+ } else {
1036
+ console.error('This case is not handled');
1037
+ }
1038
+
1039
+ return;
1040
+ }
1041
+
1042
+ dispatch({
1043
+ type: RESET_PROPS,
1044
+ });
1045
+ } catch (err) {
1046
+ const errors = getYupInnerErrors(err);
1047
+ console.log({ err, errors });
1048
+
1049
+ dispatch({
1050
+ type: SET_ERRORS,
1051
+ errors,
1052
+ });
1053
+ }
1054
+ };
1055
+ const handleToggle = () => {
1056
+ push({ search: '' });
1057
+ };
1058
+
1059
+ const onClosed = () => {
1060
+ setState(INITIAL_STATE_DATA);
1061
+ dispatch({
1062
+ type: RESET_PROPS,
1063
+ });
1064
+ };
1065
+ const onOpened = () => {
1066
+ if (state.modalType === 'chooseAttribute') {
1067
+ attributeOptionRef.current.focus();
1068
+ }
1069
+ };
1070
+
1071
+ const sendAdvancedTabEvent = tab => {
1072
+ if (tab !== 'advanced') {
1073
+ return;
1074
+ }
1075
+
1076
+ if (isCreatingContentType) {
1077
+ emitEvent('didSelectContentTypeSettings');
1078
+
1079
+ return;
1080
+ }
1081
+
1082
+ if (state.forTarget === 'contentType') {
1083
+ emitEvent('didSelectContentTypeFieldSettings');
1084
+ }
1085
+ };
1086
+
1087
+ const sendButtonAddMoreFieldEvent = shouldContinue => {
1088
+ if (
1089
+ state.modalType === 'attribute' &&
1090
+ state.forTarget === 'contentType' &&
1091
+ state.attributeType !== 'dynamiczone' &&
1092
+ shouldContinue
1093
+ ) {
1094
+ emitEvent('willAddMoreFieldToContentType');
1095
+ }
1096
+ };
1097
+
1098
+ const shouldDisableAdvancedTab = () => {
1099
+ return (
1100
+ ((state.attributeType === 'component' || state.modalType === 'addComponentToDynamicZone') &&
1101
+ get(modifiedData, ['createComponent'], null) === false) ||
1102
+ state.modalType === 'editCategory'
1103
+ );
1104
+ };
1105
+
1106
+ // Display data for the attributes picker modal
1107
+ const displayedAttributes = getAttributesToDisplay(
1108
+ state.forTarget,
1109
+ state.targetUid,
1110
+ // We need the nested components so we know when to remove the component option
1111
+ nestedComponents
1112
+ );
1113
+
1114
+ // Styles
1115
+ const modalBodyStyle = isPickingAttribute ? { paddingTop: '0.5rem', paddingBottom: '3rem' } : {};
1116
+
1117
+ return (
1118
+ <>
1119
+ <Modal
1120
+ isOpen={isOpen}
1121
+ onOpened={onOpened}
1122
+ onClosed={onClosed}
1123
+ onToggle={handleToggle}
1124
+ withoverflow={toString(
1125
+ state.modalType === 'addComponentToDynamicZone' ||
1126
+ (state.modalType === 'attribute' && state.attributeType === 'media')
1127
+ )}
1128
+ >
1129
+ <HeaderModal>
1130
+ <ModalHeader headerId={state.headerId} headers={headers} />
1131
+ <section>
1132
+ <HeaderModalTitle>
1133
+ <FormattedMessage
1134
+ id={getModalTitleSubHeader(state)}
1135
+ values={{
1136
+ type: upperFirst(
1137
+ formatMessage({
1138
+ id: getTrad(`attribute.${state.attributeType}`),
1139
+ })
1140
+ ),
1141
+ name: upperFirst(state.attributeName),
1142
+ step: state.step,
1143
+ }}
1144
+ >
1145
+ {msg => <span>{upperFirst(msg)}</span>}
1146
+ </FormattedMessage>
1147
+
1148
+ {!isPickingAttribute && (
1149
+ <>
1150
+ <div className="settings-tabs">
1151
+ <HeaderModalNavContainer>
1152
+ {NAVLINKS.map((link, index) => {
1153
+ return (
1154
+ <HeaderNavLink
1155
+ // The advanced tab is disabled when adding an existing component
1156
+ // step 1
1157
+ isDisabled={index === 1 && shouldDisableAdvancedTab()}
1158
+ isActive={state.settingType === link.id}
1159
+ key={link.id}
1160
+ {...link}
1161
+ onClick={() => {
1162
+ setState(prev => ({
1163
+ ...prev,
1164
+ settingType: link.id,
1165
+ }));
1166
+ sendAdvancedTabEvent(link.id);
1167
+ push({ search: getNextSearch(link.id, state) });
1168
+ }}
1169
+ nextTab={index === NAVLINKS.length - 1 ? 0 : index + 1}
1170
+ />
1171
+ );
1172
+ })}
1173
+ </HeaderModalNavContainer>
1174
+ </div>
1175
+ <hr />
1176
+ </>
1177
+ )}
1178
+ </HeaderModalTitle>
1179
+ </section>
1180
+ </HeaderModal>
1181
+ <form onSubmit={handleSubmit}>
1182
+ <ModalForm>
1183
+ <ModalBody style={modalBodyStyle}>
1184
+ <div className="container-fluid">
1185
+ {isPickingAttribute
1186
+ ? displayedAttributes.map((row, i) => {
1187
+ return (
1188
+ <div key={i} className="row">
1189
+ {i === 1 && (
1190
+ <hr
1191
+ style={{
1192
+ width: 'calc(100% - 30px)',
1193
+ marginBottom: 16,
1194
+ marginTop: 19,
1195
+ borderColor: '#F0F3F8',
1196
+ }}
1197
+ />
1198
+ )}
1199
+ {row.map((attr, index) => {
1200
+ const tabIndex =
1201
+ i === 0 ? index : displayedAttributes[0].length + index;
1202
+
1203
+ return (
1204
+ <AttributeOption
1205
+ key={attr}
1206
+ tabIndex={tabIndex}
1207
+ isDisplayed
1208
+ onClick={() => {}}
1209
+ ref={i === 0 && index === 0 ? attributeOptionRef : null}
1210
+ type={attr}
1211
+ />
1212
+ );
1213
+ })}
1214
+ </div>
1215
+ );
1216
+ })
1217
+ : form({
1218
+ data: modifiedData,
1219
+ type: state.attributeType,
1220
+ step: state.step,
1221
+ actionType: state.actionType,
1222
+ attributes,
1223
+ extensions: ctbFormsAPI,
1224
+ forTarget: state.forTarget,
1225
+ contentTypeSchema: allDataSchema.contentType || {},
1226
+ }).items.map((row, index) => {
1227
+ return (
1228
+ <div className="row" key={index}>
1229
+ {row.map((input, i) => {
1230
+ // The divider type is used mainly the advanced tab
1231
+ // It is the one responsible for displaying the settings label
1232
+ if (input.type === 'divider' || input.type === 'dividerDraftPublish') {
1233
+ const tradId =
1234
+ input.type === 'divider'
1235
+ ? 'form.attribute.item.settings.name'
1236
+ : 'form.contentType.divider.draft-publish';
1237
+
1238
+ return (
1239
+ <div className="col-12" key="divider">
1240
+ <Padded bottom size="smd">
1241
+ <div style={{ paddingTop: 3 }} />
1242
+ <Text
1243
+ fontSize="xs"
1244
+ color="grey"
1245
+ fontWeight="bold"
1246
+ textTransform="uppercase"
1247
+ >
1248
+ <FormattedMessage id={getTrad(tradId)}>
1249
+ {txt => txt}
1250
+ </FormattedMessage>
1251
+ </Text>
1252
+ </Padded>
1253
+ </div>
1254
+ );
1255
+ }
1256
+
1257
+ // The spacer type is used mainly to align the icon picker...
1258
+ if (input.type === 'spacer') {
1259
+ return <div key="spacer" style={{ height: 8 }} />;
1260
+ }
1261
+
1262
+ // The spacer type is used mainly to align the icon picker...
1263
+ if (input.type === 'spacer-small') {
1264
+ return <div key={`${index}.${i}`} style={{ height: 4 }} />;
1265
+ }
1266
+
1267
+ if (input.type === 'spacer-medium') {
1268
+ return <div key={`${index}.${i}`} style={{ height: 8 }} />;
1269
+ }
1270
+
1271
+ // This type is used in the addComponentToDynamicZone modal when selecting the option add an existing component
1272
+ // It pushes select the components to the right
1273
+ if (input.type === 'pushRight') {
1274
+ return <div key={`${index}.${i}`} className={`col-${input.size}`} />;
1275
+ }
1276
+
1277
+ if (input.type === 'relation') {
1278
+ return (
1279
+ <RelationForm
1280
+ key="relation"
1281
+ mainBoxHeader={get(headers, [0, 'label'], '')}
1282
+ modifiedData={modifiedData}
1283
+ naturePickerType={state.forTarget}
1284
+ onChange={handleChange}
1285
+ errors={formErrors}
1286
+ />
1287
+ );
1288
+ }
1289
+
1290
+ // When extending the yup schema of an existing field (like in https://github.com/strapi/strapi/blob/293ff3b8f9559236609d123a2774e3be05ce8274/packages/strapi-plugin-i18n/admin/src/index.js#L52)
1291
+ // and triggering a yup validation error in the UI (missing a required field for example)
1292
+ // We got an object that looks like: formErrors = { "pluginOptions.i18n.localized": {...} }
1293
+ // In order to deal with this error, we can't rely on lodash.get to resolve this key
1294
+ // - lodash will try to access {pluginOptions: {i18n: {localized: true}}})
1295
+ // - and we just want to access { "pluginOptions.i18n.localized": {...} }
1296
+ // NOTE: this is a hack
1297
+ const pluginOptionError = Object.keys(formErrors).find(
1298
+ key => key === input.name
1299
+ );
1300
+
1301
+ // Retrieve the error for a specific input
1302
+ const errorId = pluginOptionError
1303
+ ? formErrors[pluginOptionError].id
1304
+ : get(
1305
+ formErrors,
1306
+ [
1307
+ ...input.name
1308
+ .split('.')
1309
+ // The filter here is used when creating a component
1310
+ // in the component step 1 modal
1311
+ // Since the component info is stored in the
1312
+ // componentToCreate object we can access the error
1313
+ // By removing the key
1314
+ .filter(key => key !== 'componentToCreate'),
1315
+ 'id',
1316
+ ],
1317
+ null
1318
+ );
1319
+
1320
+ const retrievedValue = get(modifiedData, input.name, '');
1321
+
1322
+ let value;
1323
+
1324
+ // Condition for the boolean default value
1325
+ // The radio input doesn't accept false, true or null as value
1326
+ // So we pass them as string
1327
+ // This way the data stays accurate and we don't have to operate
1328
+ // any data mutation
1329
+ if (input.name === 'default' && state.attributeType === 'boolean') {
1330
+ value = toString(retrievedValue);
1331
+ // Same here for the enum
1332
+ } else if (input.name === 'enum' && Array.isArray(retrievedValue)) {
1333
+ value = retrievedValue.join('\n');
1334
+ } else if (input.name === 'uid') {
1335
+ value = input.value;
1336
+ } else if (input.name === 'allowedTypes' && retrievedValue === '') {
1337
+ value = null;
1338
+ } else if (input.type === 'checkbox' && !retrievedValue) {
1339
+ value = false;
1340
+ } else {
1341
+ value = retrievedValue;
1342
+ }
1343
+
1344
+ // The addon input is not present in @buffetjs so we are using the old lib
1345
+ // for the moment that's why we don't want them be passed to buffet
1346
+ // like the other created inputs
1347
+ if (input.type === 'addon') {
1348
+ return (
1349
+ <InputsIndex
1350
+ key={input.name}
1351
+ {...input}
1352
+ type="string"
1353
+ onChange={handleChange}
1354
+ value={value}
1355
+ />
1356
+ );
1357
+ }
1358
+
1359
+ return (
1360
+ <div className={`col-${input.size || 6}`} key={input.name}>
1361
+ <Inputs
1362
+ {...input}
1363
+ modifiedData={modifiedData}
1364
+ addComponentsToDynamicZone={handleClickAddComponentsToDynamicZone}
1365
+ changeMediaAllowedTypes={handleChangeMediaAllowedTypes}
1366
+ customInputs={{
1367
+ allowedTypesSelect: WrapperSelect,
1368
+ checkbox: CheckboxWithDescription,
1369
+ componentIconPicker: ComponentIconPicker,
1370
+ componentSelect: WrapperSelect,
1371
+ creatableSelect: WrapperSelect,
1372
+ customCheckboxWithChildren: CustomCheckbox,
1373
+ booleanBox: BooleanBox,
1374
+ ...inputsFromPlugins,
1375
+ }}
1376
+ isCreating={isCreating}
1377
+ // Props for the componentSelect
1378
+ isCreatingComponentWhileAddingAField={
1379
+ isCreatingComponentWhileAddingAField
1380
+ }
1381
+ // Props for the componentSelect
1382
+ // Since the component is created after adding it to a type
1383
+ // its name and category can't be retrieved from the data manager
1384
+ componentCategoryNeededForAddingAfieldWhileCreatingAComponent={get(
1385
+ componentToCreate,
1386
+ 'category',
1387
+ null
1388
+ )}
1389
+ // Props for the componentSelect same explanation
1390
+ componentNameNeededForAddingAfieldWhileCreatingAComponent={get(
1391
+ componentToCreate,
1392
+ 'name',
1393
+ null
1394
+ )}
1395
+ isAddingAComponentToAnotherComponent={
1396
+ state.forTarget === 'components' ||
1397
+ state.forTarget === 'component'
1398
+ }
1399
+ value={value}
1400
+ error={isEmpty(errorId) ? null : formatMessage({ id: errorId })}
1401
+ onChange={handleChange}
1402
+ onBlur={() => {}}
1403
+ description={
1404
+ get(input, 'description.id', null)
1405
+ ? formatMessage(input.description)
1406
+ : input.description
1407
+ }
1408
+ placeholder={
1409
+ get(input, 'placeholder.id', null)
1410
+ ? formatMessage(input.placeholder)
1411
+ : input.placeholder
1412
+ }
1413
+ label={
1414
+ get(input, 'label.id', null)
1415
+ ? formatMessage(input.label)
1416
+ : input.label
1417
+ }
1418
+ />
1419
+ </div>
1420
+ );
1421
+ })}
1422
+ </div>
1423
+ );
1424
+ })}
1425
+ </div>
1426
+ </ModalBody>
1427
+ </ModalForm>
1428
+ {!isPickingAttribute && (
1429
+ <ModalFooter>
1430
+ <section style={{ alignItems: 'center' }}>
1431
+ <Button type="button" color="cancel" onClick={handleToggle}>
1432
+ {formatMessage({
1433
+ id: 'app.components.Button.cancel',
1434
+ })}
1435
+ </Button>
1436
+ <div>
1437
+ {isCreatingAttribute && !isInFirstComponentStep && (
1438
+ <Button
1439
+ type={isCreating ? 'button' : 'submit'}
1440
+ color="success"
1441
+ onClick={e => {
1442
+ handleSubmit(e, false);
1443
+ }}
1444
+ style={{ marginRight: '10px' }}
1445
+ >
1446
+ {formatMessage({ id: 'form.button.finish' })}
1447
+ </Button>
1448
+ )}
1449
+ {(isCreatingContentType || isCreatingComponent) && !isCreating && (
1450
+ <Button
1451
+ type="button"
1452
+ color="delete"
1453
+ onClick={e => {
1454
+ e.preventDefault();
1455
+ deleteData();
1456
+ }}
1457
+ style={{ marginRight: '10px' }}
1458
+ >
1459
+ {formatMessage({ id: getTrad('form.button.delete') })}
1460
+ </Button>
1461
+ )}
1462
+ {isEditingCategory && (
1463
+ <Button
1464
+ type="button"
1465
+ color="delete"
1466
+ onClick={e => {
1467
+ e.preventDefault();
1468
+
1469
+ deleteCategory(initialData.name);
1470
+ }}
1471
+ style={{ marginRight: '10px' }}
1472
+ >
1473
+ {formatMessage({ id: getTrad('form.button.delete') })}
1474
+ </Button>
1475
+ )}
1476
+ {isCreating && state.attributeType === 'dynamiczone' && (
1477
+ <CustomButton
1478
+ type={isCreating ? 'submit' : 'button'}
1479
+ color={
1480
+ (isCreatingContentType ||
1481
+ isCreatingComponent ||
1482
+ isEditingCategory ||
1483
+ (state.modalType === 'addComponentToDynamicZone' &&
1484
+ state.step === '1' &&
1485
+ !isCreatingComponentFromAView)) &&
1486
+ !isCreating
1487
+ ? 'success'
1488
+ : 'primary'
1489
+ }
1490
+ onClick={e => handleSubmit(e, true)}
1491
+ icon={
1492
+ (isCreatingAttribute &&
1493
+ !isCreatingComponentFromAView &&
1494
+ state.step !== '1') ||
1495
+ (state.modalType === 'addComponentToDynamicZone' &&
1496
+ isCreatingComponentFromAView) ||
1497
+ (isCreatingComponentFromAView && state.step === '2')
1498
+ }
1499
+ >
1500
+ {getButtonSubmitMessage()}
1501
+ </CustomButton>
1502
+ )}
1503
+ {state.attributeType !== 'dynamiczone' && (
1504
+ <CustomButton
1505
+ type={isCreating ? 'submit' : 'button'}
1506
+ color={
1507
+ (isCreatingContentType ||
1508
+ isCreatingComponent ||
1509
+ isEditingCategory ||
1510
+ (state.modalType === 'addComponentToDynamicZone' &&
1511
+ state.step === '1' &&
1512
+ !isCreatingComponentFromAView)) &&
1513
+ !isCreating
1514
+ ? 'success'
1515
+ : 'primary'
1516
+ }
1517
+ onClick={e => handleSubmit(e, true)}
1518
+ icon={
1519
+ (isCreatingAttribute &&
1520
+ !isCreatingComponentFromAView &&
1521
+ state.step !== '1') ||
1522
+ (state.modalType === 'addComponentToDynamicZone' &&
1523
+ isCreatingComponentFromAView) ||
1524
+ (isCreatingComponentFromAView && state.step === '2')
1525
+ }
1526
+ >
1527
+ {getButtonSubmitMessage()}
1528
+ </CustomButton>
1529
+ )}
1530
+ </div>
1531
+ </section>
1532
+ </ModalFooter>
1533
+ )}
1534
+ </form>
1535
+ </Modal>
1536
+ {/* CONFIRM MODAL FOR DRAFT AND PUBLISH */}
1537
+ <PopUpWarning
1538
+ isOpen={showConfirmModal}
1539
+ onConfirm={handleConfirmDisableDraftAndPublish}
1540
+ toggleModal={toggleConfirmModal}
1541
+ popUpWarningType="danger"
1542
+ content={{
1543
+ message: getTrad('popUpWarning.draft-publish.message'),
1544
+ secondMessage: getTrad('popUpWarning.draft-publish.second-message'),
1545
+ confirm: getTrad('popUpWarning.draft-publish.button.confirm'),
1546
+ }}
1547
+ />
1548
+ </>
1549
+ );
1550
+ };
1551
+
1552
+ export default FormModal;