@strapi/admin 4.4.3 → 4.5.0-beta.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/admin/src/content-manager/components/CollectionTypeFormWrapper/index.js +35 -1
- package/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/index.js +11 -7
- package/admin/src/content-manager/components/DynamicTable/CellContent/index.js +6 -5
- package/admin/src/content-manager/components/DynamicTable/TableRows/index.js +5 -0
- package/admin/src/content-manager/components/DynamicTable/index.js +1 -1
- package/admin/src/content-manager/components/EditViewDataManagerProvider/index.js +73 -20
- package/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js +166 -26
- package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/cleanData.js +17 -1
- package/admin/src/content-manager/components/Inputs/index.js +12 -4
- package/admin/src/content-manager/components/NonRepeatableComponent/index.js +2 -0
- package/admin/src/content-manager/components/RelationInput/RelationInput.js +427 -0
- package/admin/src/content-manager/components/{SelectWrapper → RelationInput/components}/Option.js +15 -25
- package/admin/src/content-manager/components/RelationInput/components/Relation.js +48 -0
- package/admin/src/content-manager/components/RelationInput/components/RelationItem.js +52 -0
- package/admin/src/content-manager/components/RelationInput/components/RelationList.js +52 -0
- package/admin/src/content-manager/components/RelationInput/constants.js +1 -0
- package/admin/src/content-manager/components/RelationInput/index.js +1 -0
- package/admin/src/content-manager/components/RelationInputDataManager/RelationInputDataManager.js +261 -0
- package/admin/src/content-manager/components/RelationInputDataManager/constants.js +8 -0
- package/admin/src/content-manager/components/RelationInputDataManager/index.js +1 -0
- package/admin/src/content-manager/components/{SelectWrapper → RelationInputDataManager}/utils/connect.js +0 -1
- package/admin/src/content-manager/components/RelationInputDataManager/utils/getRelationLink.js +5 -0
- package/admin/src/content-manager/components/RelationInputDataManager/utils/index.js +4 -0
- package/admin/src/content-manager/components/RelationInputDataManager/utils/normalizeRelations.js +65 -0
- package/admin/src/content-manager/components/RelationInputDataManager/utils/normalizeSearchResults.js +12 -0
- package/admin/src/content-manager/components/RelationInputDataManager/utils/select.js +98 -0
- package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/index.js +5 -0
- package/admin/src/content-manager/components/SingleTypeFormWrapper/index.js +42 -2
- package/admin/src/content-manager/hooks/useFetchContentTypeLayout/utils/formatLayouts.js +7 -69
- package/admin/src/content-manager/hooks/useRelation/index.js +1 -0
- package/admin/src/content-manager/hooks/useRelation/useRelation.js +81 -0
- package/admin/src/content-manager/pages/EditSettingsView/components/DisplayedFields.js +4 -4
- package/admin/src/content-manager/pages/EditSettingsView/index.js +21 -49
- package/admin/src/content-manager/pages/EditSettingsView/reducer.js +0 -25
- package/admin/src/content-manager/pages/EditView/Header/index.js +121 -140
- package/admin/src/content-manager/pages/EditView/Header/utils/index.js +0 -1
- package/admin/src/content-manager/pages/EditView/Header/utils/select.js +4 -2
- package/admin/src/content-manager/pages/EditView/index.js +12 -65
- package/admin/src/content-manager/pages/ListView/FieldPicker/index.js +0 -1
- package/admin/src/content-manager/pages/ListView/index.js +0 -1
- package/admin/src/content-manager/pages/ListViewLayoutManager/index.js +0 -1
- package/admin/src/content-manager/utils/formatLayoutToApi.js +1 -3
- package/admin/src/pages/HomePage/SocialLinks.js +1 -1
- package/admin/src/translations/ar.json +0 -1
- package/admin/src/translations/ca.json +1 -8
- package/admin/src/translations/cs.json +0 -2
- package/admin/src/translations/de.json +4 -12
- package/admin/src/translations/dk.json +3 -11
- package/admin/src/translations/en.json +4 -11
- package/admin/src/translations/es.json +156 -168
- package/admin/src/translations/fr.json +3 -11
- package/admin/src/translations/gu.json +608 -617
- package/admin/src/translations/hi.json +689 -698
- package/admin/src/translations/hu.json +2 -10
- package/admin/src/translations/id.json +2 -10
- package/admin/src/translations/it.json +2 -10
- package/admin/src/translations/ja.json +2 -11
- package/admin/src/translations/ko.json +2 -11
- package/admin/src/translations/ml.json +689 -698
- package/admin/src/translations/ms.json +0 -2
- package/admin/src/translations/nl.json +3 -11
- package/admin/src/translations/pl.json +2 -11
- package/admin/src/translations/pt-BR.json +3 -11
- package/admin/src/translations/pt.json +0 -1
- package/admin/src/translations/ru.json +489 -501
- package/admin/src/translations/sa.json +85 -93
- package/admin/src/translations/sk.json +3 -10
- package/admin/src/translations/sv.json +3 -9
- package/admin/src/translations/th.json +0 -2
- package/admin/src/translations/tr.json +0 -1
- package/admin/src/translations/uk.json +0 -2
- package/admin/src/translations/vi.json +0 -1
- package/admin/src/translations/zh-Hans.json +4 -13
- package/admin/src/translations/zh.json +3 -11
- package/build/{8773.51992277.chunk.js → 1939.e3c87653.chunk.js} +50 -50
- package/build/8738.a30a2160.chunk.js +461 -0
- package/build/962.8651ba3f.chunk.js +184 -0
- package/build/Admin-authenticatedApp.883449a5.chunk.js +80 -0
- package/build/{Admin_homePage.6d5e3236.chunk.js → Admin_homePage.4b2be829.chunk.js} +1 -1
- package/build/{ar-json.d4cb26d9.chunk.js → ar-json.3489463d.chunk.js} +1 -1
- package/build/{ca-json.d16c1d28.chunk.js → ca-json.82df6eab.chunk.js} +1 -1
- package/build/content-manager.933dc286.chunk.js +1201 -0
- package/build/{cs-json.c8f28ba8.chunk.js → cs-json.ce49da5c.chunk.js} +1 -1
- package/build/{de-json.a9b514dc.chunk.js → de-json.0ad554eb.chunk.js} +1 -1
- package/build/{dk-json.09e8d145.chunk.js → dk-json.e195ea1a.chunk.js} +1 -1
- package/build/{en-json.e936d40e.chunk.js → en-json.1889403c.chunk.js} +1 -1
- package/build/{es-json.3a9c7c09.chunk.js → es-json.09f80f6e.chunk.js} +1 -1
- package/build/{fr-json.4ed1fc2c.chunk.js → fr-json.606d056b.chunk.js} +1 -1
- package/build/{gu-json.d8311297.chunk.js → gu-json.9881264f.chunk.js} +1 -1
- package/build/{hi-json.0edb8d29.chunk.js → hi-json.83dcf48f.chunk.js} +1 -1
- package/build/{hu-json.7855529a.chunk.js → hu-json.6f328bce.chunk.js} +1 -1
- package/build/{id-json.df9618f2.chunk.js → id-json.1f3c4303.chunk.js} +1 -1
- package/build/index.html +1 -1
- package/build/{it-json.a21bf078.chunk.js → it-json.494ac432.chunk.js} +1 -1
- package/build/{ja-json.7b0d9067.chunk.js → ja-json.6f262117.chunk.js} +1 -1
- package/build/{ko-json.983c1f8f.chunk.js → ko-json.36dc3b9a.chunk.js} +1 -1
- package/build/main.63e7ea0a.js +9338 -0
- package/build/{ml-json.8dd021c8.chunk.js → ml-json.9566bf9a.chunk.js} +1 -1
- package/build/{ms-json.836ed013.chunk.js → ms-json.ed51e902.chunk.js} +1 -1
- package/build/{nl-json.29d2eb37.chunk.js → nl-json.94c3a289.chunk.js} +1 -1
- package/build/{pl-json.1f04f00c.chunk.js → pl-json.ccc6ef23.chunk.js} +1 -1
- package/build/{pt-BR-json.b4bc8efe.chunk.js → pt-BR-json.744f024d.chunk.js} +1 -1
- package/build/{pt-json.c23020ab.chunk.js → pt-json.3161ca22.chunk.js} +1 -1
- package/build/{ru-json.7ab40ccf.chunk.js → ru-json.d22ea13c.chunk.js} +1 -1
- package/build/{runtime~main.7faf633a.js → runtime~main.3a5e1b07.js} +1 -1
- package/build/{sa-json.c5a9f4ea.chunk.js → sa-json.8fb1c04d.chunk.js} +1 -1
- package/build/{sk-json.e4c24c4e.chunk.js → sk-json.6c7335d4.chunk.js} +1 -1
- package/build/{sv-json.c3f471ae.chunk.js → sv-json.2e589a7d.chunk.js} +1 -1
- package/build/{th-json.a59ffb32.chunk.js → th-json.72e8de3d.chunk.js} +1 -1
- package/build/{tr-json.276e59fe.chunk.js → tr-json.9c44ea0c.chunk.js} +1 -1
- package/build/{uk-json.5b5b9c27.chunk.js → uk-json.c4cd2e24.chunk.js} +1 -1
- package/build/{upload-translation-en-json.004a86c1.chunk.js → upload-translation-en-json.86da7b0a.chunk.js} +1 -1
- package/build/{vi-json.bf3424be.chunk.js → vi-json.f7890025.chunk.js} +1 -1
- package/build/{zh-Hans-json.9c99f8d4.chunk.js → zh-Hans-json.a4d7dc69.chunk.js} +1 -1
- package/build/{zh-json.451a0271.chunk.js → zh-json.66aa2ae1.chunk.js} +1 -1
- package/package.json +7 -7
- package/admin/src/content-manager/components/SelectMany/ListItem.js +0 -102
- package/admin/src/content-manager/components/SelectMany/index.js +0 -148
- package/admin/src/content-manager/components/SelectOne/SingleValue.js +0 -67
- package/admin/src/content-manager/components/SelectOne/index.js +0 -97
- package/admin/src/content-manager/components/SelectWrapper/Label.js +0 -60
- package/admin/src/content-manager/components/SelectWrapper/index.js +0 -356
- package/admin/src/content-manager/components/SelectWrapper/utils/index.js +0 -2
- package/admin/src/content-manager/components/SelectWrapper/utils/select.js +0 -45
- package/admin/src/content-manager/pages/EditSettingsView/components/RelationalFieldButton.js +0 -135
- package/admin/src/content-manager/pages/EditSettingsView/components/RelationalFields.js +0 -103
- package/admin/src/content-manager/pages/EditView/Header/utils/getDraftRelations.js +0 -62
- package/build/1669.d1b29c28.chunk.js +0 -1
- package/build/524.8a540ac1.chunk.js +0 -644
- package/build/Admin-authenticatedApp.88fa40ac.chunk.js +0 -80
- package/build/content-manager.8bddf2e6.chunk.js +0 -1178
- package/build/main.6650d2e7.js +0 -9338
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { memo, useCallback, useEffect, useMemo, useRef } from 'react';
|
|
2
|
+
import { useQueryClient } from 'react-query';
|
|
2
3
|
import { useHistory } from 'react-router-dom';
|
|
3
4
|
import axios from 'axios';
|
|
4
5
|
import get from 'lodash/get';
|
|
@@ -34,6 +35,7 @@ import selectCrudReducer from '../../sharedReducers/crudReducer/selectors';
|
|
|
34
35
|
|
|
35
36
|
// This container is used to handle the CRUD
|
|
36
37
|
const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }) => {
|
|
38
|
+
const queryClient = useQueryClient();
|
|
37
39
|
const toggleNotification = useNotification();
|
|
38
40
|
const { setCurrentStep } = useGuidedTour();
|
|
39
41
|
const { trackUsage } = useTracking();
|
|
@@ -260,7 +262,11 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
|
|
|
260
262
|
|
|
261
263
|
setCurrentStep('contentManager.success');
|
|
262
264
|
|
|
265
|
+
// TODO: need to find a better place, or a better abstraction
|
|
266
|
+
queryClient.invalidateQueries(['relation']);
|
|
267
|
+
|
|
263
268
|
dispatch(submitSucceeded(cleanReceivedData(data)));
|
|
269
|
+
|
|
264
270
|
// Enable navigation and remove loaders
|
|
265
271
|
dispatch(setStatus('resolved'));
|
|
266
272
|
|
|
@@ -284,9 +290,33 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
|
|
|
284
290
|
rawQuery,
|
|
285
291
|
toggleNotification,
|
|
286
292
|
setCurrentStep,
|
|
293
|
+
queryClient,
|
|
287
294
|
]
|
|
288
295
|
);
|
|
289
296
|
|
|
297
|
+
const onDraftRelationCheck = useCallback(async () => {
|
|
298
|
+
try {
|
|
299
|
+
trackUsageRef.current('willCheckDraftRelations');
|
|
300
|
+
|
|
301
|
+
const endPoint = getRequestUrl(
|
|
302
|
+
`collection-types/${slug}/${id}/actions/numberOfDraftRelations`
|
|
303
|
+
);
|
|
304
|
+
dispatch(setStatus('draft-relation-check-pending'));
|
|
305
|
+
|
|
306
|
+
const numberOfDraftRelations = await axiosInstance.get(endPoint);
|
|
307
|
+
trackUsageRef.current('didCheckDraftRelations');
|
|
308
|
+
|
|
309
|
+
dispatch(setStatus('resolved'));
|
|
310
|
+
|
|
311
|
+
return numberOfDraftRelations.data.data;
|
|
312
|
+
} catch (err) {
|
|
313
|
+
displayErrors(err);
|
|
314
|
+
dispatch(setStatus('resolved'));
|
|
315
|
+
|
|
316
|
+
return Promise.reject(err);
|
|
317
|
+
}
|
|
318
|
+
}, [displayErrors, id, slug, dispatch]);
|
|
319
|
+
|
|
290
320
|
const onPublish = useCallback(async () => {
|
|
291
321
|
try {
|
|
292
322
|
trackUsageRef.current('willPublishEntry');
|
|
@@ -332,6 +362,9 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
|
|
|
332
362
|
message: { id: getTrad('success.record.save') },
|
|
333
363
|
});
|
|
334
364
|
|
|
365
|
+
// TODO: need to find a better place, or a better abstraction
|
|
366
|
+
queryClient.invalidateQueries(['relation']);
|
|
367
|
+
|
|
335
368
|
dispatch(submitSucceeded(cleanReceivedData(data)));
|
|
336
369
|
|
|
337
370
|
dispatch(setStatus('resolved'));
|
|
@@ -346,7 +379,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
|
|
|
346
379
|
return Promise.reject(err);
|
|
347
380
|
}
|
|
348
381
|
},
|
|
349
|
-
[cleanReceivedData, displayErrors, slug, id, dispatch, toggleNotification]
|
|
382
|
+
[cleanReceivedData, displayErrors, slug, id, dispatch, toggleNotification, queryClient]
|
|
350
383
|
);
|
|
351
384
|
|
|
352
385
|
const onUnpublish = useCallback(async () => {
|
|
@@ -387,6 +420,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
|
|
|
387
420
|
onDeleteSucceeded,
|
|
388
421
|
onPost,
|
|
389
422
|
onPublish,
|
|
423
|
+
onDraftRelationCheck,
|
|
390
424
|
onPut,
|
|
391
425
|
onUnpublish,
|
|
392
426
|
status,
|
package/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
1
|
+
import React, { useState, useMemo } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import { useQuery } from 'react-query';
|
|
4
4
|
import { useIntl } from 'react-intl';
|
|
@@ -28,10 +28,12 @@ const fetchRelation = async (endPoint, notifyStatus) => {
|
|
|
28
28
|
return { results, pagination };
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
-
const RelationMultiple = ({ fieldSchema, metadatas,
|
|
31
|
+
const RelationMultiple = ({ fieldSchema, metadatas, name, entityId, value, contentType }) => {
|
|
32
32
|
const { formatMessage } = useIntl();
|
|
33
33
|
const { notifyStatus } = useNotifyAT();
|
|
34
|
-
const
|
|
34
|
+
const relationFetchEndpoint = useMemo(() => {
|
|
35
|
+
return getRequestUrl(`collection-types/${contentType.uid}/${entityId}/${name.split('.')[0]}`);
|
|
36
|
+
}, [entityId, name, contentType]);
|
|
35
37
|
const [isOpen, setIsOpen] = useState(false);
|
|
36
38
|
|
|
37
39
|
const Label = (
|
|
@@ -56,8 +58,8 @@ const RelationMultiple = ({ fieldSchema, metadatas, queryInfos, name, rowId, val
|
|
|
56
58
|
};
|
|
57
59
|
|
|
58
60
|
const { data, status } = useQuery(
|
|
59
|
-
[fieldSchema.targetModel,
|
|
60
|
-
() => fetchRelation(
|
|
61
|
+
[fieldSchema.targetModel, entityId],
|
|
62
|
+
() => fetchRelation(relationFetchEndpoint, notify),
|
|
61
63
|
{
|
|
62
64
|
enabled: isOpen,
|
|
63
65
|
staleTime: 0,
|
|
@@ -115,6 +117,9 @@ const RelationMultiple = ({ fieldSchema, metadatas, queryInfos, name, rowId, val
|
|
|
115
117
|
};
|
|
116
118
|
|
|
117
119
|
RelationMultiple.propTypes = {
|
|
120
|
+
contentType: PropTypes.shape({
|
|
121
|
+
uid: PropTypes.string.isRequired,
|
|
122
|
+
}).isRequired,
|
|
118
123
|
fieldSchema: PropTypes.shape({
|
|
119
124
|
relation: PropTypes.string,
|
|
120
125
|
targetModel: PropTypes.string,
|
|
@@ -127,8 +132,7 @@ RelationMultiple.propTypes = {
|
|
|
127
132
|
}),
|
|
128
133
|
}).isRequired,
|
|
129
134
|
name: PropTypes.string.isRequired,
|
|
130
|
-
|
|
131
|
-
queryInfos: PropTypes.shape({ endPoint: PropTypes.string.isRequired }).isRequired,
|
|
135
|
+
entityId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
|
132
136
|
value: PropTypes.object.isRequired,
|
|
133
137
|
};
|
|
134
138
|
|
|
@@ -16,7 +16,7 @@ const TypographyMaxWidth = styled(Typography)`
|
|
|
16
16
|
max-width: 300px;
|
|
17
17
|
`;
|
|
18
18
|
|
|
19
|
-
const CellContent = ({ content, fieldSchema, metadatas, name,
|
|
19
|
+
const CellContent = ({ content, fieldSchema, metadatas, name, rowId, contentType }) => {
|
|
20
20
|
const { type } = fieldSchema;
|
|
21
21
|
|
|
22
22
|
if (!hasContent(type, content, metadatas, fieldSchema)) {
|
|
@@ -39,11 +39,11 @@ const CellContent = ({ content, fieldSchema, metadatas, name, queryInfos, rowId
|
|
|
39
39
|
return (
|
|
40
40
|
<RelationMultiple
|
|
41
41
|
fieldSchema={fieldSchema}
|
|
42
|
-
queryInfos={queryInfos}
|
|
43
42
|
metadatas={metadatas}
|
|
44
43
|
value={content}
|
|
45
44
|
name={name}
|
|
46
|
-
|
|
45
|
+
entityId={rowId}
|
|
46
|
+
contentType={contentType}
|
|
47
47
|
/>
|
|
48
48
|
);
|
|
49
49
|
}
|
|
@@ -66,11 +66,13 @@ const CellContent = ({ content, fieldSchema, metadatas, name, queryInfos, rowId
|
|
|
66
66
|
|
|
67
67
|
CellContent.defaultProps = {
|
|
68
68
|
content: undefined,
|
|
69
|
-
queryInfos: undefined,
|
|
70
69
|
};
|
|
71
70
|
|
|
72
71
|
CellContent.propTypes = {
|
|
73
72
|
content: PropTypes.any,
|
|
73
|
+
contentType: PropTypes.shape({
|
|
74
|
+
uid: PropTypes.string.isRequired,
|
|
75
|
+
}).isRequired,
|
|
74
76
|
fieldSchema: PropTypes.shape({
|
|
75
77
|
component: PropTypes.string,
|
|
76
78
|
multiple: PropTypes.bool,
|
|
@@ -81,7 +83,6 @@ CellContent.propTypes = {
|
|
|
81
83
|
metadatas: PropTypes.object.isRequired,
|
|
82
84
|
name: PropTypes.string.isRequired,
|
|
83
85
|
rowId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
|
84
|
-
queryInfos: PropTypes.shape({ endPoint: PropTypes.string.isRequired }),
|
|
85
86
|
};
|
|
86
87
|
|
|
87
88
|
export default CellContent;
|
|
@@ -24,6 +24,7 @@ import CellContent from '../CellContent';
|
|
|
24
24
|
const TableRows = ({
|
|
25
25
|
canCreate,
|
|
26
26
|
canDelete,
|
|
27
|
+
contentType,
|
|
27
28
|
headers,
|
|
28
29
|
entriesToDelete,
|
|
29
30
|
onClickDelete,
|
|
@@ -94,6 +95,7 @@ const TableRows = ({
|
|
|
94
95
|
<CellContent
|
|
95
96
|
content={data[name.split('.')[0]]}
|
|
96
97
|
name={name}
|
|
98
|
+
contentType={contentType}
|
|
97
99
|
{...rest}
|
|
98
100
|
rowId={data.id}
|
|
99
101
|
/>
|
|
@@ -185,6 +187,9 @@ TableRows.defaultProps = {
|
|
|
185
187
|
TableRows.propTypes = {
|
|
186
188
|
canCreate: PropTypes.bool,
|
|
187
189
|
canDelete: PropTypes.bool,
|
|
190
|
+
contentType: PropTypes.shape({
|
|
191
|
+
uid: PropTypes.string.isRequired,
|
|
192
|
+
}).isRequired,
|
|
188
193
|
entriesToDelete: PropTypes.array,
|
|
189
194
|
headers: PropTypes.array.isRequired,
|
|
190
195
|
onClickDelete: PropTypes.func,
|
|
@@ -112,6 +112,7 @@ const DynamicTable = ({
|
|
|
112
112
|
<TableRows
|
|
113
113
|
canCreate={canCreate}
|
|
114
114
|
canDelete={canDelete}
|
|
115
|
+
contentType={layout.contentType}
|
|
115
116
|
headers={tableHeaders}
|
|
116
117
|
rows={rows}
|
|
117
118
|
withBulkActions
|
|
@@ -139,7 +140,6 @@ DynamicTable.propTypes = {
|
|
|
139
140
|
metadatas: PropTypes.object.isRequired,
|
|
140
141
|
layouts: PropTypes.shape({
|
|
141
142
|
list: PropTypes.array.isRequired,
|
|
142
|
-
editRelations: PropTypes.array,
|
|
143
143
|
}).isRequired,
|
|
144
144
|
options: PropTypes.object.isRequired,
|
|
145
145
|
settings: PropTypes.object.isRequired,
|
|
@@ -36,6 +36,7 @@ const EditViewDataManagerProvider = ({
|
|
|
36
36
|
isSingleType,
|
|
37
37
|
onPost,
|
|
38
38
|
onPublish,
|
|
39
|
+
onDraftRelationCheck,
|
|
39
40
|
onPut,
|
|
40
41
|
onUnpublish,
|
|
41
42
|
readActionAllowedFields,
|
|
@@ -46,7 +47,15 @@ const EditViewDataManagerProvider = ({
|
|
|
46
47
|
updateActionAllowedFields,
|
|
47
48
|
}) => {
|
|
48
49
|
const [reducerState, dispatch] = useReducer(reducer, initialState);
|
|
49
|
-
const {
|
|
50
|
+
const {
|
|
51
|
+
formErrors,
|
|
52
|
+
initialData,
|
|
53
|
+
modifiedData,
|
|
54
|
+
modifiedDZName,
|
|
55
|
+
shouldCheckErrors,
|
|
56
|
+
publishConfirmation,
|
|
57
|
+
} = reducerState;
|
|
58
|
+
|
|
50
59
|
const toggleNotification = useNotification();
|
|
51
60
|
const { lockApp, unlockApp } = useOverlayBlocker();
|
|
52
61
|
|
|
@@ -130,12 +139,17 @@ const EditViewDataManagerProvider = ({
|
|
|
130
139
|
|
|
131
140
|
useEffect(() => {
|
|
132
141
|
if (initialValues) {
|
|
142
|
+
const relationalFields = Object.keys(initialValues).filter(
|
|
143
|
+
(key) => currentContentTypeLayout?.attributes[key]?.type === 'relation'
|
|
144
|
+
);
|
|
145
|
+
|
|
133
146
|
dispatch({
|
|
134
147
|
type: 'INIT_FORM',
|
|
135
148
|
initialValues,
|
|
149
|
+
relationalFields,
|
|
136
150
|
});
|
|
137
151
|
}
|
|
138
|
-
}, [initialValues]);
|
|
152
|
+
}, [initialValues, currentContentTypeLayout]);
|
|
139
153
|
|
|
140
154
|
const addComponentToDynamicZone = useCallback((keys, componentUid, shouldCheckErrors = false) => {
|
|
141
155
|
trackUsageRef.current('didAddComponentToDynamicZone');
|
|
@@ -156,9 +170,18 @@ const EditViewDataManagerProvider = ({
|
|
|
156
170
|
});
|
|
157
171
|
}, []);
|
|
158
172
|
|
|
159
|
-
const
|
|
173
|
+
const connectRelation = useCallback(({ target: { name, value, replace } }) => {
|
|
174
|
+
dispatch({
|
|
175
|
+
type: 'CONNECT_RELATION',
|
|
176
|
+
keys: name.split('.'),
|
|
177
|
+
value,
|
|
178
|
+
replace,
|
|
179
|
+
});
|
|
180
|
+
}, []);
|
|
181
|
+
|
|
182
|
+
const loadRelation = useCallback(({ target: { name, value } }) => {
|
|
160
183
|
dispatch({
|
|
161
|
-
type: '
|
|
184
|
+
type: 'LOAD_RELATION',
|
|
162
185
|
keys: name.split('.'),
|
|
163
186
|
value,
|
|
164
187
|
});
|
|
@@ -287,6 +310,14 @@ const EditViewDataManagerProvider = ({
|
|
|
287
310
|
return shouldNotRunValidations ? { status: 'draft' } : {};
|
|
288
311
|
}, [hasDraftAndPublish, shouldNotRunValidations]);
|
|
289
312
|
|
|
313
|
+
const handlePublishPromptDismissal = useCallback(async (e) => {
|
|
314
|
+
e.preventDefault();
|
|
315
|
+
|
|
316
|
+
return dispatch({
|
|
317
|
+
type: 'RESET_PUBLISH_CONFIRMATION',
|
|
318
|
+
});
|
|
319
|
+
}, []);
|
|
320
|
+
|
|
290
321
|
const handleSubmit = useCallback(
|
|
291
322
|
async (e) => {
|
|
292
323
|
e.preventDefault();
|
|
@@ -332,8 +363,27 @@ const EditViewDataManagerProvider = ({
|
|
|
332
363
|
},
|
|
333
364
|
{ isCreatingEntry, isDraft: false, isFromComponent: false }
|
|
334
365
|
);
|
|
335
|
-
let errors = {};
|
|
336
366
|
|
|
367
|
+
const draftCount = await onDraftRelationCheck();
|
|
368
|
+
|
|
369
|
+
if (!publishConfirmation.show && draftCount > 0) {
|
|
370
|
+
// If the warning hasn't already been shown and draft relations are found,
|
|
371
|
+
// abort the publish call and ask for confirmation from the user
|
|
372
|
+
dispatch({
|
|
373
|
+
type: 'SET_PUBLISH_CONFIRMATION',
|
|
374
|
+
publishConfirmation: {
|
|
375
|
+
show: true,
|
|
376
|
+
draftCount,
|
|
377
|
+
},
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
dispatch({
|
|
383
|
+
type: 'RESET_PUBLISH_CONFIRMATION',
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
let errors = {};
|
|
337
387
|
try {
|
|
338
388
|
await schema.validate(modifiedData, { abortEarly: false });
|
|
339
389
|
} catch (err) {
|
|
@@ -355,7 +405,15 @@ const EditViewDataManagerProvider = ({
|
|
|
355
405
|
type: 'SET_FORM_ERRORS',
|
|
356
406
|
errors,
|
|
357
407
|
});
|
|
358
|
-
}, [
|
|
408
|
+
}, [
|
|
409
|
+
allLayoutData,
|
|
410
|
+
currentContentTypeLayout,
|
|
411
|
+
isCreatingEntry,
|
|
412
|
+
modifiedData,
|
|
413
|
+
publishConfirmation.show,
|
|
414
|
+
onPublish,
|
|
415
|
+
onDraftRelationCheck,
|
|
416
|
+
]);
|
|
359
417
|
|
|
360
418
|
const shouldCheckDZErrors = useCallback(
|
|
361
419
|
(dzName) => {
|
|
@@ -404,19 +462,11 @@ const EditViewDataManagerProvider = ({
|
|
|
404
462
|
});
|
|
405
463
|
}, []);
|
|
406
464
|
|
|
407
|
-
const
|
|
465
|
+
const disconnectRelation = useCallback(({ target: { name, value } }) => {
|
|
408
466
|
dispatch({
|
|
409
|
-
type: '
|
|
410
|
-
dragIndex,
|
|
411
|
-
overIndex,
|
|
467
|
+
type: 'DISCONNECT_RELATION',
|
|
412
468
|
keys: name.split('.'),
|
|
413
|
-
|
|
414
|
-
}, []);
|
|
415
|
-
|
|
416
|
-
const onRemoveRelation = useCallback((keys) => {
|
|
417
|
-
dispatch({
|
|
418
|
-
type: 'REMOVE_RELATION',
|
|
419
|
-
keys,
|
|
469
|
+
value,
|
|
420
470
|
});
|
|
421
471
|
}, []);
|
|
422
472
|
|
|
@@ -470,7 +520,7 @@ const EditViewDataManagerProvider = ({
|
|
|
470
520
|
value={{
|
|
471
521
|
addComponentToDynamicZone,
|
|
472
522
|
addNonRepeatableComponentToField,
|
|
473
|
-
|
|
523
|
+
connectRelation,
|
|
474
524
|
addRepeatableComponentToField,
|
|
475
525
|
allLayoutData,
|
|
476
526
|
checkFormErrors,
|
|
@@ -483,15 +533,15 @@ const EditViewDataManagerProvider = ({
|
|
|
483
533
|
shouldNotRunValidations,
|
|
484
534
|
status,
|
|
485
535
|
layout: currentContentTypeLayout,
|
|
536
|
+
loadRelation,
|
|
486
537
|
modifiedData,
|
|
487
538
|
moveComponentDown,
|
|
488
539
|
moveComponentField,
|
|
489
540
|
moveComponentUp,
|
|
490
|
-
moveRelation,
|
|
491
541
|
onChange: handleChange,
|
|
492
542
|
onPublish: handlePublish,
|
|
493
543
|
onUnpublish,
|
|
494
|
-
|
|
544
|
+
disconnectRelation,
|
|
495
545
|
readActionAllowedFields,
|
|
496
546
|
redirectToPreviousPage,
|
|
497
547
|
removeComponentFromDynamicZone,
|
|
@@ -500,6 +550,8 @@ const EditViewDataManagerProvider = ({
|
|
|
500
550
|
slug,
|
|
501
551
|
triggerFormValidation,
|
|
502
552
|
updateActionAllowedFields,
|
|
553
|
+
onPublishPromptDismissal: handlePublishPromptDismissal,
|
|
554
|
+
publishConfirmation,
|
|
503
555
|
}}
|
|
504
556
|
>
|
|
505
557
|
<>
|
|
@@ -543,6 +595,7 @@ EditViewDataManagerProvider.propTypes = {
|
|
|
543
595
|
isSingleType: PropTypes.bool.isRequired,
|
|
544
596
|
onPost: PropTypes.func.isRequired,
|
|
545
597
|
onPublish: PropTypes.func.isRequired,
|
|
598
|
+
onDraftRelationCheck: PropTypes.func.isRequired,
|
|
546
599
|
onPut: PropTypes.func.isRequired,
|
|
547
600
|
onUnpublish: PropTypes.func.isRequired,
|
|
548
601
|
readActionAllowedFields: PropTypes.array.isRequired,
|
|
@@ -3,6 +3,7 @@ import unset from 'lodash/unset';
|
|
|
3
3
|
import get from 'lodash/get';
|
|
4
4
|
import set from 'lodash/set';
|
|
5
5
|
import take from 'lodash/take';
|
|
6
|
+
import pull from 'lodash/pull';
|
|
6
7
|
import { moveFields } from './utils';
|
|
7
8
|
import { getMaxTempKey } from '../../utils';
|
|
8
9
|
|
|
@@ -14,6 +15,10 @@ const initialState = {
|
|
|
14
15
|
modifiedData: null,
|
|
15
16
|
shouldCheckErrors: false,
|
|
16
17
|
modifiedDZName: null,
|
|
18
|
+
publishConfirmation: {
|
|
19
|
+
show: false,
|
|
20
|
+
draftCount: 0,
|
|
21
|
+
},
|
|
17
22
|
};
|
|
18
23
|
|
|
19
24
|
const reducer = (state, action) =>
|
|
@@ -72,29 +77,170 @@ const reducer = (state, action) =>
|
|
|
72
77
|
|
|
73
78
|
break;
|
|
74
79
|
}
|
|
75
|
-
case '
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
80
|
+
case 'LOAD_RELATION': {
|
|
81
|
+
const initialDataPath = ['initialData', ...action.keys, 'results'];
|
|
82
|
+
const modifiedDataPath = ['modifiedData', ...action.keys, 'results'];
|
|
83
|
+
const { value } = action;
|
|
79
84
|
|
|
80
|
-
|
|
85
|
+
set(draftState, initialDataPath, value);
|
|
81
86
|
|
|
82
|
-
|
|
87
|
+
/**
|
|
88
|
+
* We need to set the value also on modifiedData, because initialData
|
|
89
|
+
* and modifiedData need to stay in sync, so that the CM can compare
|
|
90
|
+
* both states, to render the dirty UI state
|
|
91
|
+
*/
|
|
83
92
|
|
|
84
|
-
|
|
85
|
-
set(draftState, ['modifiedData', ...action.keys], [el]);
|
|
93
|
+
set(draftState, modifiedDataPath, value);
|
|
86
94
|
|
|
87
|
-
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
case 'CONNECT_RELATION': {
|
|
98
|
+
const path = ['modifiedData', ...action.keys];
|
|
99
|
+
const { value, replace = false } = action;
|
|
100
|
+
const connectedRelations = get(state, [...path, 'connect']);
|
|
101
|
+
const disconnectedRelations = get(state, [...path, 'disconnect']) ?? [];
|
|
102
|
+
const savedRelations = get(state, [...path, 'results']) ?? [];
|
|
103
|
+
const existInSavedRelation =
|
|
104
|
+
savedRelations?.findIndex((savedRelations) => savedRelations.id === value.id) !== -1;
|
|
105
|
+
|
|
106
|
+
if (!connectedRelations) {
|
|
107
|
+
set(draftState, [...path, 'connect'], []);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// We should add a relation in the connect array only if it is not an already saved relation
|
|
111
|
+
if (!existInSavedRelation) {
|
|
112
|
+
if (replace) {
|
|
113
|
+
set(draftState, [...path, 'connect'], [value]);
|
|
114
|
+
} else {
|
|
115
|
+
const nextValue = get(draftState, [...path, 'connect']);
|
|
116
|
+
nextValue.push(value);
|
|
117
|
+
}
|
|
88
118
|
}
|
|
89
119
|
|
|
90
|
-
|
|
120
|
+
// Disconnect array handling
|
|
121
|
+
if (replace) {
|
|
122
|
+
// In xToOne relations we should place the saved relation in disconnected array to not display it
|
|
123
|
+
// only needed if there is a saved relation and it is not already stored in disconnected array
|
|
124
|
+
if (savedRelations.length && !disconnectedRelations.length) {
|
|
125
|
+
set(draftState, [...path, 'disconnect'], savedRelations);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// If the saved relation is stored in disconnected array
|
|
129
|
+
// We should remove it when an action requires to reconnect this relation
|
|
130
|
+
// We then reset the connect/disconnect state
|
|
131
|
+
if (disconnectedRelations.length) {
|
|
132
|
+
const existsInDisconnectedRelations =
|
|
133
|
+
disconnectedRelations.findIndex(
|
|
134
|
+
(disconnectedRelation) => disconnectedRelation?.id === value.id
|
|
135
|
+
) > -1;
|
|
136
|
+
|
|
137
|
+
if (existsInDisconnectedRelations) {
|
|
138
|
+
set(draftState, [...path, 'disconnect'], []);
|
|
139
|
+
set(draftState, [...path, 'connect'], []);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
} else if (disconnectedRelations.length) {
|
|
143
|
+
// In xToMany relations, when an action requires to connect a relation
|
|
144
|
+
// We should remove it from the disconnected array if it existed in it
|
|
145
|
+
const existsInDisconnect = disconnectedRelations.find(
|
|
146
|
+
(disconnectValue) => disconnectValue.id === value.id
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
if (existsInDisconnect) {
|
|
150
|
+
const newDisconnectArray = pull([...disconnectedRelations], existsInDisconnect);
|
|
151
|
+
set(draftState, [...path, 'disconnect'], newDisconnectArray);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
case 'DISCONNECT_RELATION': {
|
|
158
|
+
const path = ['modifiedData', ...action.keys];
|
|
159
|
+
const { value } = action;
|
|
160
|
+
const connectedRelations = get(state, [...path, 'connect']);
|
|
161
|
+
const disconnectedRelations = get(state, [...path, 'disconnect']);
|
|
162
|
+
|
|
163
|
+
if (!disconnectedRelations) {
|
|
164
|
+
set(draftState, [...path, 'disconnect'], []);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const nextValue = get(draftState, [...path, 'disconnect']);
|
|
168
|
+
nextValue.push(value);
|
|
169
|
+
|
|
170
|
+
if (connectedRelations?.length) {
|
|
171
|
+
const existsInConnect = connectedRelations.find(
|
|
172
|
+
(connectValue) => connectValue.id === value.id
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
if (existsInConnect) {
|
|
176
|
+
const newConnectArray = pull([...connectedRelations], existsInConnect);
|
|
177
|
+
set(draftState, [...path, 'connect'], newConnectArray);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
91
180
|
|
|
92
181
|
break;
|
|
93
182
|
}
|
|
94
183
|
case 'INIT_FORM': {
|
|
184
|
+
const { initialValues, relationalFields = [] } = action;
|
|
185
|
+
|
|
95
186
|
draftState.formErrors = {};
|
|
96
|
-
|
|
97
|
-
draftState.
|
|
187
|
+
|
|
188
|
+
draftState.initialData = {
|
|
189
|
+
...initialValues,
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* The state we keep in the client for relations looks like:
|
|
193
|
+
*
|
|
194
|
+
* {
|
|
195
|
+
* count: <int>
|
|
196
|
+
* results: [<Relation>]
|
|
197
|
+
* }
|
|
198
|
+
*
|
|
199
|
+
* The content API only returns { count: <int> }, which is why
|
|
200
|
+
* we need to extend the existing state rather than overwriting it.
|
|
201
|
+
*/
|
|
202
|
+
|
|
203
|
+
...relationalFields.reduce((acc, name) => {
|
|
204
|
+
acc[name] = {
|
|
205
|
+
...(state.initialData?.[name] ?? {}),
|
|
206
|
+
...(initialValues?.[name] ?? {}),
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
return acc;
|
|
210
|
+
}, {}),
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
draftState.modifiedData = {
|
|
214
|
+
...initialValues,
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* The client sends the following to the content API:
|
|
218
|
+
*
|
|
219
|
+
* {
|
|
220
|
+
* connect: [<Relation>],
|
|
221
|
+
* disconnect: [<Relation>]
|
|
222
|
+
* }
|
|
223
|
+
*
|
|
224
|
+
* but receives only { count: <int> } in return. After save/ publish
|
|
225
|
+
* we have to:
|
|
226
|
+
*
|
|
227
|
+
* 1) reset the connect/ disconnect arrays
|
|
228
|
+
* 2) extend the existing state with the API response, so that `count`
|
|
229
|
+
* stays in sync
|
|
230
|
+
*/
|
|
231
|
+
|
|
232
|
+
...relationalFields.reduce((acc, name) => {
|
|
233
|
+
const { connect, disconnect, ...currentState } = state.modifiedData?.[name] ?? {};
|
|
234
|
+
|
|
235
|
+
acc[name] = {
|
|
236
|
+
...(currentState ?? {}),
|
|
237
|
+
...(initialValues?.[name] ?? {}),
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
return acc;
|
|
241
|
+
}, {}),
|
|
242
|
+
};
|
|
243
|
+
|
|
98
244
|
draftState.modifiedDZName = null;
|
|
99
245
|
draftState.shouldCheckErrors = false;
|
|
100
246
|
break;
|
|
@@ -213,19 +359,6 @@ const reducer = (state, action) =>
|
|
|
213
359
|
|
|
214
360
|
break;
|
|
215
361
|
}
|
|
216
|
-
case 'REMOVE_RELATION': {
|
|
217
|
-
const pathArray = action.keys.split('.');
|
|
218
|
-
const pathArrayLength = pathArray.length - 1;
|
|
219
|
-
const pathToData = ['modifiedData', ...take(pathArray, pathArrayLength)];
|
|
220
|
-
const currentValue = get(state, pathToData).slice();
|
|
221
|
-
const indexToRemove = parseInt(pathArray[pathArrayLength], 10);
|
|
222
|
-
|
|
223
|
-
currentValue.splice(indexToRemove, 1);
|
|
224
|
-
|
|
225
|
-
set(draftState, pathToData, currentValue);
|
|
226
|
-
|
|
227
|
-
break;
|
|
228
|
-
}
|
|
229
362
|
case 'SET_DEFAULT_DATA_STRUCTURES': {
|
|
230
363
|
draftState.componentsDataStructure = action.componentsDataStructure;
|
|
231
364
|
draftState.contentTypeDataStructure = action.contentTypeDataStructure;
|
|
@@ -246,7 +379,14 @@ const reducer = (state, action) =>
|
|
|
246
379
|
|
|
247
380
|
break;
|
|
248
381
|
}
|
|
249
|
-
|
|
382
|
+
case 'SET_PUBLISH_CONFIRMATION': {
|
|
383
|
+
draftState.publishConfirmation = { ...action.publishConfirmation };
|
|
384
|
+
break;
|
|
385
|
+
}
|
|
386
|
+
case 'RESET_PUBLISH_CONFIRMATION': {
|
|
387
|
+
draftState.publishConfirmation = { ...state.publishConfirmation, show: false };
|
|
388
|
+
break;
|
|
389
|
+
}
|
|
250
390
|
default:
|
|
251
391
|
return draftState;
|
|
252
392
|
}
|
|
@@ -51,6 +51,19 @@ const cleanData = (retrievedData, currentSchema, componentsSchema) => {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
break;
|
|
54
|
+
|
|
55
|
+
case 'relation':
|
|
56
|
+
// Instead of the full relation object, we only want to send its ID
|
|
57
|
+
// and need to clean-up the connect|disconnect arrays
|
|
58
|
+
cleanedData = Object.entries(value).reduce((acc, [key, value]) => {
|
|
59
|
+
if (['connect', 'disconnect'].includes(key)) {
|
|
60
|
+
acc[key] = value.map((currentValue) => ({ id: currentValue.id }));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return acc;
|
|
64
|
+
}, {});
|
|
65
|
+
break;
|
|
66
|
+
|
|
54
67
|
case 'dynamiczone':
|
|
55
68
|
cleanedData = value.map((componentData) => {
|
|
56
69
|
const subCleanedData = recursiveCleanData(
|
|
@@ -62,7 +75,6 @@ const cleanData = (retrievedData, currentSchema, componentsSchema) => {
|
|
|
62
75
|
});
|
|
63
76
|
break;
|
|
64
77
|
default:
|
|
65
|
-
// The helper is mainly used for the relations in order to just send the id
|
|
66
78
|
cleanedData = helperCleanData(value, 'id');
|
|
67
79
|
}
|
|
68
80
|
|
|
@@ -75,6 +87,10 @@ const cleanData = (retrievedData, currentSchema, componentsSchema) => {
|
|
|
75
87
|
return recursiveCleanData(retrievedData, currentSchema);
|
|
76
88
|
};
|
|
77
89
|
|
|
90
|
+
// TODO: check which parts are still needed: I suspect the
|
|
91
|
+
// isArray part can go away, but I'm not sure what could send
|
|
92
|
+
// an object; in case both can go away we might be able to get
|
|
93
|
+
// rid of the whole helper
|
|
78
94
|
export const helperCleanData = (value, key) => {
|
|
79
95
|
if (isArray(value)) {
|
|
80
96
|
return value.map((obj) => (obj[key] ? obj[key] : obj));
|