@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.
- package/LICENSE +22 -0
- package/README.md +19 -0
- package/admin/src/InjectedComponents/ContentManager/EditSettingViewButton.js +76 -0
- package/admin/src/InjectedComponents/ContentManager/EditViewLink.js +44 -0
- package/admin/src/assets/images/logo.svg +1 -0
- package/admin/src/components/AllowedTypesSelect/MenuList.js +115 -0
- package/admin/src/components/AllowedTypesSelect/Text.js +26 -0
- package/admin/src/components/AllowedTypesSelect/index.js +55 -0
- package/admin/src/components/AttributeOption/Button.js +50 -0
- package/admin/src/components/AttributeOption/Card.js +26 -0
- package/admin/src/components/AttributeOption/index.js +127 -0
- package/admin/src/components/BooleanBox/CT.js +21 -0
- package/admin/src/components/BooleanBox/Enumeration.js +10 -0
- package/admin/src/components/BooleanBox/EnumerationWrapper.js +14 -0
- package/admin/src/components/BooleanBox/Label.js +9 -0
- package/admin/src/components/BooleanBox/ST.js +21 -0
- package/admin/src/components/BooleanBox/Wrapper.js +75 -0
- package/admin/src/components/BooleanBox/icons/CTSelected.js +136 -0
- package/admin/src/components/BooleanBox/icons/CTUnselected.js +136 -0
- package/admin/src/components/BooleanBox/icons/STSelected.js +80 -0
- package/admin/src/components/BooleanBox/icons/STUnselected.js +76 -0
- package/admin/src/components/BooleanBox/index.js +103 -0
- package/admin/src/components/CheckboxWithDescription/Wrapper.js +12 -0
- package/admin/src/components/CheckboxWithDescription/index.js +40 -0
- package/admin/src/components/ComponentCard/Close.js +12 -0
- package/admin/src/components/ComponentCard/Wrapper.js +84 -0
- package/admin/src/components/ComponentCard/index.js +62 -0
- package/admin/src/components/ComponentIconPicker/Cell.js +40 -0
- package/admin/src/components/ComponentIconPicker/CellRenderer.js +36 -0
- package/admin/src/components/ComponentIconPicker/Search.js +12 -0
- package/admin/src/components/ComponentIconPicker/SearchWrapper.js +34 -0
- package/admin/src/components/ComponentIconPicker/Wrapper.js +60 -0
- package/admin/src/components/ComponentIconPicker/index.js +143 -0
- package/admin/src/components/ComponentList/index.js +78 -0
- package/admin/src/components/ComponentSelect/Category.js +20 -0
- package/admin/src/components/ComponentSelect/CategoryName.js +11 -0
- package/admin/src/components/ComponentSelect/MenuList.js +110 -0
- package/admin/src/components/ComponentSelect/MultipleMenuList.js +271 -0
- package/admin/src/components/ComponentSelect/Value.js +83 -0
- package/admin/src/components/ComponentSelect/index.js +83 -0
- package/admin/src/components/ComponentSelect/utils/HasSomeSubArray.js +5 -0
- package/admin/src/components/ComponentSelect/utils/hasSubArray.js +5 -0
- package/admin/src/components/CreatableSelect/index.js +58 -0
- package/admin/src/components/CustomCheckbox/StyledCustomCheckbox.js +25 -0
- package/admin/src/components/CustomCheckbox/index.js +71 -0
- package/admin/src/components/CustomLink/P.js +17 -0
- package/admin/src/components/CustomLink/StyledCustomLink.js +20 -0
- package/admin/src/components/CustomLink/index.js +31 -0
- package/admin/src/components/DynamicZoneList/ComponentButton.js +39 -0
- package/admin/src/components/DynamicZoneList/index.js +124 -0
- package/admin/src/components/HeaderModalNavContainer/index.js +25 -0
- package/admin/src/components/HeaderNavLink/Wrapper.js +21 -0
- package/admin/src/components/HeaderNavLink/index.js +50 -0
- package/admin/src/components/List/List.js +131 -0
- package/admin/src/components/List/index.js +295 -0
- package/admin/src/components/ListButton/index.js +9 -0
- package/admin/src/components/ListHeader/Title.js +19 -0
- package/admin/src/components/ListHeader/Wrapper.js +24 -0
- package/admin/src/components/ListHeader/index.js +37 -0
- package/admin/src/components/ListRow/Wrapper.js +76 -0
- package/admin/src/components/ListRow/index.js +334 -0
- package/admin/src/components/ModalHeader/ComponentIcon.js +21 -0
- package/admin/src/components/ModalHeader/ComponentInfos.js +22 -0
- package/admin/src/components/ModalHeader/ComponentInfosWrapper.js +9 -0
- package/admin/src/components/ModalHeader/DropdownInfos.js +50 -0
- package/admin/src/components/ModalHeader/Icon.js +26 -0
- package/admin/src/components/ModalHeader/IconWrapper.js +11 -0
- package/admin/src/components/ModalHeader/Item.js +19 -0
- package/admin/src/components/ModalHeader/Menu.js +8 -0
- package/admin/src/components/ModalHeader/Toggle.js +14 -0
- package/admin/src/components/ModalHeader/Wrapper.js +12 -0
- package/admin/src/components/ModalHeader/index.js +105 -0
- package/admin/src/components/RelationForm/Wrapper.js +9 -0
- package/admin/src/components/RelationForm/index.js +66 -0
- package/admin/src/components/RelationFormBox/Wrapper.js +34 -0
- package/admin/src/components/RelationFormBox/index.js +85 -0
- package/admin/src/components/RelationFormNaturePicker/Wrapper.js +53 -0
- package/admin/src/components/RelationFormNaturePicker/index.js +112 -0
- package/admin/src/components/RelationTargetPicker/Item.js +62 -0
- package/admin/src/components/RelationTargetPicker/Wrapper.js +89 -0
- package/admin/src/components/RelationTargetPicker/index.js +88 -0
- package/admin/src/components/SelectCheckbox/index.js +8 -0
- package/admin/src/components/SelectMenuSubUl/ToggleUl.js +23 -0
- package/admin/src/components/SelectMenuSubUl/index.js +23 -0
- package/admin/src/components/SelectMenuUl/index.js +66 -0
- package/admin/src/components/Td/index.js +37 -0
- package/admin/src/components/UpperFirst/index.js +14 -0
- package/admin/src/components/WrapperSelect/index.js +89 -0
- package/admin/src/containers/App/Wrapper.js +14 -0
- package/admin/src/containers/App/index.js +39 -0
- package/admin/src/containers/App/utils/icons.json +962 -0
- package/admin/src/containers/DataManagerProvider/constants.js +15 -0
- package/admin/src/containers/DataManagerProvider/index.js +610 -0
- package/admin/src/containers/DataManagerProvider/reducer.js +461 -0
- package/admin/src/containers/DataManagerProvider/selectors.js +25 -0
- package/admin/src/containers/DataManagerProvider/utils/cleanData.js +167 -0
- package/admin/src/containers/DataManagerProvider/utils/createDataObject.js +8 -0
- package/admin/src/containers/DataManagerProvider/utils/createModifiedDataSchema.js +91 -0
- package/admin/src/containers/DataManagerProvider/utils/retrieveComponentsFromSchema.js +55 -0
- package/admin/src/containers/DataManagerProvider/utils/retrieveComponentsThatHaveComponents.js +35 -0
- package/admin/src/containers/DataManagerProvider/utils/retrieveNestedComponents.js +34 -0
- package/admin/src/containers/DataManagerProvider/utils/retrieveSpecificInfoFromComponents.js +12 -0
- package/admin/src/containers/FormModal/CustomButton.js +12 -0
- package/admin/src/containers/FormModal/attributes/advancedForm.js +269 -0
- package/admin/src/containers/FormModal/attributes/attributeOptions.js +99 -0
- package/admin/src/containers/FormModal/attributes/baseForm.js +322 -0
- package/admin/src/containers/FormModal/attributes/commonBaseForm.js +7 -0
- package/admin/src/containers/FormModal/attributes/form.js +9 -0
- package/admin/src/containers/FormModal/attributes/index.js +4 -0
- package/admin/src/containers/FormModal/attributes/nameField.js +19 -0
- package/admin/src/containers/FormModal/attributes/types.js +309 -0
- package/admin/src/containers/FormModal/attributes/uiHelpers.js +11 -0
- package/admin/src/containers/FormModal/attributes/validation/common.js +151 -0
- package/admin/src/containers/FormModal/category/createCategorySchema.js +28 -0
- package/admin/src/containers/FormModal/category/form.js +27 -0
- package/admin/src/containers/FormModal/category/index.js +3 -0
- package/admin/src/containers/FormModal/category/regex.js +3 -0
- package/admin/src/containers/FormModal/component/componentField.js +25 -0
- package/admin/src/containers/FormModal/component/createComponentSchema.js +49 -0
- package/admin/src/containers/FormModal/component/form.js +67 -0
- package/admin/src/containers/FormModal/component/index.js +4 -0
- package/admin/src/containers/FormModal/constants.js +11 -0
- package/admin/src/containers/FormModal/contentType/createContentTypeSchema.js +44 -0
- package/admin/src/containers/FormModal/contentType/form.js +116 -0
- package/admin/src/containers/FormModal/contentType/index.js +2 -0
- package/admin/src/containers/FormModal/dynamicZone/form.js +45 -0
- package/admin/src/containers/FormModal/dynamicZone/index.js +2 -0
- package/admin/src/containers/FormModal/forms/index.js +164 -0
- package/admin/src/containers/FormModal/index.js +1552 -0
- package/admin/src/containers/FormModal/reducer.js +293 -0
- package/admin/src/containers/FormModal/selectors.js +24 -0
- package/admin/src/containers/FormModal/utils/canEditContentType.js +19 -0
- package/admin/src/containers/FormModal/utils/createHeadersArray.js +37 -0
- package/admin/src/containers/FormModal/utils/createHeadersObjectFromArray.js +27 -0
- package/admin/src/containers/FormModal/utils/createUid.js +17 -0
- package/admin/src/containers/FormModal/utils/getAttributesToDisplay.js +35 -0
- package/admin/src/containers/FormModal/utils/getModalTitleSubHeader.js +25 -0
- package/admin/src/containers/FormModal/utils/getNextSearch.js +17 -0
- package/admin/src/containers/FormModal/utils/index.js +6 -0
- package/admin/src/containers/FormModal/utils/relations.js +9 -0
- package/admin/src/containers/FormModal/utils/staticData.js +43 -0
- package/admin/src/containers/Initializer/index.js +26 -0
- package/admin/src/containers/LeftMenu/Wrapper.js +14 -0
- package/admin/src/containers/LeftMenu/index.js +220 -0
- package/admin/src/containers/ListView/Wrapper.js +16 -0
- package/admin/src/containers/ListView/index.js +322 -0
- package/admin/src/containers/NotFoundPage/index.js +20 -0
- package/admin/src/containers/RecursivePath/index.js +22 -0
- package/admin/src/contexts/DataManagerContext.js +5 -0
- package/admin/src/contexts/ListViewContext.js +5 -0
- package/admin/src/hooks/useDataManager.js +6 -0
- package/admin/src/hooks/useListView.js +6 -0
- package/admin/src/icons/Curve.js +26 -0
- package/admin/src/icons/ManyToMany.js +98 -0
- package/admin/src/icons/ManyToOne.js +64 -0
- package/admin/src/icons/ManyWay.js +36 -0
- package/admin/src/icons/OneToMany.js +65 -0
- package/admin/src/icons/OneToOne.js +47 -0
- package/admin/src/icons/OneWay.js +38 -0
- package/admin/src/index.js +83 -0
- package/admin/src/lifecycles.js +13 -0
- package/admin/src/permissions.js +9 -0
- package/admin/src/pluginId.js +5 -0
- package/admin/src/reducers.js +10 -0
- package/admin/src/translations/ar.json +48 -0
- package/admin/src/translations/cs.json +151 -0
- package/admin/src/translations/de.json +187 -0
- package/admin/src/translations/dk.json +181 -0
- package/admin/src/translations/en.json +188 -0
- package/admin/src/translations/es.json +188 -0
- package/admin/src/translations/fr.json +87 -0
- package/admin/src/translations/id.json +187 -0
- package/admin/src/translations/index.js +51 -0
- package/admin/src/translations/it.json +187 -0
- package/admin/src/translations/ja.json +50 -0
- package/admin/src/translations/ko.json +57 -0
- package/admin/src/translations/ms.json +179 -0
- package/admin/src/translations/nl.json +171 -0
- package/admin/src/translations/pl.json +152 -0
- package/admin/src/translations/pt-BR.json +51 -0
- package/admin/src/translations/pt.json +51 -0
- package/admin/src/translations/ru.json +188 -0
- package/admin/src/translations/sk.json +186 -0
- package/admin/src/translations/th.json +181 -0
- package/admin/src/translations/tr.json +51 -0
- package/admin/src/translations/uk.json +180 -0
- package/admin/src/translations/zh-Hans.json +188 -0
- package/admin/src/translations/zh.json +178 -0
- package/admin/src/utils/convertAttrObjToArray.js +7 -0
- package/admin/src/utils/formAPI.js +112 -0
- package/admin/src/utils/getAttributeDisplayedType.js +31 -0
- package/admin/src/utils/getComponents.js +31 -0
- package/admin/src/utils/getTrad.js +5 -0
- package/admin/src/utils/makeSearch.js +15 -0
- package/admin/src/utils/makeUnique.js +3 -0
- package/config/functions/bootstrap.js +14 -0
- package/config/routes.json +144 -0
- package/controllers/Builder.js +7 -0
- package/controllers/ComponentCategories.js +43 -0
- package/controllers/Components.js +152 -0
- package/controllers/Connections.js +9 -0
- package/controllers/ContentTypes.js +143 -0
- package/controllers/validation/common.js +118 -0
- package/controllers/validation/component-category.js +21 -0
- package/controllers/validation/component.js +89 -0
- package/controllers/validation/content-type.js +161 -0
- package/controllers/validation/data-transform.js +32 -0
- package/controllers/validation/model-schema.js +91 -0
- package/controllers/validation/relations.js +56 -0
- package/controllers/validation/types.js +277 -0
- package/package.json +63 -0
- package/services/Builder.js +7 -0
- package/services/ComponentCategories.js +88 -0
- package/services/Components.js +103 -0
- package/services/ContentTypes.js +263 -0
- package/services/api-handler.js +158 -0
- package/services/constants.js +59 -0
- package/services/schema-builder/component-builder.js +137 -0
- package/services/schema-builder/content-type-builder.js +256 -0
- package/services/schema-builder/index.js +196 -0
- package/services/schema-builder/schema-handler.js +297 -0
- package/utils/attributes.js +154 -0
- 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;
|