@strapi/admin 4.4.0-beta.1 → 4.4.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/admin/src/StrapiApp.js +4 -12
  2. package/admin/src/components/Providers/index.js +14 -10
  3. package/admin/src/content-manager/components/FieldTypeIcon/index.js +31 -1
  4. package/admin/src/content-manager/components/Inputs/index.js +30 -10
  5. package/admin/src/content-manager/pages/EditSettingsView/components/FormModal.js +7 -2
  6. package/admin/src/content-manager/pages/EditSettingsView/index.js +2 -1
  7. package/admin/src/content-manager/pages/EditView/index.js +91 -84
  8. package/admin/src/core/apis/CustomFields.js +80 -0
  9. package/admin/src/core/apis/index.js +1 -0
  10. package/admin/src/hooks/index.js +0 -1
  11. package/admin/src/pages/AuthPage/utils/forms.js +2 -2
  12. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js +180 -452
  13. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/schema.js +1 -2
  14. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/DeleteButton/index.js +0 -1
  15. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/UpdateButton/index.js +36 -3
  16. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/index.js +11 -13
  17. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/index.js +2 -3
  18. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/utils/tableHeaders.js +8 -8
  19. package/admin/src/pages/SettingsPage/pages/ApiTokens/ProtectedEditView/index.js +1 -1
  20. package/admin/src/pages/SettingsPage/pages/Users/ListPage/ModalForm/utils/schema.js +1 -1
  21. package/admin/src/pages/SettingsPage/pages/Users/utils/validations/users/profile.js +2 -2
  22. package/admin/src/permissions/defaultPermissions.js +6 -2
  23. package/admin/src/translations/en.json +0 -17
  24. package/build/524.40377968.chunk.js +644 -0
  25. package/build/{Admin-authenticatedApp.3a31a087.chunk.js → Admin-authenticatedApp.50e41ff2.chunk.js} +1 -1
  26. package/build/{Admin_homePage.6d5e3236.chunk.js → Admin_homePage.118926e0.chunk.js} +1 -1
  27. package/build/{Admin_profilePage.83991a6c.chunk.js → Admin_profilePage.9d50ac44.chunk.js} +4 -4
  28. package/build/{Admin_settingsPage.fc9c607a.chunk.js → Admin_settingsPage.98a711e5.chunk.js} +16 -16
  29. package/build/admin-app.8bc3e80f.chunk.js +112 -0
  30. package/build/admin-edit-roles-page.554ba3fa.chunk.js +1 -0
  31. package/build/{admin-edit-users.5bebf473.chunk.js → admin-edit-users.c585212f.chunk.js} +2 -2
  32. package/build/{admin-users.dccd5f4c.chunk.js → admin-users.97a08630.chunk.js} +1 -1
  33. package/build/api-tokens-create-page.4c262d6e.chunk.js +1 -0
  34. package/build/api-tokens-edit-page.10a9d368.chunk.js +1 -0
  35. package/build/api-tokens-list-page.442c9f3c.chunk.js +15 -0
  36. package/build/content-manager.2a6f876d.chunk.js +1178 -0
  37. package/build/content-type-builder-list-view.5b3cd768.chunk.js +194 -0
  38. package/build/content-type-builder-translation-en-json.f985c9c4.chunk.js +1 -0
  39. package/build/content-type-builder.d4610e20.chunk.js +145 -0
  40. package/build/en-json.12bc5a14.chunk.js +1 -0
  41. package/build/index.html +1 -1
  42. package/build/{main.cdfda31e.js → main.fdc482f3.js} +1151 -1151
  43. package/build/{runtime~main.fa8f8898.js → runtime~main.29105d25.js} +2 -2
  44. package/build/sso-settings-page.445184e0.chunk.js +1 -0
  45. package/build/{webhook-edit-page.9e46fc3f.chunk.js → webhook-edit-page.d2ea3351.chunk.js} +1 -1
  46. package/package.json +7 -8
  47. package/server/bootstrap.js +1 -19
  48. package/server/config/admin-actions.js +0 -20
  49. package/server/content-types/api-token.js +1 -25
  50. package/server/content-types/index.js +0 -1
  51. package/server/controllers/api-token.js +1 -24
  52. package/server/controllers/index.js +0 -1
  53. package/server/routes/api-tokens.js +0 -11
  54. package/server/routes/index.js +0 -2
  55. package/server/services/api-token.js +29 -310
  56. package/server/services/constants.js +0 -10
  57. package/server/services/permission/engine-hooks.js +82 -0
  58. package/server/services/permission/engine.js +226 -36
  59. package/server/services/permission.js +1 -4
  60. package/server/strategies/admin.js +1 -7
  61. package/server/strategies/api-token.js +11 -71
  62. package/server/validation/api-tokens.js +2 -12
  63. package/server/validation/common-validators.js +1 -1
  64. package/admin/src/contexts/ApiTokenPermissions/index.js +0 -24
  65. package/admin/src/hooks/useRegenerate/index.js +0 -34
  66. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/ActionBoundRoutes/index.js +0 -56
  67. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/BoundRoute/getMethodColor.js +0 -41
  68. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/BoundRoute/index.js +0 -72
  69. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/CollapsableContentType/CheckBoxWrapper.js +0 -30
  70. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/CollapsableContentType/index.js +0 -150
  71. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/ContenTypesSection/index.js +0 -37
  72. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/Permissions/index.js +0 -40
  73. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/Regenerate/index.js +0 -68
  74. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/init.js +0 -13
  75. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/reducer.js +0 -55
  76. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/getDateOfExpiration.js +0 -16
  77. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/index.js +0 -5
  78. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/transformPermissionsData.js +0 -36
  79. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/DefaultButton/index.js +0 -63
  80. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/ReadButton/index.js +0 -19
  81. package/build/4235.982b5799.chunk.js +0 -30
  82. package/build/611.a91aff91.chunk.js +0 -158
  83. package/build/7379.d246dd38.chunk.js +0 -1
  84. package/build/admin-app.41b6472c.chunk.js +0 -112
  85. package/build/admin-edit-roles-page.4dd6bcb9.chunk.js +0 -1
  86. package/build/api-tokens-create-page.29cc87b6.chunk.js +0 -1
  87. package/build/api-tokens-edit-page.c294a88f.chunk.js +0 -1
  88. package/build/api-tokens-list-page.bb36535f.chunk.js +0 -16
  89. package/build/content-manager.fb5ee865.chunk.js +0 -1178
  90. package/build/content-type-builder-list-view.8cc534e0.chunk.js +0 -194
  91. package/build/content-type-builder-translation-en-json.201bfb78.chunk.js +0 -1
  92. package/build/content-type-builder.42cecba9.chunk.js +0 -142
  93. package/build/en-json.a9918c93.chunk.js +0 -1
  94. package/build/sso-settings-page.9ceb0140.chunk.js +0 -1
  95. package/server/content-types/api-token-permission.js +0 -36
  96. package/server/controllers/content-api.js +0 -15
  97. package/server/routes/content-api.js +0 -20
@@ -8,7 +8,7 @@ import invariant from 'invariant';
8
8
  import { Helmet } from 'react-helmet';
9
9
  import { basename, createHook } from './core/utils';
10
10
  import configureStore from './core/store/configureStore';
11
- import { Plugin } from './core/apis';
11
+ import { customFields, Plugin } from './core/apis';
12
12
  import App from './pages/App';
13
13
  import AuthLogo from './assets/images/logo_strapi_auth_v4.png';
14
14
  import MenuLogo from './assets/images/logo_strapi_menu.png';
@@ -47,6 +47,7 @@ class StrapiApp {
47
47
  this.admin = {
48
48
  injectionZones,
49
49
  };
50
+ this.customFields = customFields;
50
51
 
51
52
  this.menu = [];
52
53
  this.settings = {
@@ -280,17 +281,7 @@ class StrapiApp {
280
281
 
281
282
  async initialize() {
282
283
  Object.keys(this.appPlugins).forEach((plugin) => {
283
- this.appPlugins[plugin].register({
284
- addComponents: this.addComponents,
285
- addCorePluginMenuLink: this.addCorePluginMenuLink,
286
- addFields: this.addFields,
287
- addMenuLink: this.addMenuLink,
288
- addMiddlewares: this.addMiddlewares,
289
- addReducers: this.addReducers,
290
- createHook: this.createHook,
291
- createSettingSection: this.createSettingSection,
292
- registerPlugin: this.registerPlugin,
293
- });
284
+ this.appPlugins[plugin].register(this);
294
285
  });
295
286
  }
296
287
 
@@ -430,6 +421,7 @@ class StrapiApp {
430
421
  authLogo={this.configurations.authLogo}
431
422
  components={components}
432
423
  fields={fields}
424
+ customFields={this.customFields}
433
425
  localeNames={localeNames}
434
426
  getAdminInjectedComponents={this.getAdminInjectedComponents}
435
427
  getPlugin={this.getPlugin}
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { QueryClientProvider, QueryClient } from 'react-query';
4
- import { LibraryProvider, StrapiAppProvider } from '@strapi/helper-plugin';
4
+ import { LibraryProvider, CustomFieldsProvider, StrapiAppProvider } from '@strapi/helper-plugin';
5
5
  import { Provider } from 'react-redux';
6
6
  import { AdminContext } from '../../contexts';
7
7
  import ConfigurationsProvider from '../ConfigurationsProvider';
@@ -25,6 +25,7 @@ const Providers = ({
25
25
  authLogo,
26
26
  children,
27
27
  components,
28
+ customFields,
28
29
  fields,
29
30
  getAdminInjectedComponents,
30
31
  getPlugin,
@@ -64,15 +65,17 @@ const Providers = ({
64
65
  settings={settings}
65
66
  >
66
67
  <LibraryProvider components={components} fields={fields}>
67
- <LanguageProvider messages={messages} localeNames={localeNames}>
68
- <AutoReloadOverlayBlockerProvider>
69
- <OverlayBlocker>
70
- <GuidedTour>
71
- <Notifications>{children}</Notifications>
72
- </GuidedTour>
73
- </OverlayBlocker>
74
- </AutoReloadOverlayBlockerProvider>
75
- </LanguageProvider>
68
+ <CustomFieldsProvider customFields={customFields}>
69
+ <LanguageProvider messages={messages} localeNames={localeNames}>
70
+ <AutoReloadOverlayBlockerProvider>
71
+ <OverlayBlocker>
72
+ <GuidedTour>
73
+ <Notifications>{children}</Notifications>
74
+ </GuidedTour>
75
+ </OverlayBlocker>
76
+ </AutoReloadOverlayBlockerProvider>
77
+ </LanguageProvider>
78
+ </CustomFieldsProvider>
76
79
  </LibraryProvider>
77
80
  </StrapiAppProvider>
78
81
  </ConfigurationsProvider>
@@ -88,6 +91,7 @@ Providers.propTypes = {
88
91
  authLogo: PropTypes.oneOfType([PropTypes.string, PropTypes.any]).isRequired,
89
92
  children: PropTypes.element.isRequired,
90
93
  components: PropTypes.object.isRequired,
94
+ customFields: PropTypes.object.isRequired,
91
95
  fields: PropTypes.object.isRequired,
92
96
  getAdminInjectedComponents: PropTypes.func.isRequired,
93
97
  getPlugin: PropTypes.func.isRequired,
@@ -1,5 +1,7 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
+ import { Box } from '@strapi/design-system/Box';
4
+ import { useCustomFields } from '@strapi/helper-plugin';
3
5
  import Date from '@strapi/icons/Date';
4
6
  import Boolean from '@strapi/icons/Boolean';
5
7
  import Email from '@strapi/icons/Email';
@@ -40,10 +42,38 @@ const iconByTypes = {
40
42
  dynamiczone: <DynamicZone />,
41
43
  };
42
44
 
43
- const FieldTypeIcon = ({ type }) => iconByTypes[type] || null;
45
+ const FieldTypeIcon = ({ type, customFieldUid }) => {
46
+ const customFieldsRegistry = useCustomFields();
47
+
48
+ let Compo = iconByTypes[type];
49
+
50
+ if (customFieldUid) {
51
+ const customField = customFieldsRegistry.get(customFieldUid);
52
+ const CustomFieldIcon = customField.icon;
53
+
54
+ if (CustomFieldIcon) {
55
+ Compo = (
56
+ <Box marginRight={3} width={7} height={6}>
57
+ <CustomFieldIcon />
58
+ </Box>
59
+ );
60
+ }
61
+ }
62
+
63
+ if (!iconByTypes[type]) {
64
+ return null;
65
+ }
66
+
67
+ return Compo;
68
+ };
69
+
70
+ FieldTypeIcon.defaultProps = {
71
+ customFieldUid: null,
72
+ };
44
73
 
45
74
  FieldTypeIcon.propTypes = {
46
75
  type: PropTypes.string.isRequired,
76
+ customFieldUid: PropTypes.string,
47
77
  };
48
78
 
49
79
  export default FieldTypeIcon;
@@ -5,7 +5,7 @@ import get from 'lodash/get';
5
5
  import omit from 'lodash/omit';
6
6
  import take from 'lodash/take';
7
7
  import isEqual from 'react-fast-compare';
8
- import { GenericInput, NotAllowedInput, useLibrary } from '@strapi/helper-plugin';
8
+ import { GenericInput, NotAllowedInput, useLibrary, useCustomFields } from '@strapi/helper-plugin';
9
9
  import { useContentTypeLayout } from '../../hooks';
10
10
  import { getFieldName } from '../../utils';
11
11
  import Wysiwyg from '../Wysiwyg';
@@ -39,9 +39,10 @@ function Inputs({
39
39
  const { fields } = useLibrary();
40
40
  const { formatMessage } = useIntl();
41
41
  const { contentType: currentContentTypeLayout } = useContentTypeLayout();
42
+ const customFieldsRegistry = useCustomFields();
42
43
 
43
44
  const disabled = useMemo(() => !get(metadatas, 'editable', true), [metadatas]);
44
- const type = fieldSchema.type;
45
+ const { type, customField: customFieldUid } = fieldSchema;
45
46
  const error = get(formErrors, [keys], null);
46
47
 
47
48
  const fieldName = useMemo(() => {
@@ -191,6 +192,19 @@ function Inputs({
191
192
  return minutes % metadatas.step === 0 ? metadatas.step : step;
192
193
  }, [inputType, inputValue, metadatas.step, step]);
193
194
 
195
+ // Memoize the component to avoid remounting it and losing state
196
+ const CustomFieldInput = useMemo(() => {
197
+ if (customFieldUid) {
198
+ const customField = customFieldsRegistry.get(customFieldUid);
199
+ const CustomFieldInput = React.lazy(customField.components.Input);
200
+
201
+ return CustomFieldInput;
202
+ }
203
+
204
+ // Not a custom field, component won't be used
205
+ return null;
206
+ }, [customFieldUid, customFieldsRegistry]);
207
+
194
208
  if (visible === false) {
195
209
  return null;
196
210
  }
@@ -244,6 +258,18 @@ function Inputs({
244
258
  );
245
259
  }
246
260
 
261
+ const customInputs = {
262
+ json: InputJSON,
263
+ uid: InputUID,
264
+ media: fields.media,
265
+ wysiwyg: Wysiwyg,
266
+ ...fields,
267
+ };
268
+
269
+ if (customFieldUid) {
270
+ customInputs[customFieldUid] = CustomFieldInput;
271
+ }
272
+
247
273
  return (
248
274
  <GenericInput
249
275
  attribute={fieldSchema}
@@ -256,13 +282,7 @@ function Inputs({
256
282
  error={error}
257
283
  labelAction={labelAction}
258
284
  contentTypeUID={currentContentTypeLayout.uid}
259
- customInputs={{
260
- json: InputJSON,
261
- uid: InputUID,
262
- media: fields.media,
263
- wysiwyg: Wysiwyg,
264
- ...fields,
265
- }}
285
+ customInputs={customInputs}
266
286
  multiple={fieldSchema.multiple || false}
267
287
  name={keys}
268
288
  onChange={onChange}
@@ -270,7 +290,7 @@ function Inputs({
270
290
  placeholder={placeholder ? { id: placeholder, defaultMessage: placeholder } : null}
271
291
  required={fieldSchema.required || false}
272
292
  step={inputStep}
273
- type={inputType}
293
+ type={customFieldUid || inputType}
274
294
  // validations={validations}
275
295
  value={inputValue}
276
296
  withDefaultValue={false}
@@ -26,7 +26,7 @@ const HeaderContainer = styled(Flex)`
26
26
  }
27
27
  `;
28
28
 
29
- const FormModal = ({ onToggle, onMetaChange, onSizeChange, onSubmit, type }) => {
29
+ const FormModal = ({ onToggle, onMetaChange, onSizeChange, onSubmit, type, customFieldUid }) => {
30
30
  const { selectedField } = useLayoutDnd();
31
31
  const { formatMessage } = useIntl();
32
32
 
@@ -47,7 +47,7 @@ const FormModal = ({ onToggle, onMetaChange, onSizeChange, onSubmit, type }) =>
47
47
  <form onSubmit={onSubmit}>
48
48
  <ModalHeader>
49
49
  <HeaderContainer>
50
- <FieldTypeIcon type={getAttrType(type)} />
50
+ <FieldTypeIcon type={getAttrType()} customFieldUid={customFieldUid} />
51
51
  <Typography fontWeight="bold" textColor="neutral800" as="h2" id="title">
52
52
  {formatMessage(
53
53
  {
@@ -81,7 +81,12 @@ const FormModal = ({ onToggle, onMetaChange, onSizeChange, onSubmit, type }) =>
81
81
  );
82
82
  };
83
83
 
84
+ FormModal.defaultProps = {
85
+ customFieldUid: null,
86
+ };
87
+
84
88
  FormModal.propTypes = {
89
+ customFieldUid: PropTypes.string,
85
90
  onSubmit: PropTypes.func.isRequired,
86
91
  onToggle: PropTypes.func.isRequired,
87
92
  onMetaChange: PropTypes.func.isRequired,
@@ -371,9 +371,10 @@ const EditSettingsView = ({ mainLayout, components, isContentTypeView, slug, upd
371
371
  <ModalForm
372
372
  onSubmit={handleMetaSubmit}
373
373
  onToggle={handleToggleModal}
374
- type={get(attributes, [metaToEdit, 'type'], '')}
375
374
  onMetaChange={handleMetaChange}
376
375
  onSizeChange={handleSizeChange}
376
+ type={get(attributes, [metaToEdit, 'type'], '')}
377
+ customFieldUid={get(attributes, [metaToEdit, 'customField'], '')}
377
378
  />
378
379
  )}
379
380
  </Main>
@@ -1,7 +1,12 @@
1
- import React, { memo, useCallback, useMemo } from 'react';
1
+ import React, { Suspense, memo, useCallback, useMemo } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import get from 'lodash/get';
4
- import { CheckPermissions, useTracking, LinkButton } from '@strapi/helper-plugin';
4
+ import {
5
+ CheckPermissions,
6
+ LoadingIndicatorPage,
7
+ useTracking,
8
+ LinkButton,
9
+ } from '@strapi/helper-plugin';
5
10
  import { useIntl } from 'react-intl';
6
11
  import { ContentLayout } from '@strapi/design-system/Layout';
7
12
  import { Box } from '@strapi/design-system/Box';
@@ -131,99 +136,101 @@ const EditView = ({
131
136
  <ContentLayout>
132
137
  <Grid gap={4}>
133
138
  <GridItem col={9} s={12}>
134
- <Stack spacing={6}>
135
- {formattedContentTypeLayout.map((row, index) => {
136
- if (isDynamicZone(row)) {
137
- const {
138
- 0: {
139
- 0: { name, fieldSchema, metadatas, labelAction },
140
- },
141
- } = row;
139
+ <Suspense fallback={<LoadingIndicatorPage />}>
140
+ <Stack spacing={6}>
141
+ {formattedContentTypeLayout.map((row, index) => {
142
+ if (isDynamicZone(row)) {
143
+ const {
144
+ 0: {
145
+ 0: { name, fieldSchema, metadatas, labelAction },
146
+ },
147
+ } = row;
148
+
149
+ return (
150
+ <Box key={index}>
151
+ <Grid gap={4}>
152
+ <GridItem col={12} s={12} xs={12}>
153
+ <DynamicZone
154
+ name={name}
155
+ fieldSchema={fieldSchema}
156
+ labelAction={labelAction}
157
+ metadatas={metadatas}
158
+ />
159
+ </GridItem>
160
+ </Grid>
161
+ </Box>
162
+ );
163
+ }
142
164
 
143
165
  return (
144
- <Box key={index}>
145
- <Grid gap={4}>
146
- <GridItem col={12} s={12} xs={12}>
147
- <DynamicZone
148
- name={name}
149
- fieldSchema={fieldSchema}
150
- labelAction={labelAction}
151
- metadatas={metadatas}
152
- />
153
- </GridItem>
154
- </Grid>
155
- </Box>
156
- );
157
- }
166
+ <Box
167
+ key={index}
168
+ hasRadius
169
+ background="neutral0"
170
+ shadow="tableShadow"
171
+ paddingLeft={6}
172
+ paddingRight={6}
173
+ paddingTop={6}
174
+ paddingBottom={6}
175
+ borderColor="neutral150"
176
+ >
177
+ <Stack spacing={6}>
178
+ {row.map((grid, gridIndex) => {
179
+ return (
180
+ <Grid gap={4} key={gridIndex}>
181
+ {grid.map(
182
+ ({ fieldSchema, labelAction, metadatas, name, size }) => {
183
+ const isComponent = fieldSchema.type === 'component';
158
184
 
159
- return (
160
- <Box
161
- key={index}
162
- hasRadius
163
- background="neutral0"
164
- shadow="tableShadow"
165
- paddingLeft={6}
166
- paddingRight={6}
167
- paddingTop={6}
168
- paddingBottom={6}
169
- borderColor="neutral150"
170
- >
171
- <Stack spacing={6}>
172
- {row.map((grid, gridIndex) => {
173
- return (
174
- <Grid gap={4} key={gridIndex}>
175
- {grid.map(
176
- ({ fieldSchema, labelAction, metadatas, name, size }) => {
177
- const isComponent = fieldSchema.type === 'component';
185
+ if (isComponent) {
186
+ const {
187
+ component,
188
+ max,
189
+ min,
190
+ repeatable = false,
191
+ required = false,
192
+ } = fieldSchema;
178
193
 
179
- if (isComponent) {
180
- const {
181
- component,
182
- max,
183
- min,
184
- repeatable = false,
185
- required = false,
186
- } = fieldSchema;
194
+ return (
195
+ <GridItem col={size} s={12} xs={12} key={component}>
196
+ <FieldComponent
197
+ componentUid={component}
198
+ labelAction={labelAction}
199
+ isRepeatable={repeatable}
200
+ intlLabel={{
201
+ id: metadatas.label,
202
+ defaultMessage: metadatas.label,
203
+ }}
204
+ max={max}
205
+ min={min}
206
+ name={name}
207
+ required={required}
208
+ />
209
+ </GridItem>
210
+ );
211
+ }
187
212
 
188
213
  return (
189
- <GridItem col={size} s={12} xs={12} key={component}>
190
- <FieldComponent
191
- componentUid={component}
214
+ <GridItem col={size} key={name} s={12} xs={12}>
215
+ <Inputs
216
+ fieldSchema={fieldSchema}
217
+ keys={name}
192
218
  labelAction={labelAction}
193
- isRepeatable={repeatable}
194
- intlLabel={{
195
- id: metadatas.label,
196
- defaultMessage: metadatas.label,
197
- }}
198
- max={max}
199
- min={min}
200
- name={name}
201
- required={required}
219
+ metadatas={metadatas}
202
220
  />
203
221
  </GridItem>
204
222
  );
205
223
  }
206
-
207
- return (
208
- <GridItem col={size} key={name} s={12} xs={12}>
209
- <Inputs
210
- fieldSchema={fieldSchema}
211
- keys={name}
212
- labelAction={labelAction}
213
- metadatas={metadatas}
214
- />
215
- </GridItem>
216
- );
217
- }
218
- )}
219
- </Grid>
220
- );
221
- })}
222
- </Stack>
223
- </Box>
224
- );
225
- })}
226
- </Stack>
224
+ )}
225
+ </Grid>
226
+ );
227
+ })}
228
+ </Stack>
229
+ </Box>
230
+ );
231
+ })}
232
+ </Stack>
233
+ </Suspense>
227
234
  </GridItem>
228
235
  <GridItem col={3} s={12}>
229
236
  <Stack spacing={2}>
@@ -0,0 +1,80 @@
1
+ import invariant from 'invariant';
2
+
3
+ const ALLOWED_TYPES = [
4
+ 'biginteger',
5
+ 'boolean',
6
+ 'date',
7
+ 'datetime',
8
+ 'decimal',
9
+ 'email',
10
+ 'enumeration',
11
+ 'float',
12
+ 'integer',
13
+ 'json',
14
+ 'password',
15
+ 'richtext',
16
+ 'string',
17
+ 'text',
18
+ 'time',
19
+ 'timestamp',
20
+ 'uid',
21
+ ];
22
+
23
+ class CustomFields {
24
+ constructor() {
25
+ this.customFields = {};
26
+ }
27
+
28
+ register(customFields) {
29
+ if (Array.isArray(customFields)) {
30
+ // If several custom fields are passed, register them one by one
31
+ customFields.forEach((customField) => {
32
+ this.register(customField);
33
+ });
34
+ } else {
35
+ // Handle individual custom field
36
+ const { name, pluginId, type, intlLabel, intlDescription, components } = customFields;
37
+
38
+ // Ensure required attributes are provided
39
+ invariant(name, 'A name must be provided');
40
+ invariant(type, 'A type must be provided');
41
+ invariant(intlLabel, 'An intlLabel must be provided');
42
+ invariant(intlDescription, 'An intlDescription must be provided');
43
+ invariant(components, 'A components object must be provided');
44
+ invariant(components.Input, 'An Input component must be provided');
45
+
46
+ // Ensure the type is valid
47
+ invariant(
48
+ ALLOWED_TYPES.includes(type),
49
+ `Custom field type: '${type}' is not a valid Strapi type or it can't be used with a Custom Field`
50
+ );
51
+
52
+ // Ensure name has no special characters
53
+ const isValidObjectKey = /^(?![0-9])[a-zA-Z0-9$_-]+$/g;
54
+ invariant(
55
+ isValidObjectKey.test(name),
56
+ `Custom field name: '${name}' is not a valid object key`
57
+ );
58
+
59
+ // When no plugin is specified, default to the global namespace
60
+ const uid = pluginId ? `plugin::${pluginId}.${name}` : `global::${name}`;
61
+
62
+ // Ensure the uid is unique
63
+ const uidAlreadyUsed = Object.prototype.hasOwnProperty.call(this.customFields, uid);
64
+ invariant(!uidAlreadyUsed, `Custom field: '${uid}' has already been registered`);
65
+
66
+ this.customFields[uid] = customFields;
67
+ }
68
+ }
69
+
70
+ getAll() {
71
+ return this.customFields;
72
+ }
73
+
74
+ get(uid) {
75
+ return this.customFields[uid];
76
+ }
77
+ }
78
+
79
+ // Export an instance since it's a singleton
80
+ export default new CustomFields();
@@ -3,3 +3,4 @@ export { default as Components } from './Components';
3
3
  export { default as Middlewares } from './Middlewares';
4
4
  export { default as Plugin } from './Plugin';
5
5
  export { default as Reducers } from './Reducers';
6
+ export { default as customFields } from './CustomFields';
@@ -9,4 +9,3 @@ export { default as useSettingsForm } from './useSettingsForm';
9
9
  export { default as usePermissionsDataManager } from './usePermissionsDataManager';
10
10
  export { default as useReleaseNotification } from './useReleaseNotification';
11
11
  export { default as useThemeToggle } from './useThemeToggle';
12
- export { default as useRegenerate } from './useRegenerate';
@@ -52,7 +52,7 @@ const forms = {
52
52
  fieldsToDisable: ['email'],
53
53
  fieldsToOmit: ['userInfo.confirmPassword', 'userInfo.news', 'userInfo.email'],
54
54
  schema: yup.object().shape({
55
- firstname: yup.string().required(translatedErrors.required),
55
+ firstname: yup.string().trim().required(translatedErrors.required),
56
56
  lastname: yup.string(),
57
57
  password: yup
58
58
  .string()
@@ -76,7 +76,7 @@ const forms = {
76
76
  fieldsToDisable: [],
77
77
  fieldsToOmit: ['confirmPassword', 'news'],
78
78
  schema: yup.object().shape({
79
- firstname: yup.string().required(translatedErrors.required),
79
+ firstname: yup.string().trim().required(translatedErrors.required),
80
80
  lastname: yup.string(),
81
81
  password: yup
82
82
  .string()