@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
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
import { Box } from '@strapi/design-system/Box';
|
|
5
|
+
import { Stack } from '@strapi/design-system/Stack';
|
|
6
|
+
|
|
7
|
+
const ShadowBox = styled(Box)`
|
|
8
|
+
position: relative;
|
|
9
|
+
overflow-x: hidden;
|
|
10
|
+
overflow-y: auto;
|
|
11
|
+
|
|
12
|
+
&:before,
|
|
13
|
+
&:after {
|
|
14
|
+
position: absolute;
|
|
15
|
+
width: 100%;
|
|
16
|
+
height: 4px;
|
|
17
|
+
z-index: 1;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
&:before {
|
|
21
|
+
/* TODO: as for DS Table component we would need this to be handled by the DS theme */
|
|
22
|
+
content: ${({ overflowDirection }) =>
|
|
23
|
+
overflowDirection === 'top-bottom' || overflowDirection === 'top' ? "''" : undefined};
|
|
24
|
+
background: linear-gradient(rgba(33, 33, 52, 0.1) 0%, rgba(0, 0, 0, 0) 100%);
|
|
25
|
+
top: 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
&:after {
|
|
29
|
+
/* TODO: as for DS Table component we would need this to be handled by the DS theme */
|
|
30
|
+
content: ${({ overflowDirection }) =>
|
|
31
|
+
overflowDirection === 'top-bottom' || overflowDirection === 'bottom' ? "''" : undefined};
|
|
32
|
+
background: linear-gradient(rgba(0, 0, 0, 0) 0%, rgba(33, 33, 52, 0.1) 100%);
|
|
33
|
+
bottom: 0;
|
|
34
|
+
}
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
export const RelationList = ({ children, overflow, ...props }) => {
|
|
38
|
+
return (
|
|
39
|
+
<ShadowBox overflowDirection={overflow} {...props}>
|
|
40
|
+
<Stack spacing={1}>{children}</Stack>
|
|
41
|
+
</ShadowBox>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
RelationList.defaultProps = {
|
|
46
|
+
overflow: '',
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
RelationList.propTypes = {
|
|
50
|
+
children: PropTypes.node.isRequired,
|
|
51
|
+
overflow: PropTypes.oneOf(['top-bottom', 'bottom', 'top', '']),
|
|
52
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const RELATION_ITEM_HEIGHT = 50;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as RelationInput } from './RelationInput';
|
package/admin/src/content-manager/components/RelationInputDataManager/RelationInputDataManager.js
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import PropTypes from 'prop-types';
|
|
2
|
+
import React, { memo, useEffect, useMemo } from 'react';
|
|
3
|
+
import { useIntl } from 'react-intl';
|
|
4
|
+
import get from 'lodash/get';
|
|
5
|
+
|
|
6
|
+
import { useCMEditViewDataManager, NotAllowedInput } from '@strapi/helper-plugin';
|
|
7
|
+
|
|
8
|
+
import { RelationInput } from '../RelationInput';
|
|
9
|
+
import { useRelation } from '../../hooks/useRelation';
|
|
10
|
+
import { connect, select, normalizeRelations, normalizeSearchResults } from './utils';
|
|
11
|
+
import { PUBLICATION_STATES, RELATIONS_TO_DISPLAY, SEARCH_RESULTS_TO_DISPLAY } from './constants';
|
|
12
|
+
import { getTrad } from '../../utils';
|
|
13
|
+
|
|
14
|
+
export const RelationInputDataManager = ({
|
|
15
|
+
componentId,
|
|
16
|
+
editable,
|
|
17
|
+
description,
|
|
18
|
+
intlLabel,
|
|
19
|
+
isCreatingEntry,
|
|
20
|
+
isFieldAllowed,
|
|
21
|
+
isFieldReadable,
|
|
22
|
+
labelAction,
|
|
23
|
+
mainField,
|
|
24
|
+
name,
|
|
25
|
+
queryInfos: { endpoints, defaultParams, shouldDisplayRelationLink },
|
|
26
|
+
placeholder,
|
|
27
|
+
required,
|
|
28
|
+
relationType,
|
|
29
|
+
size,
|
|
30
|
+
targetModel,
|
|
31
|
+
}) => {
|
|
32
|
+
const { formatMessage } = useIntl();
|
|
33
|
+
const { connectRelation, disconnectRelation, loadRelation, modifiedData, slug, initialData } =
|
|
34
|
+
useCMEditViewDataManager();
|
|
35
|
+
|
|
36
|
+
const { relations, search, searchFor } = useRelation(`${slug}-${name}-${initialData?.id ?? ''}`, {
|
|
37
|
+
relation: {
|
|
38
|
+
enabled: get(initialData, name)?.count !== 0 && !!endpoints.relation,
|
|
39
|
+
endpoint: endpoints.relation,
|
|
40
|
+
pageParams: {
|
|
41
|
+
...defaultParams,
|
|
42
|
+
pageSize: RELATIONS_TO_DISPLAY,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
search: {
|
|
47
|
+
endpoint: endpoints.search,
|
|
48
|
+
pageParams: {
|
|
49
|
+
...defaultParams,
|
|
50
|
+
entityId: isCreatingEntry ? undefined : componentId ?? initialData.id,
|
|
51
|
+
pageSize: SEARCH_RESULTS_TO_DISPLAY,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const relationsFromModifiedData = get(modifiedData, name);
|
|
57
|
+
const stringifiedRelations = JSON.stringify(relations);
|
|
58
|
+
const normalizedRelations = useMemo(
|
|
59
|
+
() =>
|
|
60
|
+
normalizeRelations(relations, {
|
|
61
|
+
modifiedData: relationsFromModifiedData,
|
|
62
|
+
mainFieldName: mainField.name,
|
|
63
|
+
shouldAddLink: shouldDisplayRelationLink,
|
|
64
|
+
targetModel,
|
|
65
|
+
}),
|
|
66
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
67
|
+
[
|
|
68
|
+
stringifiedRelations,
|
|
69
|
+
modifiedData,
|
|
70
|
+
name,
|
|
71
|
+
mainField.name,
|
|
72
|
+
shouldDisplayRelationLink,
|
|
73
|
+
targetModel,
|
|
74
|
+
]
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
if (relations.status === 'success') {
|
|
79
|
+
loadRelation({
|
|
80
|
+
target: { name, value: normalizedRelations.data.pages.flat() },
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
84
|
+
}, [loadRelation, relations.status, stringifiedRelations, name]);
|
|
85
|
+
|
|
86
|
+
const isMorph = useMemo(() => relationType.toLowerCase().includes('morph'), [relationType]);
|
|
87
|
+
const isSingleRelation = [
|
|
88
|
+
'oneWay',
|
|
89
|
+
'oneToOne',
|
|
90
|
+
'manyToOne',
|
|
91
|
+
'oneToManyMorph',
|
|
92
|
+
'oneToOneMorph',
|
|
93
|
+
].includes(relationType);
|
|
94
|
+
|
|
95
|
+
const isDisabled = useMemo(() => {
|
|
96
|
+
if (isMorph) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!isCreatingEntry) {
|
|
101
|
+
return (!isFieldAllowed && isFieldReadable) || !editable;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return !editable;
|
|
105
|
+
}, [isMorph, isCreatingEntry, editable, isFieldAllowed, isFieldReadable]);
|
|
106
|
+
|
|
107
|
+
const handleRelationAdd = (relation) => {
|
|
108
|
+
connectRelation({ target: { name, value: relation, replace: isSingleRelation } });
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const handleRelationRemove = (relation) => {
|
|
112
|
+
disconnectRelation({ target: { name, value: relation } });
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const handleRelationLoadMore = () => {
|
|
116
|
+
relations.fetchNextPage();
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const handleSearch = (term) => {
|
|
120
|
+
searchFor(term, {
|
|
121
|
+
idsToInclude: relationsFromModifiedData?.disconnect?.map((relation) => relation.id),
|
|
122
|
+
idsToOmit: relationsFromModifiedData?.connect?.map((relation) => relation.id),
|
|
123
|
+
});
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const handleOpenSearch = () => {
|
|
127
|
+
searchFor('', {
|
|
128
|
+
idsToInclude: relationsFromModifiedData?.disconnect?.map((relation) => relation.id),
|
|
129
|
+
idsToOmit: relationsFromModifiedData?.connect?.map((relation) => relation.id),
|
|
130
|
+
});
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const handleSearchMore = () => {
|
|
134
|
+
search.fetchNextPage();
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
if (
|
|
138
|
+
(!isFieldAllowed && isCreatingEntry) ||
|
|
139
|
+
(!isCreatingEntry && !isFieldAllowed && !isFieldReadable)
|
|
140
|
+
) {
|
|
141
|
+
return <NotAllowedInput intlLabel={intlLabel} labelAction={labelAction} />;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<RelationInput
|
|
146
|
+
description={description}
|
|
147
|
+
disabled={isDisabled}
|
|
148
|
+
id={name}
|
|
149
|
+
label={`${formatMessage({
|
|
150
|
+
id: intlLabel.id,
|
|
151
|
+
defaultMessage: intlLabel.defaultMessage,
|
|
152
|
+
})} ${initialData[name]?.count !== undefined ? `(${initialData[name].count})` : ''}`}
|
|
153
|
+
labelAction={labelAction}
|
|
154
|
+
labelLoadMore={
|
|
155
|
+
!isCreatingEntry
|
|
156
|
+
? formatMessage({
|
|
157
|
+
id: getTrad('relation.loadMore'),
|
|
158
|
+
defaultMessage: 'Load More',
|
|
159
|
+
})
|
|
160
|
+
: null
|
|
161
|
+
}
|
|
162
|
+
labelDisconnectRelation={formatMessage({
|
|
163
|
+
id: getTrad('relation.disconnect'),
|
|
164
|
+
defaultMessage: 'Remove',
|
|
165
|
+
})}
|
|
166
|
+
listHeight={320}
|
|
167
|
+
loadingMessage={() =>
|
|
168
|
+
formatMessage({
|
|
169
|
+
id: getTrad('relation.isLoading'),
|
|
170
|
+
defaultMessage: 'Relations are loading',
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
name={name}
|
|
174
|
+
numberOfRelationsToDisplay={RELATIONS_TO_DISPLAY}
|
|
175
|
+
onRelationAdd={(relation) => handleRelationAdd(relation)}
|
|
176
|
+
onRelationRemove={(relation) => handleRelationRemove(relation)}
|
|
177
|
+
onRelationLoadMore={() => handleRelationLoadMore()}
|
|
178
|
+
onSearch={(term) => handleSearch(term)}
|
|
179
|
+
onSearchNextPage={() => handleSearchMore()}
|
|
180
|
+
onSearchOpen={handleOpenSearch}
|
|
181
|
+
placeholder={formatMessage(
|
|
182
|
+
placeholder || {
|
|
183
|
+
id: getTrad('relation.add'),
|
|
184
|
+
defaultMessage: 'Add relation',
|
|
185
|
+
}
|
|
186
|
+
)}
|
|
187
|
+
publicationStateTranslations={{
|
|
188
|
+
[PUBLICATION_STATES.DRAFT]: formatMessage({
|
|
189
|
+
id: getTrad('relation.publicationState.draft'),
|
|
190
|
+
defaultMessage: 'Draft',
|
|
191
|
+
}),
|
|
192
|
+
|
|
193
|
+
[PUBLICATION_STATES.PUBLISHED]: formatMessage({
|
|
194
|
+
id: getTrad('relation.publicationState.published'),
|
|
195
|
+
defaultMessage: 'Published',
|
|
196
|
+
}),
|
|
197
|
+
}}
|
|
198
|
+
relations={normalizedRelations}
|
|
199
|
+
required={required}
|
|
200
|
+
searchResults={normalizeSearchResults(search, {
|
|
201
|
+
mainFieldName: mainField.name,
|
|
202
|
+
})}
|
|
203
|
+
size={size}
|
|
204
|
+
/>
|
|
205
|
+
);
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
RelationInputDataManager.defaultProps = {
|
|
209
|
+
componentId: undefined,
|
|
210
|
+
editable: true,
|
|
211
|
+
description: '',
|
|
212
|
+
labelAction: null,
|
|
213
|
+
isFieldAllowed: true,
|
|
214
|
+
placeholder: null,
|
|
215
|
+
required: false,
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
RelationInputDataManager.propTypes = {
|
|
219
|
+
componentId: PropTypes.number,
|
|
220
|
+
editable: PropTypes.bool,
|
|
221
|
+
description: PropTypes.string,
|
|
222
|
+
intlLabel: PropTypes.shape({
|
|
223
|
+
id: PropTypes.string.isRequired,
|
|
224
|
+
defaultMessage: PropTypes.string.isRequired,
|
|
225
|
+
values: PropTypes.object,
|
|
226
|
+
}).isRequired,
|
|
227
|
+
labelAction: PropTypes.element,
|
|
228
|
+
isCreatingEntry: PropTypes.bool.isRequired,
|
|
229
|
+
isFieldAllowed: PropTypes.bool,
|
|
230
|
+
isFieldReadable: PropTypes.bool.isRequired,
|
|
231
|
+
mainField: PropTypes.shape({
|
|
232
|
+
name: PropTypes.string.isRequired,
|
|
233
|
+
schema: PropTypes.shape({
|
|
234
|
+
type: PropTypes.string.isRequired,
|
|
235
|
+
}).isRequired,
|
|
236
|
+
}).isRequired,
|
|
237
|
+
name: PropTypes.string.isRequired,
|
|
238
|
+
placeholder: PropTypes.shape({
|
|
239
|
+
id: PropTypes.string.isRequired,
|
|
240
|
+
defaultMessage: PropTypes.string.isRequired,
|
|
241
|
+
values: PropTypes.object,
|
|
242
|
+
}),
|
|
243
|
+
required: PropTypes.bool,
|
|
244
|
+
relationType: PropTypes.string.isRequired,
|
|
245
|
+
size: PropTypes.number.isRequired,
|
|
246
|
+
targetModel: PropTypes.string.isRequired,
|
|
247
|
+
queryInfos: PropTypes.shape({
|
|
248
|
+
defaultParams: PropTypes.shape({
|
|
249
|
+
_component: PropTypes.string,
|
|
250
|
+
}),
|
|
251
|
+
endpoints: PropTypes.shape({
|
|
252
|
+
relation: PropTypes.string,
|
|
253
|
+
search: PropTypes.string.isRequired,
|
|
254
|
+
}).isRequired,
|
|
255
|
+
shouldDisplayRelationLink: PropTypes.bool.isRequired,
|
|
256
|
+
}).isRequired,
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const Memoized = memo(RelationInputDataManager);
|
|
260
|
+
|
|
261
|
+
export default connect(Memoized, select);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as RelationInputDataManager } from './RelationInputDataManager';
|
package/admin/src/content-manager/components/RelationInputDataManager/utils/normalizeRelations.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { getRelationLink } from './getRelationLink';
|
|
2
|
+
|
|
3
|
+
import { PUBLICATION_STATES } from '../constants';
|
|
4
|
+
|
|
5
|
+
export const normalizeRelation = (relation, { shouldAddLink, mainFieldName, targetModel }) => {
|
|
6
|
+
const nextRelation = { ...relation };
|
|
7
|
+
|
|
8
|
+
if (shouldAddLink) {
|
|
9
|
+
nextRelation.href = getRelationLink(targetModel, nextRelation.id);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
nextRelation.publicationState = false;
|
|
13
|
+
|
|
14
|
+
if (nextRelation?.publishedAt !== undefined) {
|
|
15
|
+
nextRelation.publicationState = nextRelation.publishedAt
|
|
16
|
+
? PUBLICATION_STATES.PUBLISHED
|
|
17
|
+
: PUBLICATION_STATES.DRAFT;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
nextRelation.mainField = nextRelation[mainFieldName];
|
|
21
|
+
|
|
22
|
+
return nextRelation;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/*
|
|
26
|
+
* Applies some transformations to existing and new relations in order to display them correctly
|
|
27
|
+
* relations: raw relations data coming from useRelations
|
|
28
|
+
* shouldAddLink: comes from generateRelationQueryInfos, if true we display a link to the relation (TO FIX: explanation)
|
|
29
|
+
* mainFieldName: name of the main field inside the relation (e.g. text field), if no displayable main field exists (e.g. date field) we use the id of the entry
|
|
30
|
+
* targetModel: the model on which the relation is based on, used to create an URL link
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
export const normalizeRelations = (
|
|
34
|
+
relations,
|
|
35
|
+
{ modifiedData = {}, shouldAddLink = false, mainFieldName, targetModel }
|
|
36
|
+
) => {
|
|
37
|
+
return {
|
|
38
|
+
...relations,
|
|
39
|
+
data: {
|
|
40
|
+
pages:
|
|
41
|
+
[
|
|
42
|
+
...(relations?.data?.pages.reverse() ?? []),
|
|
43
|
+
...(modifiedData?.connect ? [{ results: modifiedData.connect }] : []),
|
|
44
|
+
]
|
|
45
|
+
?.map((page) =>
|
|
46
|
+
page?.results
|
|
47
|
+
.filter(
|
|
48
|
+
(relation) =>
|
|
49
|
+
!modifiedData?.disconnect?.find(
|
|
50
|
+
(disconnectRelation) => disconnectRelation.id === relation.id
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
.map((relation) =>
|
|
54
|
+
normalizeRelation(relation, {
|
|
55
|
+
shouldAddLink,
|
|
56
|
+
mainFieldName,
|
|
57
|
+
targetModel,
|
|
58
|
+
})
|
|
59
|
+
)
|
|
60
|
+
.filter(Boolean)
|
|
61
|
+
)
|
|
62
|
+
?.filter((page) => page.length > 0) ?? [],
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { normalizeRelation } from './normalizeRelations';
|
|
2
|
+
|
|
3
|
+
export const normalizeSearchResults = (relations, { mainFieldName }) => {
|
|
4
|
+
return {
|
|
5
|
+
...relations,
|
|
6
|
+
data: {
|
|
7
|
+
pages: [...(relations?.data?.pages ?? [])]?.map((page) =>
|
|
8
|
+
page?.results.map((relation) => normalizeRelation(relation, { mainFieldName }))
|
|
9
|
+
),
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import get from 'lodash/get';
|
|
3
|
+
import { useCMEditViewDataManager } from '@strapi/helper-plugin';
|
|
4
|
+
|
|
5
|
+
import { getRequestUrl } from '../../../utils';
|
|
6
|
+
|
|
7
|
+
function useSelect({
|
|
8
|
+
componentUid,
|
|
9
|
+
isUserAllowedToEditField,
|
|
10
|
+
isUserAllowedToReadField,
|
|
11
|
+
name,
|
|
12
|
+
queryInfos,
|
|
13
|
+
}) {
|
|
14
|
+
const {
|
|
15
|
+
isCreatingEntry,
|
|
16
|
+
createActionAllowedFields,
|
|
17
|
+
readActionAllowedFields,
|
|
18
|
+
updateActionAllowedFields,
|
|
19
|
+
slug,
|
|
20
|
+
initialData,
|
|
21
|
+
} = useCMEditViewDataManager();
|
|
22
|
+
|
|
23
|
+
const isFieldAllowed = useMemo(() => {
|
|
24
|
+
if (isUserAllowedToEditField === true) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const allowedFields = isCreatingEntry ? createActionAllowedFields : updateActionAllowedFields;
|
|
29
|
+
|
|
30
|
+
return allowedFields.includes(name);
|
|
31
|
+
}, [
|
|
32
|
+
isCreatingEntry,
|
|
33
|
+
createActionAllowedFields,
|
|
34
|
+
name,
|
|
35
|
+
isUserAllowedToEditField,
|
|
36
|
+
updateActionAllowedFields,
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
const isFieldReadable = useMemo(() => {
|
|
40
|
+
if (isUserAllowedToReadField) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const allowedFields = isCreatingEntry ? [] : readActionAllowedFields;
|
|
45
|
+
|
|
46
|
+
return allowedFields.includes(name);
|
|
47
|
+
}, [isCreatingEntry, isUserAllowedToReadField, name, readActionAllowedFields]);
|
|
48
|
+
|
|
49
|
+
const fieldNameKeys = name.split('.');
|
|
50
|
+
let componentId;
|
|
51
|
+
|
|
52
|
+
if (componentUid) {
|
|
53
|
+
componentId = get(initialData, fieldNameKeys.slice(0, -1))?.id;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// /content-manager/relations/[model]/[id]/[field-name]
|
|
57
|
+
const relationFetchEndpoint = useMemo(() => {
|
|
58
|
+
if (isCreatingEntry) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (componentUid) {
|
|
63
|
+
// repeatable components and dz are dynamically created
|
|
64
|
+
// if no componentId exists in initialData it means that the user just created it
|
|
65
|
+
// there then are no relations to request
|
|
66
|
+
return componentId
|
|
67
|
+
? getRequestUrl(`relations/${componentUid}/${componentId}/${fieldNameKeys.at(-1)}`)
|
|
68
|
+
: null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return getRequestUrl(`relations/${slug}/${initialData.id}/${name.split('.').at(-1)}`);
|
|
72
|
+
}, [isCreatingEntry, componentUid, slug, initialData.id, name, componentId, fieldNameKeys]);
|
|
73
|
+
|
|
74
|
+
// /content-manager/relations/[model]/[field-name]
|
|
75
|
+
const relationSearchEndpoint = useMemo(() => {
|
|
76
|
+
if (componentUid) {
|
|
77
|
+
return getRequestUrl(`relations/${componentUid}/${name.split('.').at(-1)}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return getRequestUrl(`relations/${slug}/${name.split('.').at(-1)}`);
|
|
81
|
+
}, [componentUid, slug, name]);
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
componentId,
|
|
85
|
+
queryInfos: {
|
|
86
|
+
...queryInfos,
|
|
87
|
+
endpoints: {
|
|
88
|
+
search: relationSearchEndpoint,
|
|
89
|
+
relation: relationFetchEndpoint,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
isCreatingEntry,
|
|
93
|
+
isFieldAllowed,
|
|
94
|
+
isFieldReadable,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export default useSelect;
|
|
@@ -44,6 +44,7 @@ const DragButton = styled.span`
|
|
|
44
44
|
|
|
45
45
|
const DraggedItem = ({
|
|
46
46
|
componentFieldName,
|
|
47
|
+
componentUid,
|
|
47
48
|
// Errors are retrieved from the AccordionGroupCustom cloneElement
|
|
48
49
|
hasErrorMessage,
|
|
49
50
|
hasErrors,
|
|
@@ -265,11 +266,13 @@ const DraggedItem = ({
|
|
|
265
266
|
return (
|
|
266
267
|
<GridItem key={keys} col={size} s={12} xs={12}>
|
|
267
268
|
<Inputs
|
|
269
|
+
componentUid={componentUid}
|
|
268
270
|
fieldSchema={fieldSchema}
|
|
269
271
|
keys={keys}
|
|
270
272
|
metadatas={metadatas}
|
|
271
273
|
// onBlur={hasErrors ? checkFormErrors : null}
|
|
272
274
|
queryInfos={queryInfos}
|
|
275
|
+
size={size}
|
|
273
276
|
/>
|
|
274
277
|
</GridItem>
|
|
275
278
|
);
|
|
@@ -286,6 +289,7 @@ const DraggedItem = ({
|
|
|
286
289
|
};
|
|
287
290
|
|
|
288
291
|
DraggedItem.defaultProps = {
|
|
292
|
+
componentUid: undefined,
|
|
289
293
|
isDraggingSibling: false,
|
|
290
294
|
isOpen: false,
|
|
291
295
|
setIsDraggingSibling() {},
|
|
@@ -294,6 +298,7 @@ DraggedItem.defaultProps = {
|
|
|
294
298
|
|
|
295
299
|
DraggedItem.propTypes = {
|
|
296
300
|
componentFieldName: PropTypes.string.isRequired,
|
|
301
|
+
componentUid: PropTypes.string,
|
|
297
302
|
hasErrorMessage: PropTypes.bool.isRequired,
|
|
298
303
|
hasErrors: PropTypes.bool.isRequired,
|
|
299
304
|
isDraggingSibling: PropTypes.bool,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { memo, useCallback, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import { useHistory } from 'react-router-dom';
|
|
3
|
+
import { useQueryClient } from 'react-query';
|
|
3
4
|
import get from 'lodash/get';
|
|
4
5
|
import {
|
|
5
6
|
useTracking,
|
|
@@ -28,6 +29,7 @@ import buildQueryString from '../../pages/ListView/utils/buildQueryString';
|
|
|
28
29
|
|
|
29
30
|
// This container is used to handle the CRUD
|
|
30
31
|
const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
|
|
32
|
+
const queryClient = useQueryClient();
|
|
31
33
|
const { trackUsage } = useTracking();
|
|
32
34
|
const { push } = useHistory();
|
|
33
35
|
const { setCurrentStep } = useGuidedTour();
|
|
@@ -202,6 +204,9 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
|
|
|
202
204
|
|
|
203
205
|
setCurrentStep('contentManager.success');
|
|
204
206
|
|
|
207
|
+
// TODO: need to find a better place, or a better abstraction
|
|
208
|
+
queryClient.invalidateQueries(['relation']);
|
|
209
|
+
|
|
205
210
|
dispatch(submitSucceeded(cleanReceivedData(data)));
|
|
206
211
|
setIsCreatingEntry(false);
|
|
207
212
|
|
|
@@ -218,8 +223,39 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
|
|
|
218
223
|
return Promise.reject(err);
|
|
219
224
|
}
|
|
220
225
|
},
|
|
221
|
-
[
|
|
226
|
+
[
|
|
227
|
+
cleanReceivedData,
|
|
228
|
+
displayErrors,
|
|
229
|
+
slug,
|
|
230
|
+
dispatch,
|
|
231
|
+
rawQuery,
|
|
232
|
+
toggleNotification,
|
|
233
|
+
setCurrentStep,
|
|
234
|
+
queryClient,
|
|
235
|
+
]
|
|
222
236
|
);
|
|
237
|
+
|
|
238
|
+
const onDraftRelationCheck = useCallback(async () => {
|
|
239
|
+
try {
|
|
240
|
+
trackUsageRef.current('willCheckDraftRelations');
|
|
241
|
+
|
|
242
|
+
const endPoint = getRequestUrl(`${slug}/actions/numberOfDraftRelations`);
|
|
243
|
+
dispatch(setStatus('draft-relation-check-pending'));
|
|
244
|
+
|
|
245
|
+
const numberOfDraftRelations = await axiosInstance.get(endPoint);
|
|
246
|
+
trackUsageRef.current('didCheckDraftRelations');
|
|
247
|
+
|
|
248
|
+
dispatch(setStatus('resolved'));
|
|
249
|
+
|
|
250
|
+
return numberOfDraftRelations.data.data;
|
|
251
|
+
} catch (err) {
|
|
252
|
+
displayErrors(err);
|
|
253
|
+
dispatch(setStatus('resolved'));
|
|
254
|
+
|
|
255
|
+
return Promise.reject(err);
|
|
256
|
+
}
|
|
257
|
+
}, [displayErrors, slug, dispatch]);
|
|
258
|
+
|
|
223
259
|
const onPublish = useCallback(async () => {
|
|
224
260
|
try {
|
|
225
261
|
trackUsageRef.current('willPublishEntry');
|
|
@@ -267,6 +303,9 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
|
|
|
267
303
|
|
|
268
304
|
trackUsageRef.current('didEditEntry', { trackerProperty });
|
|
269
305
|
|
|
306
|
+
// TODO: need to find a better place, or a better abstraction
|
|
307
|
+
queryClient.invalidateQueries(['relation']);
|
|
308
|
+
|
|
270
309
|
dispatch(submitSucceeded(cleanReceivedData(data)));
|
|
271
310
|
|
|
272
311
|
dispatch(setStatus('resolved'));
|
|
@@ -282,7 +321,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
|
|
|
282
321
|
return Promise.reject(err);
|
|
283
322
|
}
|
|
284
323
|
},
|
|
285
|
-
[cleanReceivedData, displayErrors, slug, dispatch, rawQuery, toggleNotification]
|
|
324
|
+
[cleanReceivedData, displayErrors, slug, dispatch, rawQuery, toggleNotification, queryClient]
|
|
286
325
|
);
|
|
287
326
|
|
|
288
327
|
// The publish and unpublish method could be refactored but let's leave the duplication for now
|
|
@@ -320,6 +359,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
|
|
|
320
359
|
onDelete,
|
|
321
360
|
onDeleteSucceeded,
|
|
322
361
|
onPost,
|
|
362
|
+
onDraftRelationCheck,
|
|
323
363
|
onPublish,
|
|
324
364
|
onPut,
|
|
325
365
|
onUnpublish,
|