@strapi/admin 4.4.0-rc.0 → 4.5.0-alpha.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.
Files changed (203) hide show
  1. package/admin/src/StrapiApp.js +12 -4
  2. package/admin/src/components/Providers/index.js +10 -14
  3. package/admin/src/content-manager/components/CollectionTypeFormWrapper/index.js +11 -1
  4. package/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/index.js +11 -7
  5. package/admin/src/content-manager/components/DynamicTable/CellContent/index.js +6 -5
  6. package/admin/src/content-manager/components/DynamicTable/TableRows/index.js +5 -0
  7. package/admin/src/content-manager/components/DynamicTable/index.js +1 -1
  8. package/admin/src/content-manager/components/EditViewDataManagerProvider/index.js +23 -17
  9. package/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js +123 -24
  10. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/cleanData.js +17 -1
  11. package/admin/src/content-manager/components/FieldTypeIcon/index.js +1 -31
  12. package/admin/src/content-manager/components/Inputs/index.js +22 -36
  13. package/admin/src/content-manager/components/RelationInput/RelationInput.js +364 -0
  14. package/admin/src/content-manager/components/{SelectWrapper → RelationInput/components}/Option.js +15 -25
  15. package/admin/src/content-manager/components/RelationInput/components/Relation.js +48 -0
  16. package/admin/src/content-manager/components/RelationInput/components/RelationItem.js +52 -0
  17. package/admin/src/content-manager/components/RelationInput/components/RelationList.js +52 -0
  18. package/admin/src/content-manager/components/RelationInput/constants.js +1 -0
  19. package/admin/src/content-manager/components/RelationInput/index.js +1 -0
  20. package/admin/src/content-manager/components/RelationInputDataManager/RelationInputDataManager.js +250 -0
  21. package/admin/src/content-manager/components/RelationInputDataManager/constants.js +8 -0
  22. package/admin/src/content-manager/components/RelationInputDataManager/index.js +1 -0
  23. package/admin/src/content-manager/components/{SelectWrapper → RelationInputDataManager}/utils/connect.js +0 -1
  24. package/admin/src/content-manager/components/RelationInputDataManager/utils/getRelationLink.js +5 -0
  25. package/admin/src/content-manager/components/{SelectWrapper → RelationInputDataManager}/utils/index.js +1 -0
  26. package/admin/src/content-manager/components/RelationInputDataManager/utils/normalizeRelations.js +58 -0
  27. package/admin/src/content-manager/components/{SelectWrapper → RelationInputDataManager}/utils/select.js +31 -1
  28. package/admin/src/content-manager/components/SingleTypeFormWrapper/index.js +19 -2
  29. package/admin/src/content-manager/hooks/useFetchContentTypeLayout/utils/formatLayouts.js +7 -69
  30. package/admin/src/content-manager/hooks/useRelation/index.js +1 -0
  31. package/admin/src/content-manager/hooks/useRelation/useRelation.js +73 -0
  32. package/admin/src/content-manager/pages/EditSettingsView/components/DisplayedFields.js +4 -4
  33. package/admin/src/content-manager/pages/EditSettingsView/components/FormModal.js +2 -7
  34. package/admin/src/content-manager/pages/EditSettingsView/index.js +22 -51
  35. package/admin/src/content-manager/pages/EditSettingsView/reducer.js +0 -25
  36. package/admin/src/content-manager/pages/EditView/Header/index.js +3 -90
  37. package/admin/src/content-manager/pages/EditView/Header/utils/index.js +0 -1
  38. package/admin/src/content-manager/pages/EditView/Header/utils/select.js +0 -2
  39. package/admin/src/content-manager/pages/EditView/index.js +93 -155
  40. package/admin/src/content-manager/pages/ListView/FieldPicker/index.js +0 -1
  41. package/admin/src/content-manager/pages/ListView/index.js +0 -1
  42. package/admin/src/content-manager/pages/ListViewLayoutManager/index.js +0 -1
  43. package/admin/src/content-manager/utils/formatLayoutToApi.js +1 -3
  44. package/admin/src/core/apis/index.js +0 -1
  45. package/admin/src/hooks/index.js +0 -1
  46. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js +197 -215
  47. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/schema.js +1 -2
  48. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/DeleteButton/index.js +0 -1
  49. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/UpdateButton/index.js +36 -3
  50. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/index.js +11 -13
  51. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/index.js +2 -3
  52. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/utils/tableHeaders.js +8 -8
  53. package/admin/src/pages/SettingsPage/pages/ApiTokens/ProtectedEditView/index.js +1 -1
  54. package/admin/src/permissions/defaultPermissions.js +6 -2
  55. package/admin/src/translations/ar.json +0 -1
  56. package/admin/src/translations/ca.json +0 -9
  57. package/admin/src/translations/cs.json +0 -2
  58. package/admin/src/translations/de.json +0 -11
  59. package/admin/src/translations/dk.json +0 -11
  60. package/admin/src/translations/en.json +0 -28
  61. package/admin/src/translations/es.json +0 -11
  62. package/admin/src/translations/fr.json +0 -11
  63. package/admin/src/translations/gu.json +0 -11
  64. package/admin/src/translations/hi.json +0 -11
  65. package/admin/src/translations/hu.json +0 -10
  66. package/admin/src/translations/id.json +0 -10
  67. package/admin/src/translations/it.json +0 -10
  68. package/admin/src/translations/ja.json +0 -11
  69. package/admin/src/translations/ko.json +0 -11
  70. package/admin/src/translations/ml.json +0 -11
  71. package/admin/src/translations/ms.json +0 -2
  72. package/admin/src/translations/nl.json +0 -11
  73. package/admin/src/translations/pl.json +0 -11
  74. package/admin/src/translations/pt-BR.json +0 -11
  75. package/admin/src/translations/pt.json +0 -1
  76. package/admin/src/translations/ru.json +0 -10
  77. package/admin/src/translations/sa.json +0 -11
  78. package/admin/src/translations/sk.json +0 -10
  79. package/admin/src/translations/sv.json +0 -9
  80. package/admin/src/translations/th.json +0 -2
  81. package/admin/src/translations/tr.json +0 -1
  82. package/admin/src/translations/uk.json +0 -2
  83. package/admin/src/translations/vi.json +0 -1
  84. package/admin/src/translations/zh-Hans.json +0 -12
  85. package/admin/src/translations/zh.json +0 -11
  86. package/build/7098.40dcd7bf.chunk.js +1 -0
  87. package/build/8851.e4ac62f2.chunk.js +158 -0
  88. package/build/{8773.c06c24c0.chunk.js → 9311.7cc03f29.chunk.js} +291 -108
  89. package/build/{Admin-authenticatedApp.9dec5230.chunk.js → Admin-authenticatedApp.e39f36c9.chunk.js} +3 -3
  90. package/build/{Admin_homePage.6d5e3236.chunk.js → Admin_homePage.118926e0.chunk.js} +1 -1
  91. package/build/{Admin_profilePage.da32abbc.chunk.js → Admin_profilePage.9d50ac44.chunk.js} +1 -1
  92. package/build/{Admin_settingsPage.98e2a62b.chunk.js → Admin_settingsPage.98a711e5.chunk.js} +16 -16
  93. package/build/admin-app.4f7618a9.chunk.js +112 -0
  94. package/build/admin-edit-roles-page.554ba3fa.chunk.js +1 -0
  95. package/build/api-tokens-create-page.4c262d6e.chunk.js +1 -0
  96. package/build/api-tokens-edit-page.10a9d368.chunk.js +1 -0
  97. package/build/api-tokens-list-page.442c9f3c.chunk.js +15 -0
  98. package/build/{ar-json.d4cb26d9.chunk.js → ar-json.3489463d.chunk.js} +1 -1
  99. package/build/{ca-json.d16c1d28.chunk.js → ca-json.a16899ae.chunk.js} +1 -1
  100. package/build/content-manager.7d57c9d1.chunk.js +1200 -0
  101. package/build/content-type-builder-list-view.8cc534e0.chunk.js +194 -0
  102. package/build/content-type-builder-translation-en-json.201bfb78.chunk.js +1 -0
  103. package/build/content-type-builder.684df7a4.chunk.js +142 -0
  104. package/build/{cs-json.c8f28ba8.chunk.js → cs-json.ce49da5c.chunk.js} +1 -1
  105. package/build/{de-json.a9b514dc.chunk.js → de-json.aa6026b3.chunk.js} +1 -1
  106. package/build/{dk-json.09e8d145.chunk.js → dk-json.fac2bcfb.chunk.js} +1 -1
  107. package/build/en-json.0c69c7d7.chunk.js +1 -0
  108. package/build/{es-json.3a9c7c09.chunk.js → es-json.d672e181.chunk.js} +1 -1
  109. package/build/{fr-json.4ed1fc2c.chunk.js → fr-json.71a16175.chunk.js} +1 -1
  110. package/build/{gu-json.d8311297.chunk.js → gu-json.ca345cd1.chunk.js} +1 -1
  111. package/build/{hi-json.0edb8d29.chunk.js → hi-json.50c7e6d4.chunk.js} +1 -1
  112. package/build/{hu-json.7855529a.chunk.js → hu-json.e0521dcc.chunk.js} +1 -1
  113. package/build/{id-json.df9618f2.chunk.js → id-json.4b1ff8d6.chunk.js} +1 -1
  114. package/build/index.html +1 -1
  115. package/build/{it-json.a21bf078.chunk.js → it-json.86bac220.chunk.js} +1 -1
  116. package/build/{ja-json.7b0d9067.chunk.js → ja-json.4e44e36b.chunk.js} +1 -1
  117. package/build/{ko-json.983c1f8f.chunk.js → ko-json.1003756e.chunk.js} +1 -1
  118. package/build/main.b47db1a3.js +9337 -0
  119. package/build/{ml-json.8dd021c8.chunk.js → ml-json.c7774425.chunk.js} +1 -1
  120. package/build/{ms-json.836ed013.chunk.js → ms-json.ed51e902.chunk.js} +1 -1
  121. package/build/{nl-json.29d2eb37.chunk.js → nl-json.f58ea235.chunk.js} +1 -1
  122. package/build/{pl-json.1f04f00c.chunk.js → pl-json.fed96aba.chunk.js} +1 -1
  123. package/build/{pt-BR-json.b4bc8efe.chunk.js → pt-BR-json.073799ab.chunk.js} +1 -1
  124. package/build/{pt-json.c23020ab.chunk.js → pt-json.3161ca22.chunk.js} +1 -1
  125. package/build/{ru-json.7ab40ccf.chunk.js → ru-json.7ad2cbbf.chunk.js} +1 -1
  126. package/build/{runtime~main.4204f341.js → runtime~main.feeac6d3.js} +1 -1
  127. package/build/{sa-json.c5a9f4ea.chunk.js → sa-json.f0f704f0.chunk.js} +1 -1
  128. package/build/{sk-json.e4c24c4e.chunk.js → sk-json.a848961b.chunk.js} +1 -1
  129. package/build/sso-settings-page.445184e0.chunk.js +1 -0
  130. package/build/{sv-json.c3f471ae.chunk.js → sv-json.b038acbe.chunk.js} +1 -1
  131. package/build/{th-json.a59ffb32.chunk.js → th-json.72e8de3d.chunk.js} +1 -1
  132. package/build/{tr-json.276e59fe.chunk.js → tr-json.9c44ea0c.chunk.js} +1 -1
  133. package/build/{uk-json.5b5b9c27.chunk.js → uk-json.c4cd2e24.chunk.js} +1 -1
  134. package/build/{vi-json.bf3424be.chunk.js → vi-json.f7890025.chunk.js} +1 -1
  135. package/build/{webhook-edit-page.9e46fc3f.chunk.js → webhook-edit-page.d2ea3351.chunk.js} +1 -1
  136. package/build/{zh-Hans-json.9c99f8d4.chunk.js → zh-Hans-json.03d2bda1.chunk.js} +1 -1
  137. package/build/{zh-json.451a0271.chunk.js → zh-json.3d0cc664.chunk.js} +1 -1
  138. package/package.json +7 -8
  139. package/server/bootstrap.js +1 -19
  140. package/server/config/admin-actions.js +0 -20
  141. package/server/content-types/api-token.js +1 -25
  142. package/server/content-types/index.js +0 -1
  143. package/server/controllers/api-token.js +1 -24
  144. package/server/controllers/index.js +0 -1
  145. package/server/routes/api-tokens.js +0 -11
  146. package/server/routes/index.js +0 -2
  147. package/server/services/api-token.js +29 -310
  148. package/server/services/constants.js +0 -10
  149. package/server/services/permission/engine-hooks.js +82 -0
  150. package/server/services/permission/engine.js +226 -36
  151. package/server/services/permission.js +1 -4
  152. package/server/strategies/admin.js +1 -7
  153. package/server/strategies/api-token.js +11 -71
  154. package/server/validation/api-tokens.js +2 -12
  155. package/admin/src/content-manager/components/SelectMany/ListItem.js +0 -102
  156. package/admin/src/content-manager/components/SelectMany/index.js +0 -148
  157. package/admin/src/content-manager/components/SelectOne/SingleValue.js +0 -67
  158. package/admin/src/content-manager/components/SelectOne/index.js +0 -97
  159. package/admin/src/content-manager/components/SelectWrapper/Label.js +0 -60
  160. package/admin/src/content-manager/components/SelectWrapper/index.js +0 -356
  161. package/admin/src/content-manager/pages/EditSettingsView/components/RelationalFieldButton.js +0 -135
  162. package/admin/src/content-manager/pages/EditSettingsView/components/RelationalFields.js +0 -103
  163. package/admin/src/content-manager/pages/EditView/Header/utils/getDraftRelations.js +0 -62
  164. package/admin/src/contexts/ApiTokenPermissions/index.js +0 -24
  165. package/admin/src/core/apis/CustomFields.js +0 -80
  166. package/admin/src/hooks/useRegenerate/index.js +0 -34
  167. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/ActionBoundRoutes/index.js +0 -56
  168. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/BoundRoute/getMethodColor.js +0 -41
  169. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/BoundRoute/index.js +0 -72
  170. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/CollapsableContentType/CheckBoxWrapper.js +0 -30
  171. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/CollapsableContentType/index.js +0 -150
  172. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/ContenTypesSection/index.js +0 -37
  173. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormApiTokenContainer/index.js +0 -254
  174. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormBody/index.js +0 -77
  175. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormHead/index.js +0 -85
  176. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/Permissions/index.js +0 -40
  177. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/Regenerate/index.js +0 -68
  178. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/init.js +0 -13
  179. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/reducer.js +0 -72
  180. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/getDateOfExpiration.js +0 -16
  181. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/index.js +0 -5
  182. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/transformPermissionsData.js +0 -36
  183. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/DefaultButton/index.js +0 -63
  184. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/ReadButton/index.js +0 -19
  185. package/build/1669.d1b29c28.chunk.js +0 -1
  186. package/build/4318.7d167b58.chunk.js +0 -30
  187. package/build/524.40377968.chunk.js +0 -644
  188. package/build/7379.d246dd38.chunk.js +0 -1
  189. package/build/admin-app.a61d5c2e.chunk.js +0 -112
  190. package/build/admin-edit-roles-page.4dd6bcb9.chunk.js +0 -1
  191. package/build/api-tokens-create-page.93dd0689.chunk.js +0 -1
  192. package/build/api-tokens-edit-page.b0adac81.chunk.js +0 -1
  193. package/build/api-tokens-list-page.bb36535f.chunk.js +0 -16
  194. package/build/content-manager.feb0d540.chunk.js +0 -1178
  195. package/build/content-type-builder-list-view.5b3cd768.chunk.js +0 -194
  196. package/build/content-type-builder-translation-en-json.f985c9c4.chunk.js +0 -1
  197. package/build/content-type-builder.a684b2e8.chunk.js +0 -145
  198. package/build/en-json.a9918c93.chunk.js +0 -1
  199. package/build/main.e4065f58.js +0 -9337
  200. package/build/sso-settings-page.9ceb0140.chunk.js +0 -1
  201. package/server/content-types/api-token-permission.js +0 -36
  202. package/server/controllers/content-api.js +0 -15
  203. 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 { customFields, Plugin } from './core/apis';
11
+ import { 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';
@@ -48,7 +48,6 @@ class StrapiApp {
48
48
  this.admin = {
49
49
  injectionZones,
50
50
  };
51
- this.customFields = customFields;
52
51
 
53
52
  this.menu = [];
54
53
  this.settings = {
@@ -282,7 +281,17 @@ class StrapiApp {
282
281
 
283
282
  async initialize() {
284
283
  Object.keys(this.appPlugins).forEach((plugin) => {
285
- this.appPlugins[plugin].register(this);
284
+ this.appPlugins[plugin].register({
285
+ addComponents: this.addComponents,
286
+ addCorePluginMenuLink: this.addCorePluginMenuLink,
287
+ addFields: this.addFields,
288
+ addMenuLink: this.addMenuLink,
289
+ addMiddlewares: this.addMiddlewares,
290
+ addReducers: this.addReducers,
291
+ createHook: this.createHook,
292
+ createSettingSection: this.createSettingSection,
293
+ registerPlugin: this.registerPlugin,
294
+ });
286
295
  });
287
296
  }
288
297
 
@@ -422,7 +431,6 @@ class StrapiApp {
422
431
  authLogo={this.configurations.authLogo}
423
432
  components={components}
424
433
  fields={fields}
425
- customFields={this.customFields}
426
434
  localeNames={localeNames}
427
435
  getAdminInjectedComponents={this.getAdminInjectedComponents}
428
436
  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, CustomFieldsProvider, StrapiAppProvider } from '@strapi/helper-plugin';
4
+ import { LibraryProvider, 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,7 +25,6 @@ const Providers = ({
25
25
  authLogo,
26
26
  children,
27
27
  components,
28
- customFields,
29
28
  fields,
30
29
  getAdminInjectedComponents,
31
30
  getPlugin,
@@ -65,17 +64,15 @@ const Providers = ({
65
64
  settings={settings}
66
65
  >
67
66
  <LibraryProvider components={components} fields={fields}>
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>
67
+ <LanguageProvider messages={messages} localeNames={localeNames}>
68
+ <AutoReloadOverlayBlockerProvider>
69
+ <OverlayBlocker>
70
+ <GuidedTour>
71
+ <Notifications>{children}</Notifications>
72
+ </GuidedTour>
73
+ </OverlayBlocker>
74
+ </AutoReloadOverlayBlockerProvider>
75
+ </LanguageProvider>
79
76
  </LibraryProvider>
80
77
  </StrapiAppProvider>
81
78
  </ConfigurationsProvider>
@@ -91,7 +88,6 @@ Providers.propTypes = {
91
88
  authLogo: PropTypes.oneOfType([PropTypes.string, PropTypes.any]).isRequired,
92
89
  children: PropTypes.element.isRequired,
93
90
  components: PropTypes.object.isRequired,
94
- customFields: PropTypes.object.isRequired,
95
91
  fields: PropTypes.object.isRequired,
96
92
  getAdminInjectedComponents: PropTypes.func.isRequired,
97
93
  getPlugin: PropTypes.func.isRequired,
@@ -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,6 +290,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
284
290
  rawQuery,
285
291
  toggleNotification,
286
292
  setCurrentStep,
293
+ queryClient,
287
294
  ]
288
295
  );
289
296
 
@@ -332,6 +339,9 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
332
339
  message: { id: getTrad('success.record.save') },
333
340
  });
334
341
 
342
+ // TODO: need to find a better place, or a better abstraction
343
+ queryClient.invalidateQueries(['relation']);
344
+
335
345
  dispatch(submitSucceeded(cleanReceivedData(data)));
336
346
 
337
347
  dispatch(setStatus('resolved'));
@@ -346,7 +356,7 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
346
356
  return Promise.reject(err);
347
357
  }
348
358
  },
349
- [cleanReceivedData, displayErrors, slug, id, dispatch, toggleNotification]
359
+ [cleanReceivedData, displayErrors, slug, id, dispatch, toggleNotification, queryClient]
350
360
  );
351
361
 
352
362
  const onUnpublish = useCallback(async () => {
@@ -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, queryInfos, name, rowId, value }) => {
31
+ const RelationMultiple = ({ fieldSchema, metadatas, name, entityId, value, contentType }) => {
32
32
  const { formatMessage } = useIntl();
33
33
  const { notifyStatus } = useNotifyAT();
34
- const requestURL = getRequestUrl(`${queryInfos.endPoint}/${rowId}/${name.split('.')[0]}`);
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, rowId],
60
- () => fetchRelation(requestURL, notify),
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
- rowId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
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, queryInfos, rowId }) => {
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
- rowId={rowId}
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;
@@ -18,6 +18,7 @@ import { getFullName } from '../../../../utils';
18
18
  const TableRows = ({
19
19
  canCreate,
20
20
  canDelete,
21
+ contentType,
21
22
  headers,
22
23
  entriesToDelete,
23
24
  onClickDelete,
@@ -88,6 +89,7 @@ const TableRows = ({
88
89
  <CellContent
89
90
  content={data[name.split('.')[0]]}
90
91
  name={name}
92
+ contentType={contentType}
91
93
  {...rest}
92
94
  rowId={data.id}
93
95
  />
@@ -180,6 +182,9 @@ TableRows.defaultProps = {
180
182
  TableRows.propTypes = {
181
183
  canCreate: PropTypes.bool,
182
184
  canDelete: PropTypes.bool,
185
+ contentType: PropTypes.shape({
186
+ uid: PropTypes.string.isRequired,
187
+ }).isRequired,
183
188
  entriesToDelete: PropTypes.array,
184
189
  headers: PropTypes.array.isRequired,
185
190
  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,
@@ -130,12 +130,17 @@ const EditViewDataManagerProvider = ({
130
130
 
131
131
  useEffect(() => {
132
132
  if (initialValues) {
133
+ const relationalFields = Object.keys(initialValues).filter(
134
+ (key) => currentContentTypeLayout?.attributes[key]?.type === 'relation'
135
+ );
136
+
133
137
  dispatch({
134
138
  type: 'INIT_FORM',
135
139
  initialValues,
140
+ relationalFields,
136
141
  });
137
142
  }
138
- }, [initialValues]);
143
+ }, [initialValues, currentContentTypeLayout]);
139
144
 
140
145
  const addComponentToDynamicZone = useCallback((keys, componentUid, shouldCheckErrors = false) => {
141
146
  trackUsageRef.current('didAddComponentToDynamicZone');
@@ -156,9 +161,18 @@ const EditViewDataManagerProvider = ({
156
161
  });
157
162
  }, []);
158
163
 
159
- const addRelation = useCallback(({ target: { name, value } }) => {
164
+ const connectRelation = useCallback(({ target: { name, value, replace } }) => {
165
+ dispatch({
166
+ type: 'CONNECT_RELATION',
167
+ keys: name.split('.'),
168
+ value,
169
+ replace,
170
+ });
171
+ }, []);
172
+
173
+ const loadRelation = useCallback(({ target: { name, value } }) => {
160
174
  dispatch({
161
- type: 'ADD_RELATION',
175
+ type: 'LOAD_RELATION',
162
176
  keys: name.split('.'),
163
177
  value,
164
178
  });
@@ -404,19 +418,11 @@ const EditViewDataManagerProvider = ({
404
418
  });
405
419
  }, []);
406
420
 
407
- const moveRelation = useCallback((dragIndex, overIndex, name) => {
421
+ const disconnectRelation = useCallback(({ target: { name, value } }) => {
408
422
  dispatch({
409
- type: 'MOVE_FIELD',
410
- dragIndex,
411
- overIndex,
423
+ type: 'DISCONNECT_RELATION',
412
424
  keys: name.split('.'),
413
- });
414
- }, []);
415
-
416
- const onRemoveRelation = useCallback((keys) => {
417
- dispatch({
418
- type: 'REMOVE_RELATION',
419
- keys,
425
+ value,
420
426
  });
421
427
  }, []);
422
428
 
@@ -470,7 +476,7 @@ const EditViewDataManagerProvider = ({
470
476
  value={{
471
477
  addComponentToDynamicZone,
472
478
  addNonRepeatableComponentToField,
473
- addRelation,
479
+ connectRelation,
474
480
  addRepeatableComponentToField,
475
481
  allLayoutData,
476
482
  checkFormErrors,
@@ -483,15 +489,15 @@ const EditViewDataManagerProvider = ({
483
489
  shouldNotRunValidations,
484
490
  status,
485
491
  layout: currentContentTypeLayout,
492
+ loadRelation,
486
493
  modifiedData,
487
494
  moveComponentDown,
488
495
  moveComponentField,
489
496
  moveComponentUp,
490
- moveRelation,
491
497
  onChange: handleChange,
492
498
  onPublish: handlePublish,
493
499
  onUnpublish,
494
- onRemoveRelation,
500
+ disconnectRelation,
495
501
  readActionAllowedFields,
496
502
  redirectToPreviousPage,
497
503
  removeComponentFromDynamicZone,
@@ -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
 
@@ -72,29 +73,140 @@ const reducer = (state, action) =>
72
73
 
73
74
  break;
74
75
  }
75
- case 'ADD_RELATION': {
76
- if (!Array.isArray(action.value) || !action.value.length) {
77
- break;
76
+ case 'LOAD_RELATION': {
77
+ const initialDataPath = ['initialData', ...action.keys, 'results'];
78
+ const modifiedDataPath = ['modifiedData', ...action.keys, 'results'];
79
+ const { value } = action;
80
+
81
+ set(draftState, initialDataPath, value);
82
+
83
+ /**
84
+ * We need to set the value also on modifiedData, because initialData
85
+ * and modifiedData need to stay in sync, so that the CM can compare
86
+ * both states, to render the dirty UI state
87
+ */
88
+
89
+ set(draftState, modifiedDataPath, value);
90
+
91
+ break;
92
+ }
93
+ case 'CONNECT_RELATION': {
94
+ const path = ['modifiedData', ...action.keys];
95
+ const { value, replace = false } = action;
96
+ const connectedRelations = get(state, [...path, 'connect']);
97
+ const disconnectedRelations = get(state, [...path, 'disconnect']);
98
+
99
+ if (!connectedRelations) {
100
+ set(draftState, [...path, 'connect'], []);
78
101
  }
79
102
 
80
- const el = action.value[0].value;
103
+ if (replace) {
104
+ set(draftState, [...path, 'connect'], [value]);
105
+ } else {
106
+ const nextValue = get(draftState, [...path, 'connect']);
107
+ nextValue.push(value);
108
+ }
81
109
 
82
- const currentValue = get(state, ['modifiedData', ...action.keys], null);
110
+ if (disconnectedRelations?.length) {
111
+ const existsInDisconnect = disconnectedRelations.find(
112
+ (disconnectValue) => disconnectValue.id === value.id
113
+ );
83
114
 
84
- if (!currentValue) {
85
- set(draftState, ['modifiedData', ...action.keys], [el]);
115
+ if (existsInDisconnect) {
116
+ const newDisconnectArray = pull([...disconnectedRelations], existsInDisconnect);
117
+ set(draftState, [...path, 'disconnect'], newDisconnectArray);
118
+ }
119
+ }
86
120
 
87
- break;
121
+ break;
122
+ }
123
+ case 'DISCONNECT_RELATION': {
124
+ const path = ['modifiedData', ...action.keys];
125
+ const { value } = action;
126
+ const disconnectedRelations = get(state, [...path, 'disconnect']);
127
+ const connectedRelations = get(state, [...path, 'connect']);
128
+
129
+ if (!disconnectedRelations) {
130
+ set(draftState, [...path, 'disconnect'], []);
88
131
  }
89
132
 
90
- set(draftState, ['modifiedData', ...action.keys], [...currentValue, el]);
133
+ const nextValue = get(draftState, [...path, 'disconnect']);
134
+ nextValue.push(value);
135
+
136
+ if (connectedRelations?.length) {
137
+ const existsInConnect = connectedRelations.find(
138
+ (connectValue) => connectValue.id === value.id
139
+ );
140
+
141
+ if (existsInConnect) {
142
+ const newConnectArray = pull([...connectedRelations], existsInConnect);
143
+ set(draftState, [...path, 'connect'], newConnectArray);
144
+ }
145
+ }
91
146
 
92
147
  break;
93
148
  }
94
149
  case 'INIT_FORM': {
150
+ const { initialValues, relationalFields = [] } = action;
151
+
95
152
  draftState.formErrors = {};
96
- draftState.initialData = action.initialValues;
97
- draftState.modifiedData = action.initialValues;
153
+
154
+ draftState.initialData = {
155
+ ...initialValues,
156
+
157
+ /**
158
+ * The state we keep in the client for relations looks like:
159
+ *
160
+ * {
161
+ * count: <int>
162
+ * results: [<Relation>]
163
+ * }
164
+ *
165
+ * The content API only returns { count: <int> }, which is why
166
+ * we need to extend the existing state rather than overwriting it.
167
+ */
168
+
169
+ ...relationalFields.reduce((acc, name) => {
170
+ acc[name] = {
171
+ ...(state.initialData?.[name] ?? {}),
172
+ ...(initialValues?.[name] ?? {}),
173
+ };
174
+
175
+ return acc;
176
+ }, {}),
177
+ };
178
+
179
+ draftState.modifiedData = {
180
+ ...initialValues,
181
+
182
+ /**
183
+ * The client sends the following to the content API:
184
+ *
185
+ * {
186
+ * connect: [<Relation>],
187
+ * disconnect: [<Relation>]
188
+ * }
189
+ *
190
+ * but receives only { count: <int> } in return. After save/ publish
191
+ * we have to:
192
+ *
193
+ * 1) reset the connect/ disconnect arrays
194
+ * 2) extend the existing state with the API response, so that `count`
195
+ * stays in sync
196
+ */
197
+
198
+ ...relationalFields.reduce((acc, name) => {
199
+ const { connect, disconnect, ...currentState } = state.modifiedData?.[name] ?? {};
200
+
201
+ acc[name] = {
202
+ ...(currentState ?? {}),
203
+ ...(initialValues?.[name] ?? {}),
204
+ };
205
+
206
+ return acc;
207
+ }, {}),
208
+ };
209
+
98
210
  draftState.modifiedDZName = null;
99
211
  draftState.shouldCheckErrors = false;
100
212
  break;
@@ -213,19 +325,6 @@ const reducer = (state, action) =>
213
325
 
214
326
  break;
215
327
  }
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
328
  case 'SET_DEFAULT_DATA_STRUCTURES': {
230
329
  draftState.componentsDataStructure = action.componentsDataStructure;
231
330
  draftState.contentTypeDataStructure = action.contentTypeDataStructure;
@@ -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));
@@ -1,7 +1,5 @@
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';
5
3
  import Date from '@strapi/icons/Date';
6
4
  import Boolean from '@strapi/icons/Boolean';
7
5
  import Email from '@strapi/icons/Email';
@@ -42,38 +40,10 @@ const iconByTypes = {
42
40
  dynamiczone: <DynamicZone />,
43
41
  };
44
42
 
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
- };
43
+ const FieldTypeIcon = ({ type }) => iconByTypes[type] || null;
73
44
 
74
45
  FieldTypeIcon.propTypes = {
75
46
  type: PropTypes.string.isRequired,
76
- customFieldUid: PropTypes.string,
77
47
  };
78
48
 
79
49
  export default FieldTypeIcon;