@strapi/admin 4.5.0-alpha.0 → 4.5.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 (317) hide show
  1. package/admin/src/StrapiApp.js +21 -18
  2. package/admin/src/assets/images/hot-air-balloon.png +0 -0
  3. package/admin/src/assets/images/icon_offline-cloud.svg +3 -3
  4. package/admin/src/assets/images/logo-strapi-2022.svg +7 -0
  5. package/admin/src/assets/images/upgrade-details.png +0 -0
  6. package/admin/src/components/Providers/index.js +14 -10
  7. package/admin/src/content-manager/components/CollectionTypeFormWrapper/index.js +24 -0
  8. package/admin/src/content-manager/components/DynamicTable/CellContent/CellValue.js +1 -1
  9. package/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/index.js +5 -4
  10. package/admin/src/content-manager/components/DynamicTable/CellContent/index.js +10 -0
  11. package/admin/src/content-manager/components/DynamicTable/TableRows/index.js +20 -15
  12. package/admin/src/content-manager/components/DynamicTable/index.js +21 -4
  13. package/admin/src/content-manager/components/DynamicZone/components/Component/index.js +19 -9
  14. package/admin/src/content-manager/components/DynamicZone/index.js +6 -2
  15. package/admin/src/content-manager/components/EditViewDataManagerProvider/index.js +153 -63
  16. package/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js +180 -132
  17. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/cleanData.js +70 -16
  18. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/findLeafByPathAndReplace.js +52 -0
  19. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/index.js +2 -0
  20. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/recursivelyFindPathsBasedOnCondition.js +72 -0
  21. package/admin/src/content-manager/components/FieldComponent/index.js +9 -2
  22. package/admin/src/content-manager/components/FieldTypeIcon/index.js +31 -1
  23. package/admin/src/content-manager/components/Inputs/index.js +36 -14
  24. package/admin/src/content-manager/components/NonRepeatableComponent/index.js +2 -0
  25. package/admin/src/content-manager/components/PreviewWysiwyg/index.js +1 -1
  26. package/admin/src/content-manager/components/RelationInput/RelationInput.js +163 -96
  27. package/admin/src/content-manager/components/RelationInput/components/RelationItem.js +2 -2
  28. package/admin/src/content-manager/components/RelationInput/constants.js +1 -1
  29. package/admin/src/content-manager/components/RelationInputDataManager/RelationInputDataManager.js +116 -73
  30. package/admin/src/content-manager/components/RelationInputDataManager/utils/diffRelations.js +24 -0
  31. package/admin/src/content-manager/components/RelationInputDataManager/utils/index.js +3 -1
  32. package/admin/src/content-manager/components/RelationInputDataManager/utils/normalizeRelations.js +17 -31
  33. package/admin/src/content-manager/components/RelationInputDataManager/utils/normalizeSearchResults.js +16 -0
  34. package/admin/src/content-manager/components/RelationInputDataManager/utils/select.js +35 -11
  35. package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/index.js +5 -0
  36. package/admin/src/content-manager/components/RepeatableComponent/index.js +4 -3
  37. package/admin/src/content-manager/components/SingleTypeFormWrapper/index.js +23 -0
  38. package/admin/src/content-manager/hooks/__test__/usePrev.test.js +26 -0
  39. package/admin/src/content-manager/hooks/index.js +1 -0
  40. package/admin/src/content-manager/hooks/useFetchContentTypeLayout/utils/formatLayouts.js +19 -48
  41. package/admin/src/content-manager/hooks/usePrev.js +14 -0
  42. package/admin/src/content-manager/hooks/useRelation/useRelation.js +116 -15
  43. package/admin/src/content-manager/pages/App/reducer.js +3 -0
  44. package/admin/src/content-manager/pages/EditSettingsView/components/FormModal.js +7 -2
  45. package/admin/src/content-manager/pages/EditSettingsView/index.js +2 -1
  46. package/admin/src/content-manager/pages/EditView/Header/index.js +118 -50
  47. package/admin/src/content-manager/pages/EditView/Header/utils/select.js +4 -0
  48. package/admin/src/content-manager/pages/EditView/index.js +102 -93
  49. package/admin/src/content-manager/pages/ListSettingsView/components/DraggableCard.js +3 -3
  50. package/admin/src/content-manager/pages/ListSettingsView/components/Settings.js +2 -2
  51. package/admin/src/content-manager/pages/ListSettingsView/components/SortDisplayedFields.js +1 -1
  52. package/admin/src/content-manager/pages/ListView/index.js +24 -15
  53. package/admin/src/content-manager/pages/ListView/utils/buildQueryString.js +14 -2
  54. package/admin/src/contexts/ApiTokenPermissions/index.js +24 -0
  55. package/admin/src/core/apis/CustomFields.js +79 -0
  56. package/admin/src/core/apis/index.js +1 -0
  57. package/admin/src/core/store/configureStore.js +17 -2
  58. package/admin/src/favicon.png +0 -0
  59. package/admin/src/hooks/index.js +1 -0
  60. package/admin/src/hooks/useFetchMarketplacePlugins/index.js +2 -2
  61. package/admin/src/hooks/useFetchMarketplacePlugins/utils/api.js +4 -2
  62. package/admin/src/hooks/useFetchMarketplaceProviders/index.js +3 -3
  63. package/admin/src/hooks/useFetchMarketplaceProviders/utils/api.js +5 -3
  64. package/admin/src/hooks/useRegenerate/index.js +34 -0
  65. package/admin/src/index.js +1 -0
  66. package/admin/src/pages/App/index.js +1 -1
  67. package/admin/src/pages/HomePage/SocialLinks.js +1 -1
  68. package/admin/src/pages/HomePage/assets/corner-ornament.svg +48 -0
  69. package/admin/src/pages/HomePage/index.js +3 -2
  70. package/admin/src/pages/MarketplacePage/components/NpmPackageCard/CardButton.js +110 -0
  71. package/admin/src/pages/MarketplacePage/components/NpmPackageCard/InstallPluginButton.js +32 -21
  72. package/admin/src/pages/MarketplacePage/components/NpmPackageCard/PackageStats.js +79 -0
  73. package/admin/src/pages/MarketplacePage/components/NpmPackageCard/index.js +28 -11
  74. package/admin/src/pages/MarketplacePage/components/NpmPackagesFilters/FilterSelect.js +41 -0
  75. package/admin/src/pages/MarketplacePage/components/NpmPackagesFilters/FiltersPopover.js +96 -0
  76. package/admin/src/pages/MarketplacePage/components/NpmPackagesFilters/index.js +106 -0
  77. package/admin/src/pages/MarketplacePage/components/NpmPackagesGrid/index.js +4 -0
  78. package/admin/src/pages/MarketplacePage/components/SortSelect/index.js +70 -0
  79. package/admin/src/pages/MarketplacePage/index.js +68 -8
  80. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/ActionBoundRoutes/index.js +56 -0
  81. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/BoundRoute/getMethodColor.js +41 -0
  82. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/BoundRoute/index.js +72 -0
  83. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/CollapsableContentType/CheckBoxWrapper.js +30 -0
  84. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/CollapsableContentType/index.js +150 -0
  85. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/ContenTypesSection/index.js +37 -0
  86. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormApiTokenContainer/index.js +255 -0
  87. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormBody/index.js +78 -0
  88. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormHead/index.js +89 -0
  89. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/Permissions/index.js +40 -0
  90. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/Regenerate/index.js +68 -0
  91. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js +216 -197
  92. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/init.js +13 -0
  93. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/reducer.js +72 -0
  94. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/getDateOfExpiration.js +16 -0
  95. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/index.js +5 -0
  96. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/schema.js +2 -1
  97. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/transformPermissionsData.js +36 -0
  98. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/DefaultButton/index.js +63 -0
  99. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/DeleteButton/index.js +1 -0
  100. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/ReadButton/index.js +19 -0
  101. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/UpdateButton/index.js +3 -36
  102. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/index.js +13 -11
  103. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/index.js +3 -2
  104. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/utils/tableHeaders.js +8 -8
  105. package/admin/src/pages/SettingsPage/pages/ApiTokens/ProtectedEditView/index.js +1 -1
  106. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/index.js +7 -38
  107. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/utils/options.js +31 -0
  108. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/index.js +32 -43
  109. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/Collapse/index.js +1 -1
  110. package/admin/src/pages/SettingsPage/pages/Roles/ListPage/components/RoleRow/index.js +3 -1
  111. package/admin/src/pages/SettingsPage/pages/Roles/ListPage/index.js +2 -1
  112. package/admin/src/pages/SettingsPage/pages/Users/ListPage/ModalForm/index.js +2 -2
  113. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/EventInput/index.js +2 -2
  114. package/admin/src/permissions/defaultPermissions.js +2 -6
  115. package/admin/src/translations/ca.json +4 -2
  116. package/admin/src/translations/de.json +5 -2
  117. package/admin/src/translations/dk.json +4 -1
  118. package/admin/src/translations/en.json +38 -3
  119. package/admin/src/translations/es.json +157 -158
  120. package/admin/src/translations/fr.json +4 -1
  121. package/admin/src/translations/gu.json +608 -606
  122. package/admin/src/translations/he.json +1 -1
  123. package/admin/src/translations/hi.json +689 -687
  124. package/admin/src/translations/hu.json +3 -1
  125. package/admin/src/translations/id.json +3 -1
  126. package/admin/src/translations/it.json +3 -1
  127. package/admin/src/translations/ja.json +3 -1
  128. package/admin/src/translations/ko.json +3 -1
  129. package/admin/src/translations/ml.json +689 -687
  130. package/admin/src/translations/nl.json +4 -1
  131. package/admin/src/translations/no.json +1 -1
  132. package/admin/src/translations/pl.json +3 -1
  133. package/admin/src/translations/pt-BR.json +4 -1
  134. package/admin/src/translations/ru.json +489 -491
  135. package/admin/src/translations/sa.json +86 -83
  136. package/admin/src/translations/sk.json +4 -1
  137. package/admin/src/translations/sv.json +4 -1
  138. package/admin/src/translations/th.json +1 -1
  139. package/admin/src/translations/zh-Hans.json +5 -2
  140. package/admin/src/translations/zh.json +4 -1
  141. package/build/1856.172d5fa0.chunk.js +174 -0
  142. package/build/2077.058590f4.chunk.js +206 -0
  143. package/build/2912.2c42c07b.chunk.js +259 -0
  144. package/build/4318.5e670740.chunk.js +30 -0
  145. package/build/4715.22747b59.chunk.js +387 -0
  146. package/build/{4800.d09f1225.chunk.js → 4800.a6935af6.chunk.js} +1 -1
  147. package/build/4982.1b75ddb1.chunk.js +325 -0
  148. package/build/617f9c948fa79e6d73bd.png +0 -0
  149. package/build/6d21938306785f176538.png +0 -0
  150. package/build/70674f63fc3904c20de0.svg +7 -0
  151. package/build/7379.d246dd38.chunk.js +1 -0
  152. package/build/7692.a36fb2c2.chunk.js +470 -0
  153. package/build/7841.c50e9509.chunk.js +259 -0
  154. package/build/7866.ba215f99.chunk.js +505 -0
  155. package/build/7e9af4fb7e723fcebf1f.svg +48 -0
  156. package/build/8380.e53e7207.chunk.js +299 -0
  157. package/build/8549.832ed79d.chunk.js +159 -0
  158. package/build/8738.0fe8a61e.chunk.js +463 -0
  159. package/build/{9066.08049eb1.chunk.js → 9066.eaf76ff3.chunk.js} +5 -5
  160. package/build/{9166.037339e0.chunk.js → 9166.90876521.chunk.js} +16 -15
  161. package/build/{9420.43a86e7c.chunk.js → 9420.5292d1d2.chunk.js} +38 -37
  162. package/build/9649.468667d9.chunk.js +199 -0
  163. package/build/9d5d788027e86620c234.svg +5 -0
  164. package/build/{Admin-authenticatedApp.e39f36c9.chunk.js → Admin-authenticatedApp.c4f68103.chunk.js} +3 -3
  165. package/build/{Admin_homePage.118926e0.chunk.js → Admin_homePage.26d32e30.chunk.js} +6 -5
  166. package/build/Admin_marketplace.32375885.chunk.js +22 -0
  167. package/build/{Admin_profilePage.9d50ac44.chunk.js → Admin_profilePage.da32abbc.chunk.js} +1 -1
  168. package/build/Admin_settingsPage.bf2234e1.chunk.js +178 -0
  169. package/build/admin-app.9049056c.chunk.js +112 -0
  170. package/build/admin-edit-roles-page.69d9fcb2.chunk.js +1 -0
  171. package/build/{admin-users.97a08630.chunk.js → admin-users.d71f198a.chunk.js} +3 -3
  172. package/build/api-tokens-create-page.93dd0689.chunk.js +1 -0
  173. package/build/api-tokens-edit-page.b0adac81.chunk.js +1 -0
  174. package/build/api-tokens-list-page.bb36535f.chunk.js +16 -0
  175. package/build/ca-json.07ae0f2c.chunk.js +1 -0
  176. package/build/content-manager.ff998bed.chunk.js +1204 -0
  177. package/build/content-type-builder-list-view.5b3cd768.chunk.js +194 -0
  178. package/build/content-type-builder-translation-en-json.f985c9c4.chunk.js +1 -0
  179. package/build/content-type-builder-translation-sv-json.6deff030.chunk.js +1 -0
  180. package/build/content-type-builder.16af63a6.chunk.js +145 -0
  181. package/build/de-json.6b3e1894.chunk.js +1 -0
  182. package/build/dk-json.144c6a8e.chunk.js +1 -0
  183. package/build/{email-settings-page.64037147.chunk.js → email-settings-page.c3469093.chunk.js} +6 -6
  184. package/build/en-json.4a269f6b.chunk.js +1 -0
  185. package/build/es-json.6d123a82.chunk.js +1 -0
  186. package/build/fr-json.28ab54cb.chunk.js +1 -0
  187. package/build/gu-json.9a50ea64.chunk.js +1 -0
  188. package/build/he-json.72f18790.chunk.js +1 -0
  189. package/build/hi-json.0301b7ba.chunk.js +1 -0
  190. package/build/hu-json.c4b641bb.chunk.js +1 -0
  191. package/build/{i18n-settings-page.0b73785d.chunk.js → i18n-settings-page.46d894ff.chunk.js} +5 -5
  192. package/build/id-json.86035797.chunk.js +1 -0
  193. package/build/index.html +1 -1
  194. package/build/it-json.bbdc8993.chunk.js +1 -0
  195. package/build/ja-json.1c9eeeec.chunk.js +1 -0
  196. package/build/ko-json.e1f66398.chunk.js +1 -0
  197. package/build/main.91328e7a.js +9381 -0
  198. package/build/ml-json.963c889f.chunk.js +1 -0
  199. package/build/nl-json.2b8cc3a0.chunk.js +1 -0
  200. package/build/no-json.a58c28bd.chunk.js +1 -0
  201. package/build/pl-json.249626b3.chunk.js +1 -0
  202. package/build/pt-BR-json.7852f808.chunk.js +1 -0
  203. package/build/ru-json.d7cfc2ff.chunk.js +1 -0
  204. package/build/runtime~main.c9c319c0.js +2 -0
  205. package/build/sa-json.44e95991.chunk.js +1 -0
  206. package/build/sk-json.7ba4b330.chunk.js +1 -0
  207. package/build/sso-settings-page.9ceb0140.chunk.js +1 -0
  208. package/build/sv-json.8e5a7911.chunk.js +1 -0
  209. package/build/th-json.a67309b1.chunk.js +1 -0
  210. package/build/{upload-settings.80ff0974.chunk.js → upload-settings.53b690f3.chunk.js} +5 -5
  211. package/build/{upload-translation-en-json.004a86c1.chunk.js → upload-translation-en-json.86da7b0a.chunk.js} +1 -1
  212. package/build/{users-advanced-settings-page.a02f4806.chunk.js → users-advanced-settings-page.3f4ee86e.chunk.js} +5 -5
  213. package/build/{webhook-edit-page.d2ea3351.chunk.js → webhook-edit-page.dc9442ce.chunk.js} +2 -2
  214. package/build/webhook-list-page.02191138.chunk.js +134 -0
  215. package/build/zh-Hans-json.21617c24.chunk.js +1 -0
  216. package/build/zh-json.608aaf24.chunk.js +1 -0
  217. package/ee/admin/pages/SettingsPage/pages/Roles/ListPage/index.js +3 -2
  218. package/ee/server/controllers/user.js +5 -3
  219. package/env.js +1 -0
  220. package/package.json +13 -11
  221. package/scripts/build.js +11 -0
  222. package/server/bootstrap.js +19 -1
  223. package/server/config/admin-actions.js +20 -0
  224. package/server/content-types/api-token-permission.js +36 -0
  225. package/server/content-types/api-token.js +25 -1
  226. package/server/content-types/index.js +1 -0
  227. package/server/controllers/admin.js +3 -0
  228. package/server/controllers/api-token.js +24 -1
  229. package/server/controllers/content-api.js +15 -0
  230. package/server/controllers/index.js +1 -0
  231. package/server/controllers/user.js +3 -2
  232. package/server/routes/api-tokens.js +11 -0
  233. package/server/routes/content-api.js +20 -0
  234. package/server/routes/index.js +2 -0
  235. package/server/services/api-token.js +309 -29
  236. package/server/services/constants.js +10 -0
  237. package/server/services/permission/engine.js +36 -226
  238. package/server/services/permission.js +4 -1
  239. package/server/strategies/admin.js +7 -1
  240. package/server/strategies/api-token.js +72 -11
  241. package/server/validation/api-tokens.js +12 -2
  242. package/utils/create-plugins-exclude-path.js +40 -0
  243. package/utils/get-custom-app-config-file.js +5 -0
  244. package/webpack.alias.js +0 -13
  245. package/webpack.config.js +4 -1
  246. package/admin/src/assets/images/banner_strapi-rocket.png +0 -0
  247. package/admin/src/assets/images/big-logo-home.png +0 -0
  248. package/admin/src/assets/images/homepage-logo.png +0 -0
  249. package/admin/src/assets/images/icon_made-by-strapi.svg +0 -5
  250. package/admin/src/assets/images/logo_strapi_auth.png +0 -0
  251. package/admin/src/assets/images/logo_strapi_auth_v4.png +0 -0
  252. package/admin/src/assets/images/logo_strapi_menu.png +0 -0
  253. package/admin/src/assets/images/oops.png +0 -0
  254. package/admin/src/content-manager/components/State/index.js +0 -37
  255. package/admin/src/favicon.ico +0 -0
  256. package/build/15026a3d58aeb2828134.png +0 -0
  257. package/build/1856.47226450.chunk.js +0 -173
  258. package/build/2077.c935ee42.chunk.js +0 -205
  259. package/build/2912.a015078a.chunk.js +0 -258
  260. package/build/4715.58cd558f.chunk.js +0 -387
  261. package/build/4982.05eda880.chunk.js +0 -324
  262. package/build/7098.40dcd7bf.chunk.js +0 -1
  263. package/build/7841.91f793dc.chunk.js +0 -258
  264. package/build/7866.1201afbd.chunk.js +0 -504
  265. package/build/8380.8789ff76.chunk.js +0 -284
  266. package/build/8549.133c4473.chunk.js +0 -158
  267. package/build/8851.e4ac62f2.chunk.js +0 -158
  268. package/build/90f49a385afb000fb1d4.svg +0 -5
  269. package/build/9311.7cc03f29.chunk.js +0 -508
  270. package/build/Admin_marketplace.82c0570b.chunk.js +0 -11
  271. package/build/Admin_settingsPage.98a711e5.chunk.js +0 -178
  272. package/build/a6b842e0b6d2b61135d1.svg +0 -5
  273. package/build/admin-app.4f7618a9.chunk.js +0 -112
  274. package/build/admin-edit-roles-page.554ba3fa.chunk.js +0 -1
  275. package/build/api-tokens-create-page.4c262d6e.chunk.js +0 -1
  276. package/build/api-tokens-edit-page.10a9d368.chunk.js +0 -1
  277. package/build/api-tokens-list-page.442c9f3c.chunk.js +0 -15
  278. package/build/b997a22a2e0b87ef1fa2.ico +0 -0
  279. package/build/bd81ba6c07827282255d.png +0 -0
  280. package/build/c3de6118ef47086ad05c.png +0 -0
  281. package/build/ca-json.a16899ae.chunk.js +0 -1
  282. package/build/content-manager.7d57c9d1.chunk.js +0 -1200
  283. package/build/content-type-builder-list-view.8cc534e0.chunk.js +0 -194
  284. package/build/content-type-builder-translation-en-json.201bfb78.chunk.js +0 -1
  285. package/build/content-type-builder.684df7a4.chunk.js +0 -142
  286. package/build/de-json.aa6026b3.chunk.js +0 -1
  287. package/build/dk-json.fac2bcfb.chunk.js +0 -1
  288. package/build/en-json.0c69c7d7.chunk.js +0 -1
  289. package/build/es-json.d672e181.chunk.js +0 -1
  290. package/build/fb376b132d18bf4522ca.png +0 -0
  291. package/build/fde9b1ad0670d29a2516.png +0 -0
  292. package/build/fr-json.71a16175.chunk.js +0 -1
  293. package/build/gu-json.ca345cd1.chunk.js +0 -1
  294. package/build/he-json.3b825d80.chunk.js +0 -1
  295. package/build/hi-json.50c7e6d4.chunk.js +0 -1
  296. package/build/hu-json.e0521dcc.chunk.js +0 -1
  297. package/build/id-json.4b1ff8d6.chunk.js +0 -1
  298. package/build/it-json.86bac220.chunk.js +0 -1
  299. package/build/ja-json.4e44e36b.chunk.js +0 -1
  300. package/build/ko-json.1003756e.chunk.js +0 -1
  301. package/build/main.b47db1a3.js +0 -9337
  302. package/build/ml-json.c7774425.chunk.js +0 -1
  303. package/build/nl-json.f58ea235.chunk.js +0 -1
  304. package/build/no-json.40386397.chunk.js +0 -1
  305. package/build/pl-json.fed96aba.chunk.js +0 -1
  306. package/build/pt-BR-json.073799ab.chunk.js +0 -1
  307. package/build/ru-json.7ad2cbbf.chunk.js +0 -1
  308. package/build/runtime~main.feeac6d3.js +0 -2
  309. package/build/sa-json.f0f704f0.chunk.js +0 -1
  310. package/build/sk-json.a848961b.chunk.js +0 -1
  311. package/build/sso-settings-page.445184e0.chunk.js +0 -1
  312. package/build/sv-json.b038acbe.chunk.js +0 -1
  313. package/build/th-json.72e8de3d.chunk.js +0 -1
  314. package/build/webhook-list-page.2775a683.chunk.js +0 -134
  315. package/build/zh-Hans-json.03d2bda1.chunk.js +0 -1
  316. package/build/zh-json.3d0cc664.chunk.js +0 -1
  317. package/server/services/permission/engine-hooks.js +0 -82
@@ -1,16 +1,25 @@
1
+ /* eslint-disable no-nested-ternary */
1
2
  import PropTypes from 'prop-types';
2
- import React, { memo, useEffect, useMemo } from 'react';
3
+ import React, { memo, useMemo } from 'react';
3
4
  import { useIntl } from 'react-intl';
5
+ import get from 'lodash/get';
6
+ import pick from 'lodash/pick';
4
7
 
5
- import { useCMEditViewDataManager, NotAllowedInput, useQueryParams } from '@strapi/helper-plugin';
8
+ import { useCMEditViewDataManager, NotAllowedInput } from '@strapi/helper-plugin';
6
9
 
7
10
  import { RelationInput } from '../RelationInput';
11
+
8
12
  import { useRelation } from '../../hooks/useRelation';
9
- import { connect, select, normalizeRelations } from './utils';
10
- import { PUBLICATION_STATES, RELATIONS_TO_DISPLAY, SEARCH_RESULTS_TO_DISPLAY } from './constants';
13
+
11
14
  import { getTrad } from '../../utils';
12
15
 
13
- export const RelationInputDataManger = ({
16
+ import { PUBLICATION_STATES, RELATIONS_TO_DISPLAY, SEARCH_RESULTS_TO_DISPLAY } from './constants';
17
+ import { connect, select, normalizeSearchResults, diffRelations, normalizeRelation } from './utils';
18
+
19
+ export const RelationInputDataManager = ({
20
+ error,
21
+ componentId,
22
+ isComponentRelation,
14
23
  editable,
15
24
  description,
16
25
  intlLabel,
@@ -30,61 +39,42 @@ export const RelationInputDataManger = ({
30
39
  const { formatMessage } = useIntl();
31
40
  const { connectRelation, disconnectRelation, loadRelation, modifiedData, slug, initialData } =
32
41
  useCMEditViewDataManager();
33
- const [{ query }] = useQueryParams();
42
+
43
+ const relationsFromModifiedData = get(modifiedData, name) ?? [];
44
+
45
+ const currentLastPage = Math.ceil(get(initialData, name, []).length / RELATIONS_TO_DISPLAY);
34
46
 
35
47
  const { relations, search, searchFor } = useRelation(`${slug}-${name}-${initialData?.id ?? ''}`, {
48
+ name,
36
49
  relation: {
37
- enabled: initialData[name]?.count !== 0 && !!endpoints.relation,
50
+ enabled: !!endpoints.relation,
38
51
  endpoint: endpoints.relation,
52
+ pageGoal: currentLastPage,
39
53
  pageParams: {
40
54
  ...defaultParams,
41
- locale: query?.plugins?.i18n?.locale,
42
55
  pageSize: RELATIONS_TO_DISPLAY,
43
56
  },
57
+ onLoad: loadRelation,
58
+ normalizeArguments: {
59
+ mainFieldName: mainField.name,
60
+ shouldAddLink: shouldDisplayRelationLink,
61
+ targetModel,
62
+ },
44
63
  },
45
64
 
46
65
  search: {
47
66
  endpoint: endpoints.search,
48
67
  pageParams: {
49
68
  ...defaultParams,
50
- entityId: isCreatingEntry ? undefined : initialData.id,
51
- locale: query?.plugins?.i18n?.locale,
69
+ // eslint-disable-next-line no-nested-ternary
70
+ entityId: isCreatingEntry ? undefined : isComponentRelation ? componentId : initialData.id,
52
71
  pageSize: SEARCH_RESULTS_TO_DISPLAY,
53
72
  },
54
73
  },
55
74
  });
56
75
 
57
- const stringifiedRelations = JSON.stringify(relations);
58
- const normalizedRelations = useMemo(
59
- () =>
60
- normalizeRelations(relations, {
61
- modifiedData: modifiedData?.[name],
62
- mainFieldName: mainField.name,
63
- shouldAddLink: shouldDisplayRelationLink,
64
- targetModel,
65
- }),
66
- // eslint-disable-next-line react-hooks/exhaustive-deps
67
- [
68
- stringifiedRelations,
69
- modifiedData,
70
- name,
71
- mainField.name,
72
- shouldDisplayRelationLink,
73
- targetModel,
74
- ]
75
- );
76
-
77
- useEffect(() => {
78
- if (relations.status === 'success') {
79
- loadRelation({
80
- target: { name, value: normalizedRelations.data.pages.flat() },
81
- });
82
- }
83
- // eslint-disable-next-line react-hooks/exhaustive-deps
84
- }, [loadRelation, relations.status, stringifiedRelations, name]);
85
-
86
76
  const isMorph = useMemo(() => relationType.toLowerCase().includes('morph'), [relationType]);
87
- const isSingleRelation = [
77
+ const toOneRelation = [
88
78
  'oneWay',
89
79
  'oneToOne',
90
80
  'manyToOne',
@@ -104,24 +94,37 @@ export const RelationInputDataManger = ({
104
94
  return !editable;
105
95
  }, [isMorph, isCreatingEntry, editable, isFieldAllowed, isFieldReadable]);
106
96
 
107
- const handleRelationAdd = (relation) => {
108
- connectRelation({ target: { name, value: relation, replace: isSingleRelation } });
97
+ const handleRelationConnect = (relation) => {
98
+ /**
99
+ * Any relation being added to the store should be normalized so it has it's link.
100
+ */
101
+ const normalizedRelation = normalizeRelation(relation, {
102
+ mainFieldName: mainField.name,
103
+ shouldAddLink: shouldDisplayRelationLink,
104
+ targetModel,
105
+ });
106
+
107
+ connectRelation({ name, value: normalizedRelation, toOneRelation });
109
108
  };
110
109
 
111
- const handleRelationRemove = (relation) => {
112
- disconnectRelation({ target: { name, value: relation } });
110
+ const handleRelationDisconnect = (relation) => {
111
+ disconnectRelation({ name, id: relation.id });
113
112
  };
114
113
 
115
114
  const handleRelationLoadMore = () => {
116
115
  relations.fetchNextPage();
117
116
  };
118
117
 
119
- const handleSearch = (term) => {
120
- searchFor(term, { idsToOmit: modifiedData?.[name]?.connect?.map((relation) => relation.id) });
121
- };
118
+ const handleSearch = (term = '') => {
119
+ const [connected, disconnected] = diffRelations(
120
+ relationsFromModifiedData,
121
+ get(initialData, name)
122
+ );
122
123
 
123
- const handleOpenSearch = () => {
124
- searchFor('', { idsToOmit: modifiedData?.[name]?.connect?.map((relation) => relation.id) });
124
+ searchFor(term, {
125
+ idsToInclude: disconnected,
126
+ idsToOmit: connected,
127
+ });
125
128
  };
126
129
 
127
130
  const handleSearchMore = () => {
@@ -132,43 +135,70 @@ export const RelationInputDataManger = ({
132
135
  (!isFieldAllowed && isCreatingEntry) ||
133
136
  (!isCreatingEntry && !isFieldAllowed && !isFieldReadable)
134
137
  ) {
135
- return <NotAllowedInput intlLabel={intlLabel} labelAction={labelAction} />;
138
+ return <NotAllowedInput name={name} intlLabel={intlLabel} labelAction={labelAction} />;
136
139
  }
137
140
 
141
+ /**
142
+ * How to calculate the total number of relations even if you don't
143
+ * have them all loaded in the browser.
144
+ *
145
+ * 1. The `infiniteQuery` gives you the total number of relations in the pagination result.
146
+ * 2. You can diff the length of the browserState vs the fetchedServerState to determine if you've
147
+ * either added or removed relations.
148
+ * 3. Add them together, if you've removed relations you'll get a negative number and it'll
149
+ * actually subtract from the total number on the server (regardless of how many you fetched).
150
+ */
151
+ const browserRelationsCount = relationsFromModifiedData.length;
152
+ const serverRelationsCount = (get(initialData, name) ?? []).length;
153
+ const realServerRelationsCount = relations.data?.pages[0]?.pagination?.total ?? 0;
154
+ /**
155
+ * _IF_ theres no relations data and the browserCount is the same as serverCount you can therefore assume
156
+ * that the browser count is correct because we've just _made_ this entry and the in-component hook is now fetching.
157
+ */
158
+ const totalRelations =
159
+ !relations.data && browserRelationsCount === serverRelationsCount
160
+ ? browserRelationsCount
161
+ : browserRelationsCount - serverRelationsCount + realServerRelationsCount;
162
+
138
163
  return (
139
164
  <RelationInput
165
+ error={error}
140
166
  description={description}
141
167
  disabled={isDisabled}
142
168
  id={name}
143
169
  label={`${formatMessage({
144
170
  id: intlLabel.id,
145
171
  defaultMessage: intlLabel.defaultMessage,
146
- })} ${initialData[name]?.count !== undefined ? `(${initialData[name].count})` : ''}`}
172
+ })} ${totalRelations > 0 ? `(${totalRelations})` : ''}`}
147
173
  labelAction={labelAction}
148
174
  labelLoadMore={
149
- // TODO: only display if there are more; derive from count
150
- !isCreatingEntry &&
151
- formatMessage({
152
- id: getTrad('relation.loadMore'),
153
- defaultMessage: 'Load More',
154
- })
175
+ !isCreatingEntry
176
+ ? formatMessage({
177
+ id: getTrad('relation.loadMore'),
178
+ defaultMessage: 'Load More',
179
+ })
180
+ : null
155
181
  }
182
+ labelDisconnectRelation={formatMessage({
183
+ id: getTrad('relation.disconnect'),
184
+ defaultMessage: 'Remove',
185
+ })}
156
186
  listHeight={320}
157
- loadingMessage={() =>
158
- formatMessage({
159
- id: getTrad('relation.isLoading'),
160
- defaultMessage: 'Relations are loading',
161
- })
162
- }
187
+ loadingMessage={formatMessage({
188
+ id: getTrad('relation.isLoading'),
189
+ defaultMessage: 'Relations are loading',
190
+ })}
163
191
  name={name}
192
+ noRelationsMessage={formatMessage({
193
+ id: getTrad('relation.notAvailable'),
194
+ defaultMessage: 'No relations available',
195
+ })}
164
196
  numberOfRelationsToDisplay={RELATIONS_TO_DISPLAY}
165
- onRelationAdd={(relation) => handleRelationAdd(relation)}
166
- onRelationRemove={(relation) => handleRelationRemove(relation)}
197
+ onRelationConnect={(relation) => handleRelationConnect(relation)}
198
+ onRelationDisconnect={(relation) => handleRelationDisconnect(relation)}
167
199
  onRelationLoadMore={() => handleRelationLoadMore()}
168
200
  onSearch={(term) => handleSearch(term)}
169
201
  onSearchNextPage={() => handleSearchMore()}
170
- onSearchClose={() => {}}
171
- onSearchOpen={handleOpenSearch}
172
202
  placeholder={formatMessage(
173
203
  placeholder || {
174
204
  id: getTrad('relation.add'),
@@ -186,9 +216,16 @@ export const RelationInputDataManger = ({
186
216
  defaultMessage: 'Published',
187
217
  }),
188
218
  }}
189
- relations={normalizedRelations}
219
+ relations={pick(
220
+ { ...relations, data: relationsFromModifiedData },
221
+ 'data',
222
+ 'hasNextPage',
223
+ 'isFetchingNextPage',
224
+ 'isLoading',
225
+ 'isSuccess'
226
+ )}
190
227
  required={required}
191
- searchResults={normalizeRelations(search, {
228
+ searchResults={normalizeSearchResults(search, {
192
229
  mainFieldName: mainField.name,
193
230
  })}
194
231
  size={size}
@@ -196,17 +233,22 @@ export const RelationInputDataManger = ({
196
233
  );
197
234
  };
198
235
 
199
- RelationInputDataManger.defaultProps = {
236
+ RelationInputDataManager.defaultProps = {
237
+ componentId: undefined,
200
238
  editable: true,
239
+ error: undefined,
201
240
  description: '',
202
241
  labelAction: null,
242
+ isComponentRelation: false,
203
243
  isFieldAllowed: true,
204
244
  placeholder: null,
205
245
  required: false,
206
246
  };
207
247
 
208
- RelationInputDataManger.propTypes = {
248
+ RelationInputDataManager.propTypes = {
249
+ componentId: PropTypes.number,
209
250
  editable: PropTypes.bool,
251
+ error: PropTypes.string,
210
252
  description: PropTypes.string,
211
253
  intlLabel: PropTypes.shape({
212
254
  id: PropTypes.string.isRequired,
@@ -215,6 +257,7 @@ RelationInputDataManger.propTypes = {
215
257
  }).isRequired,
216
258
  labelAction: PropTypes.element,
217
259
  isCreatingEntry: PropTypes.bool.isRequired,
260
+ isComponentRelation: PropTypes.bool,
218
261
  isFieldAllowed: PropTypes.bool,
219
262
  isFieldReadable: PropTypes.bool.isRequired,
220
263
  mainField: PropTypes.shape({
@@ -235,7 +278,7 @@ RelationInputDataManger.propTypes = {
235
278
  targetModel: PropTypes.string.isRequired,
236
279
  queryInfos: PropTypes.shape({
237
280
  defaultParams: PropTypes.shape({
238
- _component: PropTypes.string,
281
+ locale: PropTypes.string,
239
282
  }),
240
283
  endpoints: PropTypes.shape({
241
284
  relation: PropTypes.string,
@@ -245,6 +288,6 @@ RelationInputDataManger.propTypes = {
245
288
  }).isRequired,
246
289
  };
247
290
 
248
- const Memoized = memo(RelationInputDataManger);
291
+ const Memoized = memo(RelationInputDataManager);
249
292
 
250
293
  export default connect(Memoized, select);
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @param {Array<{id: string}>} browserStateRelations
3
+ * @param {Array<{id: string}>} serverStateRelations
4
+ * @returns {[connected: string[], disconnected: string[]]} – the connected and disconnected relations ids
5
+ */
6
+ export const diffRelations = (browserStateRelations = [], serverStateRelations = []) => {
7
+ const connected = browserStateRelations.reduce((acc, relation) => {
8
+ if (!serverStateRelations.find((oldRelation) => oldRelation.id === relation.id)) {
9
+ return [...acc, relation.id];
10
+ }
11
+
12
+ return acc;
13
+ }, []);
14
+
15
+ const disconnected = serverStateRelations.reduce((acc, relation) => {
16
+ if (!browserStateRelations.find((oldRelation) => oldRelation.id === relation.id)) {
17
+ return [...acc, relation.id];
18
+ }
19
+
20
+ return acc;
21
+ }, []);
22
+
23
+ return [connected, disconnected];
24
+ };
@@ -1,3 +1,5 @@
1
1
  export { default as connect } from './connect';
2
2
  export { default as select } from './select';
3
- export { normalizeRelations } from './normalizeRelations';
3
+ export { normalizeRelations, normalizeRelation } from './normalizeRelations';
4
+ export { normalizeSearchResults } from './normalizeSearchResults';
5
+ export { diffRelations } from './diffRelations';
@@ -2,7 +2,7 @@ import { getRelationLink } from './getRelationLink';
2
2
 
3
3
  import { PUBLICATION_STATES } from '../constants';
4
4
 
5
- const normalizeRelation = (relation, { shouldAddLink, mainFieldName, targetModel }) => {
5
+ export const normalizeRelation = (relation, { shouldAddLink, mainFieldName, targetModel }) => {
6
6
  const nextRelation = { ...relation };
7
7
 
8
8
  if (shouldAddLink) {
@@ -22,37 +22,23 @@ const normalizeRelation = (relation, { shouldAddLink, mainFieldName, targetModel
22
22
  return nextRelation;
23
23
  };
24
24
 
25
+ /*
26
+ * Applies some transformations to existing and new relations in order to display them correctly
27
+ * relations: raw relations data coming from useRelations
28
+ * shouldAddLink: comes from generateRelationQueryInfos, if true we display a link to the relation (TO FIX: explanation)
29
+ * mainFieldName: name of the main field inside the relation (e.g. text field), if no displayable main field exists (e.g. date field) we use the id of the entry
30
+ * targetModel: the model on which the relation is based on, used to create an URL link
31
+ */
32
+
25
33
  export const normalizeRelations = (
26
34
  relations,
27
- { modifiedData = {}, shouldAddLink = false, mainFieldName, targetModel }
35
+ { shouldAddLink = false, mainFieldName, targetModel } = {}
28
36
  ) => {
29
- return {
30
- ...relations,
31
- data: {
32
- pages:
33
- [
34
- ...(relations?.data?.pages ?? []),
35
- ...(modifiedData?.connect ? [{ results: modifiedData.connect }] : []),
36
- ]
37
- ?.map((page) =>
38
- page?.results
39
- .filter(
40
- (relation) =>
41
- !modifiedData?.disconnect?.find(
42
- (disconnectRelation) => disconnectRelation.id === relation.id
43
- )
44
- )
45
- .map((relation) =>
46
- normalizeRelation(relation, {
47
- modifiedData,
48
- shouldAddLink,
49
- mainFieldName,
50
- targetModel,
51
- })
52
- )
53
- .filter(Boolean)
54
- )
55
- ?.filter((page) => page.length > 0) ?? [],
56
- },
57
- };
37
+ return [...relations].map((relation) =>
38
+ normalizeRelation(relation, {
39
+ shouldAddLink,
40
+ mainFieldName,
41
+ targetModel,
42
+ })
43
+ );
58
44
  };
@@ -0,0 +1,16 @@
1
+ import { normalizeRelation } from './normalizeRelations';
2
+
3
+ export const normalizeSearchResults = (relations, { mainFieldName }) => {
4
+ const { data } = relations;
5
+ const { pages = [] } = data ?? {};
6
+
7
+ return {
8
+ ...relations,
9
+ data: pages
10
+ .map((page) =>
11
+ page?.results.map((relation) => normalizeRelation(relation, { mainFieldName }))
12
+ )
13
+ .filter(Boolean)
14
+ .flat(),
15
+ };
16
+ };
@@ -1,9 +1,16 @@
1
1
  import { useMemo } from 'react';
2
+ import get from 'lodash/get';
2
3
  import { useCMEditViewDataManager } from '@strapi/helper-plugin';
3
4
 
4
5
  import { getRequestUrl } from '../../../utils';
5
6
 
6
- function useSelect({ isUserAllowedToEditField, isUserAllowedToReadField, name, queryInfos }) {
7
+ function useSelect({
8
+ componentUid,
9
+ isUserAllowedToEditField,
10
+ isUserAllowedToReadField,
11
+ name,
12
+ queryInfos,
13
+ }) {
7
14
  const {
8
15
  isCreatingEntry,
9
16
  createActionAllowedFields,
@@ -11,7 +18,6 @@ function useSelect({ isUserAllowedToEditField, isUserAllowedToReadField, name, q
11
18
  updateActionAllowedFields,
12
19
  slug,
13
20
  initialData,
14
- isSingleType,
15
21
  } = useCMEditViewDataManager();
16
22
 
17
23
  const isFieldAllowed = useMemo(() => {
@@ -40,25 +46,43 @@ function useSelect({ isUserAllowedToEditField, isUserAllowedToReadField, name, q
40
46
  return allowedFields.includes(name);
41
47
  }, [isCreatingEntry, isUserAllowedToReadField, name, readActionAllowedFields]);
42
48
 
43
- // /content-manager/[collection-type]/[content-type]/[id]/[field-name]
44
- const relationFetchEndpoint = useMemo(() => {
45
- const collectionTypePrefix = isSingleType ? 'single-types' : 'collection-types';
49
+ const fieldNameKeys = name.split('.');
50
+ let componentId;
51
+
52
+ if (componentUid) {
53
+ componentId = get(initialData, fieldNameKeys.slice(0, -1))?.id;
54
+ }
46
55
 
56
+ // /content-manager/relations/[model]/[id]/[field-name]
57
+ const relationFetchEndpoint = useMemo(() => {
47
58
  if (isCreatingEntry) {
48
59
  return null;
49
60
  }
50
61
 
51
- return getRequestUrl(
52
- `${collectionTypePrefix}/${slug}/${initialData.id}/${name.split('.').at(-1)}`
53
- );
54
- }, [isCreatingEntry, slug, initialData, name, isSingleType]);
62
+ if (componentUid) {
63
+ // repeatable components and dz are dynamically created
64
+ // if no componentId exists in initialData it means that the user just created it
65
+ // there then are no relations to request
66
+ return componentId
67
+ ? getRequestUrl(`relations/${componentUid}/${componentId}/${fieldNameKeys.at(-1)}`)
68
+ : null;
69
+ }
55
70
 
56
- // /content-manager/relations/[content-type]/[field-name]
71
+ return getRequestUrl(`relations/${slug}/${initialData.id}/${name.split('.').at(-1)}`);
72
+ }, [isCreatingEntry, componentUid, slug, initialData.id, name, componentId, fieldNameKeys]);
73
+
74
+ // /content-manager/relations/[model]/[field-name]
57
75
  const relationSearchEndpoint = useMemo(() => {
76
+ if (componentUid) {
77
+ return getRequestUrl(`relations/${componentUid}/${name.split('.').at(-1)}`);
78
+ }
79
+
58
80
  return getRequestUrl(`relations/${slug}/${name.split('.').at(-1)}`);
59
- }, [slug, name]);
81
+ }, [componentUid, slug, name]);
60
82
 
61
83
  return {
84
+ componentId,
85
+ isComponentRelation: Boolean(componentUid),
62
86
  queryInfos: {
63
87
  ...queryInfos,
64
88
  endpoints: {
@@ -44,6 +44,7 @@ const DragButton = styled.span`
44
44
 
45
45
  const DraggedItem = ({
46
46
  componentFieldName,
47
+ componentUid,
47
48
  // Errors are retrieved from the AccordionGroupCustom cloneElement
48
49
  hasErrorMessage,
49
50
  hasErrors,
@@ -265,11 +266,13 @@ const DraggedItem = ({
265
266
  return (
266
267
  <GridItem key={keys} col={size} s={12} xs={12}>
267
268
  <Inputs
269
+ componentUid={componentUid}
268
270
  fieldSchema={fieldSchema}
269
271
  keys={keys}
270
272
  metadatas={metadatas}
271
273
  // onBlur={hasErrors ? checkFormErrors : null}
272
274
  queryInfos={queryInfos}
275
+ size={size}
273
276
  />
274
277
  </GridItem>
275
278
  );
@@ -286,6 +289,7 @@ const DraggedItem = ({
286
289
  };
287
290
 
288
291
  DraggedItem.defaultProps = {
292
+ componentUid: undefined,
289
293
  isDraggingSibling: false,
290
294
  isOpen: false,
291
295
  setIsDraggingSibling() {},
@@ -294,6 +298,7 @@ DraggedItem.defaultProps = {
294
298
 
295
299
  DraggedItem.propTypes = {
296
300
  componentFieldName: PropTypes.string.isRequired,
301
+ componentUid: PropTypes.string,
297
302
  hasErrorMessage: PropTypes.bool.isRequired,
298
303
  hasErrors: PropTypes.bool.isRequired,
299
304
  isDraggingSibling: PropTypes.bool,
@@ -48,7 +48,7 @@ const RepeatableComponent = ({
48
48
  const [collapseToOpen, setCollapseToOpen] = useState('');
49
49
  const [isDraggingSibling, setIsDraggingSibling] = useState(false);
50
50
  const [, drop] = useDrop({ accept: ItemTypes.COMPONENT });
51
- const { getComponentLayout } = useContentTypeLayout();
51
+ const { getComponentLayout, components } = useContentTypeLayout();
52
52
  const componentLayoutData = useMemo(
53
53
  () => getComponentLayout(componentUid),
54
54
  [componentUid, getComponentLayout]
@@ -73,7 +73,7 @@ const RepeatableComponent = ({
73
73
  if (componentValueLength < max) {
74
74
  const shouldCheckErrors = hasMinError;
75
75
 
76
- addRepeatableComponentToField(name, componentUid, shouldCheckErrors);
76
+ addRepeatableComponentToField(name, componentLayoutData, components, shouldCheckErrors);
77
77
 
78
78
  setCollapseToOpen(nextTempKey);
79
79
  } else if (componentValueLength >= max) {
@@ -84,8 +84,9 @@ const RepeatableComponent = ({
84
84
  }
85
85
  }
86
86
  }, [
87
+ components,
87
88
  addRepeatableComponentToField,
88
- componentUid,
89
+ componentLayoutData,
89
90
  componentValueLength,
90
91
  hasMinError,
91
92
  isReadOnly,
@@ -234,6 +234,28 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
234
234
  queryClient,
235
235
  ]
236
236
  );
237
+
238
+ const onDraftRelationCheck = useCallback(async () => {
239
+ try {
240
+ trackUsageRef.current('willCheckDraftRelations');
241
+
242
+ const endPoint = getRequestUrl(`${slug}/actions/numberOfDraftRelations`);
243
+ dispatch(setStatus('draft-relation-check-pending'));
244
+
245
+ const numberOfDraftRelations = await axiosInstance.get(endPoint);
246
+ trackUsageRef.current('didCheckDraftRelations');
247
+
248
+ dispatch(setStatus('resolved'));
249
+
250
+ return numberOfDraftRelations.data.data;
251
+ } catch (err) {
252
+ displayErrors(err);
253
+ dispatch(setStatus('resolved'));
254
+
255
+ return Promise.reject(err);
256
+ }
257
+ }, [displayErrors, slug, dispatch]);
258
+
237
259
  const onPublish = useCallback(async () => {
238
260
  try {
239
261
  trackUsageRef.current('willPublishEntry');
@@ -337,6 +359,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
337
359
  onDelete,
338
360
  onDeleteSucceeded,
339
361
  onPost,
362
+ onDraftRelationCheck,
340
363
  onPublish,
341
364
  onPut,
342
365
  onUnpublish,
@@ -0,0 +1,26 @@
1
+ import { renderHook } from '@testing-library/react-hooks';
2
+
3
+ import { usePrev } from '../usePrev';
4
+
5
+ describe('usePrev', () => {
6
+ const setup = () => renderHook(({ state }) => usePrev(state), { initialProps: { state: 0 } });
7
+
8
+ it('should return undefined on initial render', () => {
9
+ const { result } = setup();
10
+
11
+ expect(result.current).toBeUndefined();
12
+ });
13
+
14
+ it('should always return previous state after each update', () => {
15
+ const { result, rerender } = setup();
16
+
17
+ rerender({ state: 2 });
18
+ expect(result.current).toBe(0);
19
+
20
+ rerender({ state: 4 });
21
+ expect(result.current).toBe(2);
22
+
23
+ rerender({ state: 6 });
24
+ expect(result.current).toBe(4);
25
+ });
26
+ });
@@ -5,3 +5,4 @@ export { default as useLayoutDnd } from './useLayoutDnd';
5
5
  export { default as usePluginsQueryParams } from './usePluginsQueryParams';
6
6
  export { default as useSyncRbac } from './useSyncRbac';
7
7
  export { default as useWysiwyg } from './useWysiwyg';
8
+ export { usePrev } from './usePrev';