@strapi/admin 4.5.2 → 4.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin/src/components/AuthenticatedApp/index.js +13 -2
- package/admin/src/content-manager/components/DynamicZone/components/DynamicComponent.js +1 -1
- package/admin/src/content-manager/components/EditViewDataManagerProvider/index.js +39 -3
- package/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js +5 -1
- package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/recursivelyFindPathsBasedOnCondition.js +8 -1
- package/admin/src/content-manager/components/Inputs/index.js +5 -19
- package/admin/src/content-manager/components/NonRepeatableComponent/index.js +4 -0
- package/admin/src/content-manager/components/RelationInput/RelationInput.js +4 -9
- package/admin/src/content-manager/components/RelationInputDataManager/RelationInputDataManager.js +1 -1
- package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/index.js +4 -0
- package/admin/src/content-manager/components/SingleTypeFormWrapper/index.js +1 -1
- package/admin/src/content-manager/components/Wysiwyg/Editor.js +1 -1
- package/admin/src/content-manager/hooks/useLazyComponents/index.js +69 -0
- package/admin/src/content-manager/pages/CollectionTypeRecursivePath/components/ErrorFallback.js +13 -0
- package/admin/src/content-manager/pages/CollectionTypeRecursivePath/index.js +2 -1
- package/admin/src/content-manager/pages/EditView/GridRow/index.js +62 -0
- package/admin/src/content-manager/pages/EditView/index.js +74 -154
- package/admin/src/content-manager/pages/EditView/selectors.js +14 -0
- package/admin/src/content-manager/pages/EditView/utils/createAttributesLayout.js +11 -6
- package/admin/src/content-manager/pages/EditView/utils/getCustomFieldUidsFromLayout.js +18 -0
- package/admin/src/content-manager/pages/EditView/utils/index.js +1 -0
- package/admin/src/content-manager/pages/EditViewLayoutManager/index.js +1 -1
- package/admin/src/content-manager/sharedReducers/crudReducer/actions.js +5 -0
- package/admin/src/content-manager/sharedReducers/crudReducer/constants.js +2 -0
- package/admin/src/content-manager/sharedReducers/crudReducer/reducer.js +7 -0
- package/admin/src/core/apis/CustomFields.js +46 -1
- package/admin/src/core/utils/axiosInstance.js +4 -2
- package/admin/src/hooks/index.js +1 -0
- package/admin/src/hooks/useFetchClient/index.js +23 -0
- package/admin/src/pages/App/index.js +20 -13
- package/admin/src/pages/HomePage/SocialLinks.js +4 -4
- package/admin/src/pages/MarketplacePage/components/SortSelect/index.js +20 -0
- package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js +2 -3
- package/admin/src/pages/SettingsPage/pages/Users/ListPage/ModalForm/index.js +23 -18
- package/admin/src/translations/en.json +4 -0
- package/admin/src/translations/ru.json +789 -489
- package/admin/src/translations/tr.json +485 -5
- package/admin/src/utils/fetchClient.js +45 -0
- package/admin/src/utils/getFetchClient.js +10 -0
- package/admin/src/utils/index.js +1 -0
- package/admin/src/utils/uniqueAdminHash.js +22 -0
- package/build/{1233.bddaaa76.chunk.js → 1233.802422fa.chunk.js} +63 -63
- package/build/4306.df40a798.chunk.js +98 -0
- package/build/4318.9283c350.chunk.js +30 -0
- package/build/5057.195a59ff.chunk.js +65 -0
- package/build/{805.a1894307.chunk.js → 805.e991a370.chunk.js} +6 -6
- package/build/{4986.3820d11d.chunk.js → 8176.e929d326.chunk.js} +3 -3
- package/build/{8633.8da5488a.chunk.js → 8633.43ec9042.chunk.js} +1 -1
- package/build/8881.c693411a.chunk.js +245 -0
- package/build/9161.4a0ab137.chunk.js +2119 -0
- package/build/9279.6290c87a.chunk.js +117 -0
- package/build/9707.a0cc4ad8.chunk.js +70 -0
- package/build/Admin-authenticatedApp.0da578b8.chunk.js +80 -0
- package/build/{Admin_homePage.54e33c2d.chunk.js → Admin_homePage.8945f71a.chunk.js} +5 -5
- package/build/{Admin_marketplace.ff012eb2.chunk.js → Admin_marketplace.ed754a4a.chunk.js} +5 -5
- package/build/{Admin_profilePage.e9fcce92.chunk.js → Admin_profilePage.60ab80bb.chunk.js} +1 -1
- package/build/{Admin_settingsPage.a1a5218b.chunk.js → Admin_settingsPage.6ef8acc9.chunk.js} +6 -6
- package/build/admin-app.a3277e72.chunk.js +112 -0
- package/build/admin-edit-roles-page.f407538c.chunk.js +1 -0
- package/build/admin-edit-users.5547b126.chunk.js +10 -0
- package/build/{admin-users.a0748674.chunk.js → admin-users.e64fb0f1.chunk.js} +1 -1
- package/build/content-manager.f9630c3b.chunk.js +1197 -0
- package/build/content-type-builder-list-view.4412efc3.chunk.js +201 -0
- package/build/content-type-builder-translation-tr-json.949e22eb.chunk.js +1 -0
- package/build/content-type-builder.b132b5f4.chunk.js +145 -0
- package/build/email-settings-page.c6e62f6b.chunk.js +15 -0
- package/build/email-translation-tr-json.8aa034bb.chunk.js +1 -0
- package/build/en-json.7dd57947.chunk.js +1 -0
- package/build/i18n-translation-tr-json.34ca9d61.chunk.js +1 -0
- package/build/index.html +1 -1
- package/build/main.71f24343.js +2034 -0
- package/build/ru-json.8830286f.chunk.js +1 -0
- package/build/runtime~main.1115f82b.js +2 -0
- package/build/sso-settings-page.feed2f45.chunk.js +1 -0
- package/build/tr-json.eac8bd79.chunk.js +1 -0
- package/build/upload-settings.450cab1a.chunk.js +18 -0
- package/build/upload-translation-tr-json.b173223a.chunk.js +1 -0
- package/build/upload.74540aab.chunk.js +64 -0
- package/build/users-advanced-settings-page.0c0b8230.chunk.js +13 -0
- package/build/users-email-settings-page.3126ff8c.chunk.js +28 -0
- package/build/users-permissions-translation-tr-json.9bebc250.chunk.js +1 -0
- package/build/users-providers-settings-page.b7b602e2.chunk.js +33 -0
- package/build/users-roles-settings-page.ce5b582d.chunk.js +30 -0
- package/build/webhook-edit-page.1215a6b7.chunk.js +75 -0
- package/package.json +13 -13
- package/server/controllers/admin.js +2 -0
- package/server/routes/admin.js +1 -1
- package/server/services/metrics.js +5 -2
- package/utils/get-plugins-path.js +17 -3
- package/build/1920.208c77f8.chunk.js +0 -245
- package/build/2438.afe24949.chunk.js +0 -2525
- package/build/2517.5cc235ba.chunk.js +0 -117
- package/build/4306.e62b841c.chunk.js +0 -98
- package/build/4318.7931eee7.chunk.js +0 -30
- package/build/9707.932a3c12.chunk.js +0 -70
- package/build/Admin-authenticatedApp.7484bdf1.chunk.js +0 -80
- package/build/admin-app.9cb0abc7.chunk.js +0 -112
- package/build/admin-edit-roles-page.23f15909.chunk.js +0 -1
- package/build/admin-edit-users.283b49ed.chunk.js +0 -10
- package/build/content-manager.577fddcb.chunk.js +0 -1200
- package/build/content-type-builder-list-view.95012cf0.chunk.js +0 -201
- package/build/content-type-builder-translation-tr-json.2e52bc60.chunk.js +0 -1
- package/build/content-type-builder.95b9d6a2.chunk.js +0 -145
- package/build/email-settings-page.4bb3606f.chunk.js +0 -15
- package/build/email-translation-tr-json.87f2feb3.chunk.js +0 -1
- package/build/en-json.4a269f6b.chunk.js +0 -1
- package/build/main.23670c4c.js +0 -2026
- package/build/ru-json.d7cfc2ff.chunk.js +0 -1
- package/build/runtime~main.4dc8c7c6.js +0 -2
- package/build/sso-settings-page.9f091262.chunk.js +0 -1
- package/build/tr-json.9c44ea0c.chunk.js +0 -1
- package/build/upload-settings.3010911f.chunk.js +0 -18
- package/build/upload.9f19f2e8.chunk.js +0 -64
- package/build/users-advanced-settings-page.9df41d67.chunk.js +0 -13
- package/build/users-email-settings-page.56d82eaf.chunk.js +0 -28
- package/build/users-permissions-translation-tr-json.cdc49a3c.chunk.js +0 -1
- package/build/users-providers-settings-page.96bb7da0.chunk.js +0 -33
- package/build/users-roles-settings-page.445e5e16.chunk.js +0 -30
- package/build/webhook-edit-page.3285abc4.chunk.js +0 -75
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { memo } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
-
import
|
|
3
|
+
import { useSelector } from 'react-redux';
|
|
4
4
|
import {
|
|
5
5
|
CheckPermissions,
|
|
6
|
-
LoadingIndicatorPage,
|
|
7
6
|
useTracking,
|
|
8
7
|
LinkButton,
|
|
8
|
+
LoadingIndicatorPage,
|
|
9
9
|
} from '@strapi/helper-plugin';
|
|
10
10
|
import { useIntl } from 'react-intl';
|
|
11
11
|
import { ContentLayout } from '@strapi/design-system/Layout';
|
|
@@ -18,73 +18,60 @@ import Pencil from '@strapi/icons/Pencil';
|
|
|
18
18
|
import { InjectionZone } from '../../../shared/components';
|
|
19
19
|
import permissions from '../../../permissions';
|
|
20
20
|
import DynamicZone from '../../components/DynamicZone';
|
|
21
|
-
|
|
22
|
-
import Inputs from '../../components/Inputs';
|
|
21
|
+
|
|
23
22
|
import CollectionTypeFormWrapper from '../../components/CollectionTypeFormWrapper';
|
|
24
23
|
import EditViewDataManagerProvider from '../../components/EditViewDataManagerProvider';
|
|
25
24
|
import SingleTypeFormWrapper from '../../components/SingleTypeFormWrapper';
|
|
26
25
|
import { getTrad } from '../../utils';
|
|
26
|
+
import useLazyComponents from '../../hooks/useLazyComponents';
|
|
27
27
|
import DraftAndPublishBadge from './DraftAndPublishBadge';
|
|
28
28
|
import Informations from './Informations';
|
|
29
29
|
import Header from './Header';
|
|
30
|
-
import {
|
|
30
|
+
import { getFieldsActionMatchingPermissions } from './utils';
|
|
31
31
|
import DeleteLink from './DeleteLink';
|
|
32
|
+
import GridRow from './GridRow';
|
|
33
|
+
import { selectCurrentLayout, selectAttributesLayout, selectCustomFieldUids } from './selectors';
|
|
32
34
|
|
|
33
35
|
const cmPermissions = permissions.contentManager;
|
|
34
36
|
const ctbPermissions = [{ action: 'plugin::content-type-builder.read', subject: null }];
|
|
35
37
|
|
|
36
38
|
/* eslint-disable react/no-array-index-key */
|
|
37
|
-
const EditView = ({
|
|
38
|
-
allowedActions,
|
|
39
|
-
isSingleType,
|
|
40
|
-
goBack,
|
|
41
|
-
layout,
|
|
42
|
-
slug,
|
|
43
|
-
id,
|
|
44
|
-
origin,
|
|
45
|
-
userPermissions,
|
|
46
|
-
}) => {
|
|
39
|
+
const EditView = ({ allowedActions, isSingleType, goBack, slug, id, origin, userPermissions }) => {
|
|
47
40
|
const { trackUsage } = useTracking();
|
|
48
41
|
const { formatMessage } = useIntl();
|
|
42
|
+
|
|
43
|
+
const { layout, formattedContentTypeLayout, customFieldUids } = useSelector((state) => ({
|
|
44
|
+
layout: selectCurrentLayout(state),
|
|
45
|
+
formattedContentTypeLayout: selectAttributesLayout(state),
|
|
46
|
+
customFieldUids: selectCustomFieldUids(state),
|
|
47
|
+
}));
|
|
48
|
+
|
|
49
|
+
const { isLazyLoading, lazyComponentStore } = useLazyComponents(customFieldUids);
|
|
50
|
+
|
|
49
51
|
const { createActionAllowedFields, readActionAllowedFields, updateActionAllowedFields } =
|
|
50
|
-
|
|
51
|
-
return getFieldsActionMatchingPermissions(userPermissions, slug);
|
|
52
|
-
}, [userPermissions, slug]);
|
|
52
|
+
getFieldsActionMatchingPermissions(userPermissions, slug);
|
|
53
53
|
|
|
54
|
-
const configurationPermissions =
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
: cmPermissions.collectionTypesConfigurations;
|
|
58
|
-
}, [isSingleType]);
|
|
54
|
+
const configurationPermissions = isSingleType
|
|
55
|
+
? cmPermissions.singleTypesConfigurations
|
|
56
|
+
: cmPermissions.collectionTypesConfigurations;
|
|
59
57
|
|
|
60
58
|
// // FIXME when changing the routing
|
|
61
59
|
const configurationsURL = `/content-manager/${
|
|
62
60
|
isSingleType ? 'singleType' : 'collectionType'
|
|
63
61
|
}/${slug}/configurations/edit`;
|
|
64
|
-
const currentContentTypeLayoutData = get(layout, ['contentType'], {});
|
|
65
62
|
|
|
66
|
-
const DataManagementWrapper =
|
|
67
|
-
() => (isSingleType ? SingleTypeFormWrapper : CollectionTypeFormWrapper),
|
|
68
|
-
[isSingleType]
|
|
69
|
-
);
|
|
63
|
+
const DataManagementWrapper = isSingleType ? SingleTypeFormWrapper : CollectionTypeFormWrapper;
|
|
70
64
|
|
|
71
65
|
// Check if a block is a dynamic zone
|
|
72
|
-
const isDynamicZone =
|
|
66
|
+
const isDynamicZone = (block) => {
|
|
73
67
|
return block.every((subBlock) => {
|
|
74
68
|
return subBlock.every((obj) => obj.fieldSchema.type === 'dynamiczone');
|
|
75
69
|
});
|
|
76
|
-
}
|
|
70
|
+
};
|
|
77
71
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return createAttributesLayout(
|
|
84
|
-
currentContentTypeLayoutData.layouts.edit,
|
|
85
|
-
currentContentTypeLayoutData.attributes
|
|
86
|
-
);
|
|
87
|
-
}, [currentContentTypeLayoutData]);
|
|
72
|
+
if (isLazyLoading) {
|
|
73
|
+
return <LoadingIndicatorPage />;
|
|
74
|
+
}
|
|
88
75
|
|
|
89
76
|
return (
|
|
90
77
|
<DataManagementWrapper allLayoutData={layout} slug={slug} id={id} origin={origin}>
|
|
@@ -132,110 +119,56 @@ const EditView = ({
|
|
|
132
119
|
<ContentLayout>
|
|
133
120
|
<Grid gap={4}>
|
|
134
121
|
<GridItem col={9} s={12}>
|
|
135
|
-
<
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
0: {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
} = row;
|
|
144
|
-
|
|
145
|
-
return (
|
|
146
|
-
<Box key={index}>
|
|
147
|
-
<Grid gap={4}>
|
|
148
|
-
<GridItem col={12} s={12} xs={12}>
|
|
149
|
-
<DynamicZone
|
|
150
|
-
name={name}
|
|
151
|
-
fieldSchema={fieldSchema}
|
|
152
|
-
labelAction={labelAction}
|
|
153
|
-
metadatas={metadatas}
|
|
154
|
-
/>
|
|
155
|
-
</GridItem>
|
|
156
|
-
</Grid>
|
|
157
|
-
</Box>
|
|
158
|
-
);
|
|
159
|
-
}
|
|
122
|
+
<Stack spacing={6}>
|
|
123
|
+
{formattedContentTypeLayout.map((row, index) => {
|
|
124
|
+
if (isDynamicZone(row)) {
|
|
125
|
+
const {
|
|
126
|
+
0: {
|
|
127
|
+
0: { name, fieldSchema, metadatas, labelAction },
|
|
128
|
+
},
|
|
129
|
+
} = row;
|
|
160
130
|
|
|
161
131
|
return (
|
|
162
|
-
<Box
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
<Stack spacing={6}>
|
|
174
|
-
{row.map((grid, gridIndex) => {
|
|
175
|
-
return (
|
|
176
|
-
<Grid gap={4} key={gridIndex}>
|
|
177
|
-
{grid.map(
|
|
178
|
-
({
|
|
179
|
-
fieldSchema,
|
|
180
|
-
labelAction,
|
|
181
|
-
metadatas,
|
|
182
|
-
name,
|
|
183
|
-
size,
|
|
184
|
-
queryInfos,
|
|
185
|
-
}) => {
|
|
186
|
-
const isComponent = fieldSchema.type === 'component';
|
|
187
|
-
|
|
188
|
-
if (isComponent) {
|
|
189
|
-
const {
|
|
190
|
-
component,
|
|
191
|
-
max,
|
|
192
|
-
min,
|
|
193
|
-
repeatable = false,
|
|
194
|
-
required = false,
|
|
195
|
-
} = fieldSchema;
|
|
196
|
-
|
|
197
|
-
return (
|
|
198
|
-
<GridItem col={size} s={12} xs={12} key={component}>
|
|
199
|
-
<FieldComponent
|
|
200
|
-
componentUid={component}
|
|
201
|
-
labelAction={labelAction}
|
|
202
|
-
isRepeatable={repeatable}
|
|
203
|
-
intlLabel={{
|
|
204
|
-
id: metadatas.label,
|
|
205
|
-
defaultMessage: metadatas.label,
|
|
206
|
-
}}
|
|
207
|
-
max={max}
|
|
208
|
-
min={min}
|
|
209
|
-
name={name}
|
|
210
|
-
required={required}
|
|
211
|
-
/>
|
|
212
|
-
</GridItem>
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return (
|
|
217
|
-
<GridItem col={size} key={name} s={12} xs={12}>
|
|
218
|
-
<Inputs
|
|
219
|
-
size={size}
|
|
220
|
-
fieldSchema={fieldSchema}
|
|
221
|
-
keys={name}
|
|
222
|
-
labelAction={labelAction}
|
|
223
|
-
metadatas={metadatas}
|
|
224
|
-
queryInfos={queryInfos}
|
|
225
|
-
/>
|
|
226
|
-
</GridItem>
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
)}
|
|
230
|
-
</Grid>
|
|
231
|
-
);
|
|
232
|
-
})}
|
|
233
|
-
</Stack>
|
|
132
|
+
<Box key={index}>
|
|
133
|
+
<Grid gap={4}>
|
|
134
|
+
<GridItem col={12} s={12} xs={12}>
|
|
135
|
+
<DynamicZone
|
|
136
|
+
name={name}
|
|
137
|
+
fieldSchema={fieldSchema}
|
|
138
|
+
labelAction={labelAction}
|
|
139
|
+
metadatas={metadatas}
|
|
140
|
+
/>
|
|
141
|
+
</GridItem>
|
|
142
|
+
</Grid>
|
|
234
143
|
</Box>
|
|
235
144
|
);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<Box
|
|
149
|
+
key={index}
|
|
150
|
+
hasRadius
|
|
151
|
+
background="neutral0"
|
|
152
|
+
shadow="tableShadow"
|
|
153
|
+
paddingLeft={6}
|
|
154
|
+
paddingRight={6}
|
|
155
|
+
paddingTop={6}
|
|
156
|
+
paddingBottom={6}
|
|
157
|
+
borderColor="neutral150"
|
|
158
|
+
>
|
|
159
|
+
<Stack spacing={6}>
|
|
160
|
+
{row.map((grid, gridRowIndex) => (
|
|
161
|
+
<GridRow
|
|
162
|
+
columns={grid}
|
|
163
|
+
customFieldInputs={lazyComponentStore}
|
|
164
|
+
key={gridRowIndex}
|
|
165
|
+
/>
|
|
166
|
+
))}
|
|
167
|
+
</Stack>
|
|
168
|
+
</Box>
|
|
169
|
+
);
|
|
170
|
+
})}
|
|
171
|
+
</Stack>
|
|
239
172
|
</GridItem>
|
|
240
173
|
<GridItem col={3} s={12}>
|
|
241
174
|
<Stack spacing={2}>
|
|
@@ -328,16 +261,6 @@ EditView.propTypes = {
|
|
|
328
261
|
canCreate: PropTypes.bool.isRequired,
|
|
329
262
|
canDelete: PropTypes.bool.isRequired,
|
|
330
263
|
}).isRequired,
|
|
331
|
-
layout: PropTypes.shape({
|
|
332
|
-
components: PropTypes.object.isRequired,
|
|
333
|
-
contentType: PropTypes.shape({
|
|
334
|
-
uid: PropTypes.string.isRequired,
|
|
335
|
-
settings: PropTypes.object.isRequired,
|
|
336
|
-
metadatas: PropTypes.object.isRequired,
|
|
337
|
-
options: PropTypes.object.isRequired,
|
|
338
|
-
attributes: PropTypes.object.isRequired,
|
|
339
|
-
}).isRequired,
|
|
340
|
-
}).isRequired,
|
|
341
264
|
id: PropTypes.string,
|
|
342
265
|
isSingleType: PropTypes.bool,
|
|
343
266
|
goBack: PropTypes.func.isRequired,
|
|
@@ -346,7 +269,4 @@ EditView.propTypes = {
|
|
|
346
269
|
userPermissions: PropTypes.array,
|
|
347
270
|
};
|
|
348
271
|
|
|
349
|
-
export { EditView };
|
|
350
272
|
export default memo(EditView);
|
|
351
|
-
|
|
352
|
-
// export default () => 'TODO Edit view';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createSelector } from 'reselect';
|
|
2
|
+
import { createAttributesLayout, getCustomFieldUidsFromLayout } from './utils';
|
|
3
|
+
|
|
4
|
+
const selectCurrentLayout = (state) => state['content-manager_editViewLayoutManager'].currentLayout;
|
|
5
|
+
|
|
6
|
+
const selectAttributesLayout = createSelector(selectCurrentLayout, (layout) =>
|
|
7
|
+
createAttributesLayout(layout?.contentType ?? {})
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
const selectCustomFieldUids = createSelector(selectCurrentLayout, (layout) =>
|
|
11
|
+
getCustomFieldUidsFromLayout(layout)
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
export { selectCurrentLayout, selectAttributesLayout, selectCustomFieldUids };
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { get, isEmpty } from 'lodash';
|
|
2
|
-
// TODO: refacto this file to avoid eslint issues
|
|
3
|
-
/* eslint-disable no-restricted-syntax */
|
|
4
|
-
/* eslint-disable no-unused-vars */
|
|
5
2
|
|
|
6
|
-
const createAttributesLayout = (
|
|
3
|
+
const createAttributesLayout = (currentContentTypeLayoutData) => {
|
|
4
|
+
if (!currentContentTypeLayoutData.layouts) {
|
|
5
|
+
return [];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const currentLayout = currentContentTypeLayoutData.layouts.edit;
|
|
9
|
+
const attributes = currentContentTypeLayoutData.attributes;
|
|
10
|
+
|
|
7
11
|
const getType = (name) => get(attributes, [name, 'type'], '');
|
|
12
|
+
|
|
8
13
|
let currentRowIndex = 0;
|
|
9
14
|
const newLayout = [];
|
|
10
15
|
|
|
11
|
-
|
|
16
|
+
currentLayout.forEach((row) => {
|
|
12
17
|
const hasDynamicZone = row.some(({ name }) => getType(name) === 'dynamiczone');
|
|
13
18
|
|
|
14
19
|
if (!newLayout[currentRowIndex]) {
|
|
@@ -27,7 +32,7 @@ const createAttributesLayout = (currentLayout, attributes) => {
|
|
|
27
32
|
} else {
|
|
28
33
|
newLayout[currentRowIndex].push(row);
|
|
29
34
|
}
|
|
30
|
-
}
|
|
35
|
+
});
|
|
31
36
|
|
|
32
37
|
return newLayout.filter((arr) => arr.length > 0);
|
|
33
38
|
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const getCustomFieldUidsFromLayout = (layout) => {
|
|
2
|
+
if (!layout) return [];
|
|
3
|
+
// Get all the fields on the content-type and its components
|
|
4
|
+
const allFields = [
|
|
5
|
+
...layout.contentType.layouts.edit,
|
|
6
|
+
...Object.values(layout.components).flatMap((component) => component.layouts.edit),
|
|
7
|
+
].flat();
|
|
8
|
+
// Filter that down to custom fields and map the uids
|
|
9
|
+
const customFieldUids = allFields
|
|
10
|
+
.filter((field) => field.fieldSchema.customField)
|
|
11
|
+
.map((customField) => customField.fieldSchema.customField);
|
|
12
|
+
// Make sure the list is unique
|
|
13
|
+
const uniqueCustomFieldUids = [...new Set(customFieldUids)];
|
|
14
|
+
|
|
15
|
+
return uniqueCustomFieldUids;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default getCustomFieldUidsFromLayout;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
// eslint-disable-next-line import/prefer-default-export
|
|
2
2
|
export { default as createAttributesLayout } from './createAttributesLayout';
|
|
3
3
|
export { default as getFieldsActionMatchingPermissions } from './getFieldsActionMatchingPermissions';
|
|
4
|
+
export { default as getCustomFieldUidsFromLayout } from './getCustomFieldUidsFromLayout';
|
|
@@ -30,7 +30,7 @@ const EditViewLayoutManager = ({ layout, ...rest }) => {
|
|
|
30
30
|
return <LoadingIndicatorPage />;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
return <Permissions {...rest}
|
|
33
|
+
return <Permissions {...rest} userPermissions={permissions} />;
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
EditViewLayoutManager.propTypes = {
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
SET_DATA_STRUCTURES,
|
|
7
7
|
SET_STATUS,
|
|
8
8
|
SUBMIT_SUCCEEDED,
|
|
9
|
+
CLEAR_SET_MODIFIED_DATA_ONLY,
|
|
9
10
|
} from './constants';
|
|
10
11
|
|
|
11
12
|
export const getData = () => {
|
|
@@ -42,3 +43,7 @@ export const submitSucceeded = (data) => ({
|
|
|
42
43
|
type: SUBMIT_SUCCEEDED,
|
|
43
44
|
data,
|
|
44
45
|
});
|
|
46
|
+
|
|
47
|
+
export const clearSetModifiedDataOnly = () => ({
|
|
48
|
+
type: CLEAR_SET_MODIFIED_DATA_ONLY,
|
|
49
|
+
});
|
|
@@ -5,3 +5,5 @@ export const RESET_PROPS = 'ContentManager/CrudReducer/RESET_PROPS';
|
|
|
5
5
|
export const SET_DATA_STRUCTURES = 'ContentManager/CrudReducer/SET_DATA_STRUCTURES';
|
|
6
6
|
export const SET_STATUS = 'ContentManager/CrudReducer/SET_STATUS';
|
|
7
7
|
export const SUBMIT_SUCCEEDED = 'ContentManager/CrudReducer/SUBMIT_SUCCEEDED';
|
|
8
|
+
export const CLEAR_SET_MODIFIED_DATA_ONLY =
|
|
9
|
+
'ContentManager/CrudReducer/CLEAR_SET_MODIFIED_DATA_ONLY';
|
|
@@ -8,6 +8,7 @@ import produce from 'immer';
|
|
|
8
8
|
// to do any of this.
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
|
+
CLEAR_SET_MODIFIED_DATA_ONLY,
|
|
11
12
|
GET_DATA,
|
|
12
13
|
GET_DATA_SUCCEEDED,
|
|
13
14
|
INIT_FORM,
|
|
@@ -23,6 +24,7 @@ const crudInitialState = {
|
|
|
23
24
|
isLoading: true,
|
|
24
25
|
data: null,
|
|
25
26
|
status: 'resolved',
|
|
27
|
+
setModifiedDataOnly: false,
|
|
26
28
|
};
|
|
27
29
|
|
|
28
30
|
const crudReducer = (state = crudInitialState, action) =>
|
|
@@ -36,6 +38,7 @@ const crudReducer = (state = crudInitialState, action) =>
|
|
|
36
38
|
case GET_DATA_SUCCEEDED: {
|
|
37
39
|
draftState.isLoading = false;
|
|
38
40
|
draftState.data = action.data;
|
|
41
|
+
draftState.setModifiedDataOnly = action.setModifiedDataOnly ?? false;
|
|
39
42
|
break;
|
|
40
43
|
}
|
|
41
44
|
case INIT_FORM: {
|
|
@@ -66,6 +69,10 @@ const crudReducer = (state = crudInitialState, action) =>
|
|
|
66
69
|
draftState.data = action.data;
|
|
67
70
|
break;
|
|
68
71
|
}
|
|
72
|
+
case CLEAR_SET_MODIFIED_DATA_ONLY: {
|
|
73
|
+
draftState.setModifiedDataOnly = false;
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
69
76
|
default:
|
|
70
77
|
return draftState;
|
|
71
78
|
}
|
|
@@ -19,6 +19,40 @@ const ALLOWED_TYPES = [
|
|
|
19
19
|
'uid',
|
|
20
20
|
];
|
|
21
21
|
|
|
22
|
+
const ALLOWED_ROOT_LEVEL_OPTIONS = [
|
|
23
|
+
'min',
|
|
24
|
+
'minLength',
|
|
25
|
+
'max',
|
|
26
|
+
'maxLength',
|
|
27
|
+
'required',
|
|
28
|
+
'regex',
|
|
29
|
+
'enum',
|
|
30
|
+
'unique',
|
|
31
|
+
'private',
|
|
32
|
+
'default',
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const optionValidationsReducer = (acc, option) => {
|
|
36
|
+
if (option.items) {
|
|
37
|
+
return option.items.reduce(optionValidationsReducer, acc);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!option.name) {
|
|
41
|
+
acc.push({
|
|
42
|
+
isValidOptionPath: false,
|
|
43
|
+
errorMessage: "The 'name' property is required on an options object",
|
|
44
|
+
});
|
|
45
|
+
} else {
|
|
46
|
+
acc.push({
|
|
47
|
+
isValidOptionPath:
|
|
48
|
+
ALLOWED_ROOT_LEVEL_OPTIONS.includes(option.name) || option.name.startsWith('options'),
|
|
49
|
+
errorMessage: `'${option.name}' must be prefixed with 'options.'`,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return acc;
|
|
54
|
+
};
|
|
55
|
+
|
|
22
56
|
class CustomFields {
|
|
23
57
|
constructor() {
|
|
24
58
|
this.customFields = {};
|
|
@@ -32,7 +66,8 @@ class CustomFields {
|
|
|
32
66
|
});
|
|
33
67
|
} else {
|
|
34
68
|
// Handle individual custom field
|
|
35
|
-
const { name, pluginId, type, intlLabel, intlDescription, components } =
|
|
69
|
+
const { name, pluginId, type, intlLabel, intlDescription, components, options } =
|
|
70
|
+
customFields;
|
|
36
71
|
|
|
37
72
|
// Ensure required attributes are provided
|
|
38
73
|
invariant(name, 'A name must be provided');
|
|
@@ -55,6 +90,16 @@ class CustomFields {
|
|
|
55
90
|
`Custom field name: '${name}' is not a valid object key`
|
|
56
91
|
);
|
|
57
92
|
|
|
93
|
+
// Ensure options have valid name paths
|
|
94
|
+
const allFormOptions = [...(options?.base || []), ...(options?.advanced || [])];
|
|
95
|
+
|
|
96
|
+
if (allFormOptions.length) {
|
|
97
|
+
const optionPathValidations = allFormOptions.reduce(optionValidationsReducer, []);
|
|
98
|
+
optionPathValidations.forEach(({ isValidOptionPath, errorMessage }) => {
|
|
99
|
+
invariant(isValidOptionPath, errorMessage);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
58
103
|
// When no plugin is specified, default to the global namespace
|
|
59
104
|
const uid = pluginId ? `plugin::${pluginId}.${name}` : `global::${name}`;
|
|
60
105
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
|
-
import { auth } from '@strapi/helper-plugin';
|
|
2
|
+
import { auth, wrapAxiosInstance } from '@strapi/helper-plugin';
|
|
3
3
|
|
|
4
4
|
const instance = axios.create({
|
|
5
5
|
baseURL: process.env.STRAPI_ADMIN_BACKEND_URL,
|
|
@@ -33,4 +33,6 @@ instance.interceptors.response.use(
|
|
|
33
33
|
}
|
|
34
34
|
);
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
const wrapper = wrapAxiosInstance(instance);
|
|
37
|
+
|
|
38
|
+
export default wrapper;
|
package/admin/src/hooks/index.js
CHANGED
|
@@ -10,3 +10,4 @@ export { default as usePermissionsDataManager } from './usePermissionsDataManage
|
|
|
10
10
|
export { default as useReleaseNotification } from './useReleaseNotification';
|
|
11
11
|
export { default as useThemeToggle } from './useThemeToggle';
|
|
12
12
|
export { default as useRegenerate } from './useRegenerate';
|
|
13
|
+
export { default as useFetchClient } from './useFetchClient';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
import { getFetchClient } from '../../utils/getFetchClient';
|
|
3
|
+
|
|
4
|
+
const useFetchClient = () => {
|
|
5
|
+
const controller = useRef(null);
|
|
6
|
+
|
|
7
|
+
if (controller.current === null) {
|
|
8
|
+
controller.current = new AbortController();
|
|
9
|
+
}
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
return () => {
|
|
12
|
+
controller.current.abort();
|
|
13
|
+
};
|
|
14
|
+
}, []);
|
|
15
|
+
|
|
16
|
+
const defaultOptions = {
|
|
17
|
+
signal: controller.current.signal,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
return getFetchClient(defaultOptions);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default useFetchClient;
|
|
@@ -25,7 +25,7 @@ import NotFoundPage from '../NotFoundPage';
|
|
|
25
25
|
import UseCasePage from '../UseCasePage';
|
|
26
26
|
import { getUID } from './utils';
|
|
27
27
|
import routes from './utils/routes';
|
|
28
|
-
import { useConfigurations } from '../../hooks';
|
|
28
|
+
import { useConfigurations, useFetchClient } from '../../hooks';
|
|
29
29
|
|
|
30
30
|
const AuthenticatedApp = lazy(() =>
|
|
31
31
|
import(/* webpackChunkName: "Admin-authenticatedApp" */ '../../components/AuthenticatedApp')
|
|
@@ -35,8 +35,12 @@ function App() {
|
|
|
35
35
|
const toggleNotification = useNotification();
|
|
36
36
|
const { updateProjectSettings } = useConfigurations();
|
|
37
37
|
const { formatMessage } = useIntl();
|
|
38
|
-
const [{ isLoading, hasAdmin, uuid }, setState] = useState({
|
|
38
|
+
const [{ isLoading, hasAdmin, uuid, deviceId }, setState] = useState({
|
|
39
|
+
isLoading: true,
|
|
40
|
+
hasAdmin: false,
|
|
41
|
+
});
|
|
39
42
|
const appInfo = useAppInfos();
|
|
43
|
+
const { get } = useFetchClient();
|
|
40
44
|
|
|
41
45
|
const authRoutes = useMemo(() => {
|
|
42
46
|
return makeUniqueRoutes(
|
|
@@ -80,27 +84,29 @@ function App() {
|
|
|
80
84
|
} = await axios.get(`${strapi.backendURL}/admin/init`);
|
|
81
85
|
|
|
82
86
|
updateProjectSettings({ menuLogo: prefixFileUrlWithBackendUrl(menuLogo) });
|
|
87
|
+
const deviceId = await getUID();
|
|
83
88
|
|
|
84
89
|
if (uuid) {
|
|
85
90
|
const {
|
|
86
91
|
data: { data: properties },
|
|
87
|
-
} = await
|
|
92
|
+
} = await get(`/admin/telemetry-properties`, {
|
|
93
|
+
// NOTE: needed because the interceptors of the fetchClient redirect to /login when receive a 401 and it would end up in an infinite loop when the user doesn't have a session.
|
|
94
|
+
validateStatus: (status) => status < 500,
|
|
95
|
+
});
|
|
88
96
|
|
|
89
97
|
setTelemetryProperties(properties);
|
|
90
98
|
|
|
91
99
|
try {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
await fetch('https://analytics.strapi.io/track', {
|
|
100
|
+
await fetch('https://analytics.strapi.io/api/v2/track', {
|
|
95
101
|
method: 'POST',
|
|
96
102
|
body: JSON.stringify({
|
|
103
|
+
// This event is anonymous
|
|
97
104
|
event: 'didInitializeAdministration',
|
|
98
|
-
|
|
105
|
+
userId: '',
|
|
99
106
|
deviceId,
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
},
|
|
107
|
+
eventPropeties: {},
|
|
108
|
+
userProperties: { environment: appInfo.currentEnvironment },
|
|
109
|
+
groupProperties: { ...properties, projectId: uuid },
|
|
104
110
|
}),
|
|
105
111
|
headers: {
|
|
106
112
|
'Content-Type': 'application/json',
|
|
@@ -111,7 +117,7 @@ function App() {
|
|
|
111
117
|
}
|
|
112
118
|
}
|
|
113
119
|
|
|
114
|
-
setState({ isLoading: false, hasAdmin, uuid });
|
|
120
|
+
setState({ isLoading: false, hasAdmin, uuid, deviceId });
|
|
115
121
|
} catch (err) {
|
|
116
122
|
toggleNotification({
|
|
117
123
|
type: 'warning',
|
|
@@ -130,8 +136,9 @@ function App() {
|
|
|
130
136
|
() => ({
|
|
131
137
|
uuid,
|
|
132
138
|
telemetryProperties,
|
|
139
|
+
deviceId,
|
|
133
140
|
}),
|
|
134
|
-
[uuid, telemetryProperties]
|
|
141
|
+
[uuid, telemetryProperties, deviceId]
|
|
135
142
|
);
|
|
136
143
|
|
|
137
144
|
if (isLoading) {
|
|
@@ -33,13 +33,13 @@ const StyledReddit = styled(Reddit)`
|
|
|
33
33
|
`;
|
|
34
34
|
const StyledStrapi = styled(Strapi)`
|
|
35
35
|
> path:first-child {
|
|
36
|
-
fill: #
|
|
36
|
+
fill: #4945ff;
|
|
37
37
|
}
|
|
38
38
|
> path:nth-child(2) {
|
|
39
|
-
fill: #
|
|
39
|
+
fill: #fff;
|
|
40
40
|
}
|
|
41
|
-
> path:nth-child(
|
|
42
|
-
fill: #
|
|
41
|
+
> path:nth-child(4) {
|
|
42
|
+
fill: #9593ff;
|
|
43
43
|
}
|
|
44
44
|
`;
|
|
45
45
|
|