@strapi/admin 4.5.0-beta.0 → 4.5.1

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 (264) hide show
  1. package/admin/src/StrapiApp.js +17 -6
  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/content-manager/components/DynamicTable/CellContent/CellValue.js +1 -1
  7. package/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/index.js +5 -4
  8. package/admin/src/content-manager/components/DynamicTable/CellContent/index.js +10 -0
  9. package/admin/src/content-manager/components/DynamicTable/index.js +21 -4
  10. package/admin/src/content-manager/components/DynamicZone/components/{AddComponentButton/index.js → AddComponentButton.js} +12 -6
  11. package/admin/src/content-manager/components/DynamicZone/components/{ComponentPicker/Category/ComponentCard/index.js → ComponentCard.js} +8 -19
  12. package/admin/src/content-manager/components/DynamicZone/components/{ComponentPicker/Category/index.js → ComponentCategory.js} +19 -18
  13. package/admin/src/content-manager/components/DynamicZone/components/{ComponentPicker/index.js → ComponentPicker.js} +36 -38
  14. package/admin/src/content-manager/components/DynamicZone/components/DynamicComponent.js +195 -0
  15. package/admin/src/content-manager/components/DynamicZone/components/{DzLabel/index.js → DynamicZoneLabel.js} +13 -5
  16. package/admin/src/content-manager/components/DynamicZone/index.js +35 -116
  17. package/admin/src/content-manager/components/EditViewDataManagerProvider/index.js +103 -60
  18. package/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js +169 -162
  19. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/cleanData.js +70 -16
  20. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/findLeafByPathAndReplace.js +52 -0
  21. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/index.js +2 -0
  22. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/recursivelyFindPathsBasedOnCondition.js +72 -0
  23. package/admin/src/content-manager/components/FieldComponent/index.js +9 -2
  24. package/admin/src/content-manager/components/PreviewWysiwyg/index.js +1 -1
  25. package/admin/src/content-manager/components/RelationInput/RelationInput.js +80 -76
  26. package/admin/src/content-manager/components/RelationInputDataManager/RelationInputDataManager.js +95 -63
  27. package/admin/src/content-manager/components/RelationInputDataManager/utils/diffRelations.js +24 -0
  28. package/admin/src/content-manager/components/RelationInputDataManager/utils/index.js +2 -1
  29. package/admin/src/content-manager/components/RelationInputDataManager/utils/normalizeRelations.js +8 -29
  30. package/admin/src/content-manager/components/RelationInputDataManager/utils/normalizeSearchResults.js +8 -4
  31. package/admin/src/content-manager/components/RelationInputDataManager/utils/select.js +1 -0
  32. package/admin/src/content-manager/components/RepeatableComponent/index.js +4 -3
  33. package/admin/src/content-manager/hooks/__test__/usePrev.test.js +26 -0
  34. package/admin/src/content-manager/hooks/index.js +1 -0
  35. package/admin/src/content-manager/hooks/useFetchContentTypeLayout/utils/formatLayouts.js +19 -48
  36. package/admin/src/content-manager/hooks/usePrev.js +14 -0
  37. package/admin/src/content-manager/hooks/useRelation/useRelation.js +100 -7
  38. package/admin/src/content-manager/pages/App/reducer.js +3 -0
  39. package/admin/src/content-manager/pages/ListSettingsView/components/DraggableCard.js +3 -3
  40. package/admin/src/content-manager/pages/ListSettingsView/components/Settings.js +2 -2
  41. package/admin/src/content-manager/pages/ListSettingsView/components/SortDisplayedFields.js +1 -1
  42. package/admin/src/core/apis/CustomFields.js +0 -1
  43. package/admin/src/core/store/configureStore.js +17 -2
  44. package/admin/src/favicon.png +0 -0
  45. package/admin/src/hooks/useFetchMarketplacePlugins/index.js +2 -2
  46. package/admin/src/hooks/useFetchMarketplacePlugins/utils/api.js +4 -2
  47. package/admin/src/hooks/useFetchMarketplaceProviders/index.js +3 -3
  48. package/admin/src/hooks/useFetchMarketplaceProviders/utils/api.js +5 -3
  49. package/admin/src/index.js +1 -0
  50. package/admin/src/pages/App/index.js +1 -1
  51. package/admin/src/pages/HomePage/assets/corner-ornament.svg +48 -0
  52. package/admin/src/pages/HomePage/index.js +4 -3
  53. package/admin/src/pages/MarketplacePage/components/NpmPackageCard/CardButton.js +110 -0
  54. package/admin/src/pages/MarketplacePage/components/NpmPackageCard/InstallPluginButton.js +32 -21
  55. package/admin/src/pages/MarketplacePage/components/NpmPackageCard/PackageStats.js +79 -0
  56. package/admin/src/pages/MarketplacePage/components/NpmPackageCard/index.js +28 -11
  57. package/admin/src/pages/MarketplacePage/components/NpmPackagesFilters/FilterSelect.js +42 -0
  58. package/admin/src/pages/MarketplacePage/components/NpmPackagesFilters/FiltersPopover.js +96 -0
  59. package/admin/src/pages/MarketplacePage/components/NpmPackagesFilters/index.js +107 -0
  60. package/admin/src/pages/MarketplacePage/components/NpmPackagesGrid/index.js +4 -0
  61. package/admin/src/pages/MarketplacePage/components/SortSelect/index.js +70 -0
  62. package/admin/src/pages/MarketplacePage/index.js +68 -8
  63. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormApiTokenContainer/index.js +5 -4
  64. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormBody/index.js +4 -3
  65. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormHead/index.js +6 -2
  66. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/Regenerate/index.js +1 -1
  67. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js +5 -4
  68. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/schema.js +1 -1
  69. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/index.js +7 -38
  70. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/utils/options.js +31 -0
  71. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/index.js +32 -43
  72. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/Collapse/index.js +1 -1
  73. package/admin/src/pages/SettingsPage/pages/Roles/ListPage/components/RoleRow/index.js +3 -1
  74. package/admin/src/pages/SettingsPage/pages/Roles/ListPage/index.js +2 -1
  75. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/EventInput/index.js +2 -2
  76. package/admin/src/translations/ca.json +1 -1
  77. package/admin/src/translations/de.json +1 -1
  78. package/admin/src/translations/dk.json +1 -1
  79. package/admin/src/translations/en.json +21 -7
  80. package/admin/src/translations/es.json +1 -1
  81. package/admin/src/translations/fr.json +1 -1
  82. package/admin/src/translations/gu.json +1 -1
  83. package/admin/src/translations/he.json +1 -1
  84. package/admin/src/translations/hi.json +1 -1
  85. package/admin/src/translations/hu.json +1 -1
  86. package/admin/src/translations/id.json +1 -1
  87. package/admin/src/translations/it.json +1 -1
  88. package/admin/src/translations/ja.json +1 -1
  89. package/admin/src/translations/ko.json +1 -1
  90. package/admin/src/translations/ml.json +1 -1
  91. package/admin/src/translations/nl.json +1 -1
  92. package/admin/src/translations/no.json +1 -1
  93. package/admin/src/translations/pl.json +1 -1
  94. package/admin/src/translations/pt-BR.json +15 -15
  95. package/admin/src/translations/ru.json +1 -1
  96. package/admin/src/translations/sa.json +1 -1
  97. package/admin/src/translations/sk.json +1 -1
  98. package/admin/src/translations/sv.json +118 -86
  99. package/admin/src/translations/th.json +1 -1
  100. package/admin/src/translations/zh-Hans.json +1 -1
  101. package/admin/src/translations/zh.json +1 -1
  102. package/build/1856.db9f5782.chunk.js +174 -0
  103. package/build/2077.fed8c9c3.chunk.js +206 -0
  104. package/build/2912.fccb2c43.chunk.js +259 -0
  105. package/build/4318.5e670740.chunk.js +30 -0
  106. package/build/{9166.8fcb3019.chunk.js → 4610.7614b003.chunk.js} +22 -21
  107. package/build/4715.8e33d630.chunk.js +387 -0
  108. package/build/{4800.d09f1225.chunk.js → 4800.a6935af6.chunk.js} +1 -1
  109. package/build/4982.9e58ea3f.chunk.js +325 -0
  110. package/build/617f9c948fa79e6d73bd.png +0 -0
  111. package/build/6925.bb6dd64d.chunk.js +762 -0
  112. package/build/6d21938306785f176538.png +0 -0
  113. package/build/70674f63fc3904c20de0.svg +7 -0
  114. package/build/{7379.d246dd38.chunk.js → 7379.e972985f.chunk.js} +1 -1
  115. package/build/7692.31e83caa.chunk.js +470 -0
  116. package/build/7841.4804bd98.chunk.js +259 -0
  117. package/build/7866.6db2248d.chunk.js +505 -0
  118. package/build/7e9af4fb7e723fcebf1f.svg +48 -0
  119. package/build/8380.37126e0d.chunk.js +299 -0
  120. package/build/8549.5e5fb6b6.chunk.js +159 -0
  121. package/build/8738.5a02bffb.chunk.js +463 -0
  122. package/build/{9066.26faf397.chunk.js → 9066.5d980488.chunk.js} +5 -5
  123. package/build/9420.7addc099.chunk.js +505 -0
  124. package/build/9649.b6afc945.chunk.js +199 -0
  125. package/build/9d5d788027e86620c234.svg +5 -0
  126. package/build/Admin-authenticatedApp.c07d2a86.chunk.js +80 -0
  127. package/build/{Admin_homePage.4b2be829.chunk.js → Admin_homePage.26d32e30.chunk.js} +5 -4
  128. package/build/Admin_marketplace.444ff7b8.chunk.js +22 -0
  129. package/build/Admin_settingsPage.bf2234e1.chunk.js +178 -0
  130. package/build/admin-app.b157c10a.chunk.js +112 -0
  131. package/build/{admin-edit-roles-page.4dd6bcb9.chunk.js → admin-edit-roles-page.69d9fcb2.chunk.js} +1 -1
  132. package/build/ca-json.07ae0f2c.chunk.js +1 -0
  133. package/build/content-manager.f38edbb6.chunk.js +1202 -0
  134. package/build/content-type-builder-translation-pt-BR-json.6fe3b8d1.chunk.js +1 -0
  135. package/build/content-type-builder-translation-sv-json.6deff030.chunk.js +1 -0
  136. package/build/{content-type-builder.a6e29716.chunk.js → content-type-builder.16af63a6.chunk.js} +13 -13
  137. package/build/de-json.6b3e1894.chunk.js +1 -0
  138. package/build/dk-json.144c6a8e.chunk.js +1 -0
  139. package/build/{email-settings-page.bfe6227f.chunk.js → email-settings-page.91c925a5.chunk.js} +6 -6
  140. package/build/email-translation-en-json.ebad8943.chunk.js +1 -0
  141. package/build/en-json.4a269f6b.chunk.js +1 -0
  142. package/build/es-json.6d123a82.chunk.js +1 -0
  143. package/build/fr-json.28ab54cb.chunk.js +1 -0
  144. package/build/gu-json.9a50ea64.chunk.js +1 -0
  145. package/build/he-json.72f18790.chunk.js +1 -0
  146. package/build/hi-json.0301b7ba.chunk.js +1 -0
  147. package/build/hu-json.c4b641bb.chunk.js +1 -0
  148. package/build/{i18n-settings-page.18166125.chunk.js → i18n-settings-page.4ef64441.chunk.js} +5 -5
  149. package/build/id-json.86035797.chunk.js +1 -0
  150. package/build/index.html +1 -1
  151. package/build/it-json.bbdc8993.chunk.js +1 -0
  152. package/build/ja-json.1c9eeeec.chunk.js +1 -0
  153. package/build/ko-json.e1f66398.chunk.js +1 -0
  154. package/build/main.ca8b0ee3.js +9465 -0
  155. package/build/ml-json.963c889f.chunk.js +1 -0
  156. package/build/nl-json.2b8cc3a0.chunk.js +1 -0
  157. package/build/no-json.a58c28bd.chunk.js +1 -0
  158. package/build/pl-json.249626b3.chunk.js +1 -0
  159. package/build/pt-BR-json.2b72b1d6.chunk.js +1 -0
  160. package/build/ru-json.d7cfc2ff.chunk.js +1 -0
  161. package/build/runtime~main.ede9da1e.js +2 -0
  162. package/build/sa-json.44e95991.chunk.js +1 -0
  163. package/build/sk-json.7ba4b330.chunk.js +1 -0
  164. package/build/sv-json.fb1081ff.chunk.js +1 -0
  165. package/build/th-json.a67309b1.chunk.js +1 -0
  166. package/build/{upload-settings.3d613216.chunk.js → upload-settings.3f7ad973.chunk.js} +5 -5
  167. package/build/{users-advanced-settings-page.f4051d92.chunk.js → users-advanced-settings-page.6a838320.chunk.js} +5 -5
  168. package/build/users-permissions-translation-sv-json.d5d11648.chunk.js +1 -0
  169. package/build/{webhook-edit-page.9e46fc3f.chunk.js → webhook-edit-page.dc9442ce.chunk.js} +1 -1
  170. package/build/webhook-list-page.a110c462.chunk.js +134 -0
  171. package/build/zh-Hans-json.21617c24.chunk.js +1 -0
  172. package/build/zh-json.608aaf24.chunk.js +1 -0
  173. package/ee/admin/pages/SettingsPage/pages/Roles/ListPage/index.js +3 -2
  174. package/env.js +1 -0
  175. package/package.json +18 -17
  176. package/scripts/build.js +11 -0
  177. package/server/content-types/api-token.js +1 -1
  178. package/utils/create-plugins-exclude-path.js +40 -0
  179. package/webpack.alias.js +0 -13
  180. package/webpack.config.js +4 -1
  181. package/admin/src/assets/images/banner_strapi-rocket.png +0 -0
  182. package/admin/src/assets/images/big-logo-home.png +0 -0
  183. package/admin/src/assets/images/homepage-logo.png +0 -0
  184. package/admin/src/assets/images/icon_made-by-strapi.svg +0 -5
  185. package/admin/src/assets/images/logo_strapi_auth.png +0 -0
  186. package/admin/src/assets/images/logo_strapi_auth_v4.png +0 -0
  187. package/admin/src/assets/images/logo_strapi_menu.png +0 -0
  188. package/admin/src/assets/images/oops.png +0 -0
  189. package/admin/src/content-manager/components/DynamicZone/components/Component/Rectangle.js +0 -19
  190. package/admin/src/content-manager/components/DynamicZone/components/Component/index.js +0 -191
  191. package/admin/src/content-manager/components/State/index.js +0 -37
  192. package/admin/src/content-manager/icons/Bold/index.js +0 -22
  193. package/admin/src/content-manager/icons/Code/index.js +0 -13
  194. package/admin/src/content-manager/icons/Cross/index.js +0 -28
  195. package/admin/src/content-manager/icons/Italic/index.js +0 -23
  196. package/admin/src/content-manager/icons/Link/index.js +0 -21
  197. package/admin/src/content-manager/icons/Media/index.js +0 -14
  198. package/admin/src/content-manager/icons/Na/index.js +0 -39
  199. package/admin/src/content-manager/icons/Ol/index.js +0 -13
  200. package/admin/src/content-manager/icons/Quote/index.js +0 -13
  201. package/admin/src/content-manager/icons/Striked/index.js +0 -24
  202. package/admin/src/content-manager/icons/Ul/index.js +0 -15
  203. package/admin/src/content-manager/icons/Underline/index.js +0 -22
  204. package/admin/src/favicon.ico +0 -0
  205. package/build/15026a3d58aeb2828134.png +0 -0
  206. package/build/1856.d8f13391.chunk.js +0 -173
  207. package/build/1939.e3c87653.chunk.js +0 -325
  208. package/build/2077.31a2d91e.chunk.js +0 -205
  209. package/build/2912.ab68a736.chunk.js +0 -258
  210. package/build/4318.7d167b58.chunk.js +0 -30
  211. package/build/4715.44b1ef9b.chunk.js +0 -386
  212. package/build/4982.c2a311b7.chunk.js +0 -324
  213. package/build/6925.f5c8b6fc.chunk.js +0 -761
  214. package/build/7841.4b67af3f.chunk.js +0 -258
  215. package/build/7866.5fbeb7e5.chunk.js +0 -504
  216. package/build/8380.9b53a31d.chunk.js +0 -284
  217. package/build/8549.cf10b5d1.chunk.js +0 -158
  218. package/build/8738.a30a2160.chunk.js +0 -461
  219. package/build/90f49a385afb000fb1d4.svg +0 -5
  220. package/build/9420.0fe11290.chunk.js +0 -504
  221. package/build/962.8651ba3f.chunk.js +0 -184
  222. package/build/Admin-authenticatedApp.883449a5.chunk.js +0 -80
  223. package/build/Admin_marketplace.82c0570b.chunk.js +0 -11
  224. package/build/Admin_settingsPage.98e2a62b.chunk.js +0 -178
  225. package/build/a6b842e0b6d2b61135d1.svg +0 -5
  226. package/build/admin-app.a61d5c2e.chunk.js +0 -112
  227. package/build/b997a22a2e0b87ef1fa2.ico +0 -0
  228. package/build/bd81ba6c07827282255d.png +0 -0
  229. package/build/c3de6118ef47086ad05c.png +0 -0
  230. package/build/ca-json.82df6eab.chunk.js +0 -1
  231. package/build/content-manager.933dc286.chunk.js +0 -1201
  232. package/build/content-type-builder-translation-pt-BR-json.d6c7fcc1.chunk.js +0 -1
  233. package/build/de-json.0ad554eb.chunk.js +0 -1
  234. package/build/dk-json.e195ea1a.chunk.js +0 -1
  235. package/build/email-translation-en-json.3d74ff95.chunk.js +0 -1
  236. package/build/en-json.1889403c.chunk.js +0 -1
  237. package/build/es-json.09f80f6e.chunk.js +0 -1
  238. package/build/fb376b132d18bf4522ca.png +0 -0
  239. package/build/fde9b1ad0670d29a2516.png +0 -0
  240. package/build/fr-json.606d056b.chunk.js +0 -1
  241. package/build/gu-json.9881264f.chunk.js +0 -1
  242. package/build/he-json.3b825d80.chunk.js +0 -1
  243. package/build/hi-json.83dcf48f.chunk.js +0 -1
  244. package/build/hu-json.6f328bce.chunk.js +0 -1
  245. package/build/id-json.1f3c4303.chunk.js +0 -1
  246. package/build/it-json.494ac432.chunk.js +0 -1
  247. package/build/ja-json.6f262117.chunk.js +0 -1
  248. package/build/ko-json.36dc3b9a.chunk.js +0 -1
  249. package/build/main.63e7ea0a.js +0 -9338
  250. package/build/ml-json.9566bf9a.chunk.js +0 -1
  251. package/build/nl-json.94c3a289.chunk.js +0 -1
  252. package/build/no-json.40386397.chunk.js +0 -1
  253. package/build/pl-json.ccc6ef23.chunk.js +0 -1
  254. package/build/pt-BR-json.744f024d.chunk.js +0 -1
  255. package/build/ru-json.d22ea13c.chunk.js +0 -1
  256. package/build/runtime~main.3a5e1b07.js +0 -2
  257. package/build/sa-json.8fb1c04d.chunk.js +0 -1
  258. package/build/sk-json.6c7335d4.chunk.js +0 -1
  259. package/build/sv-json.2e589a7d.chunk.js +0 -1
  260. package/build/th-json.72e8de3d.chunk.js +0 -1
  261. package/build/users-permissions-translation-sv-json.83c60841.chunk.js +0 -1
  262. package/build/webhook-list-page.a712ae40.chunk.js +0 -134
  263. package/build/zh-Hans-json.a4d7dc69.chunk.js +0 -1
  264. package/build/zh-json.66aa2ae1.chunk.js +0 -1
@@ -13,7 +13,7 @@ import { Layout } from '@strapi/design-system/Layout';
13
13
  import { Main } from '@strapi/design-system/Main';
14
14
  import { Box } from '@strapi/design-system/Box';
15
15
  import { Grid, GridItem } from '@strapi/design-system/Grid';
16
- import Logo from '../../assets/images/homepage-logo.png';
16
+ import cornerOrnamentPath from './assets/corner-ornament.svg';
17
17
  import { useModels } from '../../hooks';
18
18
  import isGuidedTourCompleted from '../../components/GuidedTour/utils/isGuidedTourCompleted';
19
19
  import GuidedTourHomepage from '../../components/GuidedTour/Homepage';
@@ -25,13 +25,14 @@ const LogoContainer = styled(Box)`
25
25
  position: absolute;
26
26
  top: 0;
27
27
  right: 0;
28
+
28
29
  img {
29
30
  width: ${150 / 16}rem;
30
31
  }
31
32
  `;
32
33
 
33
34
  const HomePage = () => {
34
- // // Temporary until we develop the menu API
35
+ // Temporary until we develop the menu API
35
36
  const { collectionTypes, singleTypes, isLoading: isLoadingForModels } = useModels();
36
37
  const { guidedTourState, isGuidedTourVisible, isSkipped } = useGuidedTour();
37
38
 
@@ -64,7 +65,7 @@ const HomePage = () => {
64
65
  </FormattedMessage>
65
66
  <Main>
66
67
  <LogoContainer>
67
- <img alt="" aria-hidden src={Logo} />
68
+ <img alt="" aria-hidden src={cornerOrnamentPath} />
68
69
  </LogoContainer>
69
70
  <Box padding={10}>
70
71
  <Grid>
@@ -0,0 +1,110 @@
1
+ import React from 'react';
2
+ import semver from 'semver';
3
+ import PropTypes from 'prop-types';
4
+ import { useIntl } from 'react-intl';
5
+ import { Tooltip } from '@strapi/design-system/Tooltip';
6
+ import { Button } from '@strapi/design-system/Button';
7
+ import { Box } from '@strapi/design-system/Box';
8
+ import Duplicate from '@strapi/icons/Duplicate';
9
+
10
+ const TooltipButton = ({ description, installMessage, disabled, handleCopy, pluginName }) => (
11
+ <Tooltip data-testid={`tooltip-${pluginName}`} description={description}>
12
+ <Box>
13
+ <Button
14
+ size="S"
15
+ startIcon={<Duplicate />}
16
+ variant="secondary"
17
+ disabled={disabled}
18
+ onClick={handleCopy}
19
+ >
20
+ {installMessage}
21
+ </Button>
22
+ </Box>
23
+ </Tooltip>
24
+ );
25
+
26
+ const CardButton = ({ strapiPeerDepVersion, strapiAppVersion, handleCopy, pluginName }) => {
27
+ const { formatMessage } = useIntl();
28
+ const versionRange = semver.validRange(strapiPeerDepVersion);
29
+ const isCompatible = semver.satisfies(strapiAppVersion, versionRange);
30
+
31
+ const installMessage = formatMessage({
32
+ id: 'admin.pages.MarketPlacePage.plugin.copy',
33
+ defaultMessage: 'Copy install command',
34
+ });
35
+
36
+ // Only plugins receive a strapiAppVersion
37
+ if (strapiAppVersion) {
38
+ if (!versionRange) {
39
+ return (
40
+ <TooltipButton
41
+ installMessage={installMessage}
42
+ pluginName={pluginName}
43
+ description={formatMessage(
44
+ {
45
+ id: 'admin.pages.MarketPlacePage.plugin.version.null',
46
+ defaultMessage:
47
+ 'Unable to verify compatibility with your Strapi version: "{strapiAppVersion}"',
48
+ },
49
+ { strapiAppVersion }
50
+ )}
51
+ handleCopy={handleCopy}
52
+ />
53
+ );
54
+ }
55
+
56
+ if (!isCompatible) {
57
+ return (
58
+ <TooltipButton
59
+ installMessage={installMessage}
60
+ pluginName={pluginName}
61
+ description={formatMessage(
62
+ {
63
+ id: 'admin.pages.MarketPlacePage.plugin.version',
64
+ defaultMessage:
65
+ 'Update your Strapi version: "{strapiAppVersion}" to: "{versionRange}"',
66
+ },
67
+ {
68
+ strapiAppVersion,
69
+ versionRange,
70
+ }
71
+ )}
72
+ disabled
73
+ />
74
+ );
75
+ }
76
+ }
77
+
78
+ return (
79
+ <Button size="S" startIcon={<Duplicate />} variant="secondary" onClick={handleCopy}>
80
+ {installMessage}
81
+ </Button>
82
+ );
83
+ };
84
+
85
+ TooltipButton.defaultProps = {
86
+ disabled: false,
87
+ handleCopy: null,
88
+ };
89
+
90
+ TooltipButton.propTypes = {
91
+ description: PropTypes.string.isRequired,
92
+ installMessage: PropTypes.string.isRequired,
93
+ disabled: PropTypes.bool,
94
+ handleCopy: PropTypes.func,
95
+ pluginName: PropTypes.string.isRequired,
96
+ };
97
+
98
+ CardButton.defaultProps = {
99
+ strapiAppVersion: null,
100
+ strapiPeerDepVersion: null,
101
+ };
102
+
103
+ CardButton.propTypes = {
104
+ strapiAppVersion: PropTypes.string,
105
+ strapiPeerDepVersion: PropTypes.string,
106
+ handleCopy: PropTypes.func.isRequired,
107
+ pluginName: PropTypes.string.isRequired,
108
+ };
109
+
110
+ export default CardButton;
@@ -1,20 +1,34 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { useIntl } from 'react-intl';
4
- import { CopyToClipboard } from 'react-copy-to-clipboard';
5
4
  import { useNotification, useTracking } from '@strapi/helper-plugin';
6
5
  import { Box } from '@strapi/design-system/Box';
7
6
  import { Icon } from '@strapi/design-system/Icon';
8
7
  import { Typography } from '@strapi/design-system/Typography';
9
8
  import Check from '@strapi/icons/Check';
10
- import Duplicate from '@strapi/icons/Duplicate';
11
- import { Button } from '@strapi/design-system/Button';
9
+ import CardButton from './CardButton';
12
10
 
13
- const InstallPluginButton = ({ isInstalled, isInDevelopmentMode, commandToCopy }) => {
11
+ const InstallPluginButton = ({
12
+ isInstalled,
13
+ isInDevelopmentMode,
14
+ commandToCopy,
15
+ strapiAppVersion,
16
+ strapiPeerDepVersion,
17
+ pluginName,
18
+ }) => {
14
19
  const toggleNotification = useNotification();
15
20
  const { formatMessage } = useIntl();
16
21
  const { trackUsage } = useTracking();
17
22
 
23
+ const handleCopy = () => {
24
+ navigator.clipboard.writeText(commandToCopy);
25
+ trackUsage('willInstallPlugin');
26
+ toggleNotification({
27
+ type: 'success',
28
+ message: { id: 'admin.pages.MarketPlacePage.plugin.copy.success' },
29
+ });
30
+ };
31
+
18
32
  // Already installed
19
33
  if (isInstalled) {
20
34
  return (
@@ -33,23 +47,12 @@ const InstallPluginButton = ({ isInstalled, isInDevelopmentMode, commandToCopy }
33
47
  // In development, show install button
34
48
  if (isInDevelopmentMode) {
35
49
  return (
36
- <CopyToClipboard
37
- onCopy={() => {
38
- trackUsage('willInstallPlugin');
39
- toggleNotification({
40
- type: 'success',
41
- message: { id: 'admin.pages.MarketPlacePage.plugin.copy.success' },
42
- });
43
- }}
44
- text={commandToCopy}
45
- >
46
- <Button size="S" startIcon={<Duplicate />} variant="secondary">
47
- {formatMessage({
48
- id: 'admin.pages.MarketPlacePage.plugin.copy',
49
- defaultMessage: 'Copy install command',
50
- })}
51
- </Button>
52
- </CopyToClipboard>
50
+ <CardButton
51
+ strapiAppVersion={strapiAppVersion}
52
+ strapiPeerDepVersion={strapiPeerDepVersion}
53
+ handleCopy={handleCopy}
54
+ pluginName={pluginName}
55
+ />
53
56
  );
54
57
  }
55
58
 
@@ -57,10 +60,18 @@ const InstallPluginButton = ({ isInstalled, isInDevelopmentMode, commandToCopy }
57
60
  return null;
58
61
  };
59
62
 
63
+ InstallPluginButton.defaultProps = {
64
+ strapiAppVersion: null,
65
+ strapiPeerDepVersion: null,
66
+ };
67
+
60
68
  InstallPluginButton.propTypes = {
61
69
  isInstalled: PropTypes.bool.isRequired,
62
70
  isInDevelopmentMode: PropTypes.bool.isRequired,
63
71
  commandToCopy: PropTypes.string.isRequired,
72
+ strapiAppVersion: PropTypes.string,
73
+ strapiPeerDepVersion: PropTypes.string,
74
+ pluginName: PropTypes.string.isRequired,
64
75
  };
65
76
 
66
77
  export default InstallPluginButton;
@@ -0,0 +1,79 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import styled from 'styled-components';
4
+ import { useIntl } from 'react-intl';
5
+ import { Typography } from '@strapi/design-system/Typography';
6
+ import { Icon } from '@strapi/design-system/Icon';
7
+ import { Divider } from '@strapi/design-system/Divider';
8
+ import { Stack } from '@strapi/design-system/Stack';
9
+ import Github from '@strapi/icons/Github';
10
+ import Download from '@strapi/icons/Download';
11
+ import Star from '@strapi/icons/Star';
12
+ import { pxToRem } from '@strapi/helper-plugin';
13
+
14
+ const VerticalDivider = styled(Divider)`
15
+ width: ${pxToRem(12)};
16
+ transform: rotate(90deg);
17
+ `;
18
+
19
+ const PackageStats = ({ githubStars, npmDownloads, npmPackageType }) => {
20
+ const { formatMessage } = useIntl();
21
+
22
+ return (
23
+ <Stack horizontal spacing={1}>
24
+ {!!githubStars && (
25
+ <>
26
+ <Icon as={Github} height={pxToRem(12)} width={pxToRem(12)} aria-hidden />
27
+ <Icon as={Star} height={pxToRem(12)} width={pxToRem(12)} color="warning500" aria-hidden />
28
+ <p
29
+ aria-label={formatMessage(
30
+ {
31
+ id: `admin.pages.MarketPlacePage.${npmPackageType}.githubStars`,
32
+ defaultMessage: `This {package} was starred {starsCount} on GitHub`,
33
+ },
34
+ {
35
+ starsCount: githubStars,
36
+ package: npmPackageType,
37
+ }
38
+ )}
39
+ >
40
+ <Typography variant="pi" textColor="neutral800" lineHeight={16}>
41
+ {githubStars}
42
+ </Typography>
43
+ </p>
44
+ <VerticalDivider unsetMargin={false} background="neutral200" />
45
+ </>
46
+ )}
47
+ <Icon as={Download} height={pxToRem(12)} width={pxToRem(12)} aria-hidden />
48
+ <p
49
+ aria-label={formatMessage(
50
+ {
51
+ id: `admin.pages.MarketPlacePage.${npmPackageType}.downloads`,
52
+ defaultMessage: `This {package} has {downloadsCount} weekly downloads`,
53
+ },
54
+ {
55
+ downloadsCount: npmDownloads,
56
+ package: npmPackageType,
57
+ }
58
+ )}
59
+ >
60
+ <Typography variant="pi" textColor="neutral800" lineHeight={16}>
61
+ {npmDownloads}
62
+ </Typography>
63
+ </p>
64
+ </Stack>
65
+ );
66
+ };
67
+
68
+ PackageStats.defaultProps = {
69
+ githubStars: 0,
70
+ npmDownloads: 0,
71
+ };
72
+
73
+ PackageStats.propTypes = {
74
+ githubStars: PropTypes.number,
75
+ npmDownloads: PropTypes.number,
76
+ npmPackageType: PropTypes.string.isRequired,
77
+ };
78
+
79
+ export default PackageStats;
@@ -13,8 +13,9 @@ import { Tooltip } from '@strapi/design-system/Tooltip';
13
13
  import ExternalLink from '@strapi/icons/ExternalLink';
14
14
  import CheckCircle from '@strapi/icons/CheckCircle';
15
15
  import { useTracking } from '@strapi/helper-plugin';
16
- import madeByStrapiIcon from '../../../../assets/images/icon_made-by-strapi.svg';
16
+ import StrapiLogo from '../../../../assets/images/logo-strapi-2022.svg';
17
17
  import InstallPluginButton from './InstallPluginButton';
18
+ import PackageStats from './PackageStats';
18
19
 
19
20
  // Custom component to have an ellipsis after the 2nd line
20
21
  const EllipsisText = styled(Typography)`
@@ -32,6 +33,7 @@ const NpmPackageCard = ({
32
33
  useYarn,
33
34
  isInDevelopmentMode,
34
35
  npmPackageType,
36
+ strapiAppVersion,
35
37
  }) => {
36
38
  const { attributes } = npmPackage;
37
39
  const { formatMessage } = useIntl();
@@ -66,14 +68,21 @@ const NpmPackageCard = ({
66
68
  data-testid="npm-package-card"
67
69
  >
68
70
  <Box>
69
- <Box
70
- as="img"
71
- src={attributes.logo.url}
72
- alt={`${attributes.name} logo`}
73
- hasRadius
74
- width={11}
75
- height={11}
76
- />
71
+ <Flex direction="row" justifyContent="space-between" alignItems="flex-start">
72
+ <Box
73
+ as="img"
74
+ src={attributes.logo.url}
75
+ alt={`${attributes.name} logo`}
76
+ hasRadius
77
+ width={11}
78
+ height={11}
79
+ />
80
+ <PackageStats
81
+ githubStars={attributes.githubStars}
82
+ npmDownloads={attributes.npmDownloads}
83
+ npmPackageType={npmPackageType}
84
+ />
85
+ </Flex>
77
86
  <Box paddingTop={4}>
78
87
  <Typography as="h3" variant="delta">
79
88
  <Flex alignItems="center">
@@ -95,7 +104,7 @@ const NpmPackageCard = ({
95
104
  <Flex>
96
105
  <Box
97
106
  as="img"
98
- src={madeByStrapiIcon}
107
+ src={StrapiLogo}
99
108
  alt={madeByStrapiMessage}
100
109
  marginLeft={1}
101
110
  width={6}
@@ -132,13 +141,16 @@ const NpmPackageCard = ({
132
141
  >
133
142
  {formatMessage({
134
143
  id: 'admin.pages.MarketPlacePage.plugin.info.text',
135
- defaultMessage: 'Learn more',
144
+ defaultMessage: 'More',
136
145
  })}
137
146
  </LinkButton>
138
147
  <InstallPluginButton
139
148
  isInstalled={isInstalled}
140
149
  isInDevelopmentMode={isInDevelopmentMode}
141
150
  commandToCopy={commandToCopy}
151
+ strapiAppVersion={strapiAppVersion}
152
+ strapiPeerDepVersion={attributes.strapiVersion}
153
+ pluginName={attributes.name}
142
154
  />
143
155
  </Stack>
144
156
  </Flex>
@@ -147,6 +159,7 @@ const NpmPackageCard = ({
147
159
 
148
160
  NpmPackageCard.defaultProps = {
149
161
  isInDevelopmentMode: false,
162
+ strapiAppVersion: null,
150
163
  };
151
164
 
152
165
  NpmPackageCard.propTypes = {
@@ -164,12 +177,16 @@ NpmPackageCard.propTypes = {
164
177
  validated: PropTypes.bool.isRequired,
165
178
  madeByStrapi: PropTypes.bool.isRequired,
166
179
  strapiCompatibility: PropTypes.oneOf(['v3', 'v4']),
180
+ strapiVersion: PropTypes.string,
181
+ githubStars: PropTypes.number,
182
+ npmDownloads: PropTypes.number,
167
183
  }).isRequired,
168
184
  }).isRequired,
169
185
  isInstalled: PropTypes.bool.isRequired,
170
186
  useYarn: PropTypes.bool.isRequired,
171
187
  isInDevelopmentMode: PropTypes.bool,
172
188
  npmPackageType: PropTypes.string.isRequired,
189
+ strapiAppVersion: PropTypes.string,
173
190
  };
174
191
 
175
192
  export default NpmPackageCard;
@@ -0,0 +1,42 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Select, Option } from '@strapi/design-system/Select';
4
+
5
+ const FilterSelect = ({ message, value, onChange, possibleFilters, onClear, customizeContent }) => {
6
+ const computeFilterMessage = (filterName, count) => {
7
+ return `${filterName} (${count})`;
8
+ };
9
+
10
+ return (
11
+ <Select
12
+ data-testid={`${message}-button`}
13
+ aria-label={message}
14
+ placeholder={message}
15
+ size="M"
16
+ onChange={onChange}
17
+ onClear={onClear}
18
+ value={value}
19
+ customizeContent={customizeContent}
20
+ multi
21
+ >
22
+ {Object.entries(possibleFilters).map(([filterName, count]) => {
23
+ return (
24
+ <Option data-testid={`${filterName}-${count}`} key={filterName} value={filterName}>
25
+ {computeFilterMessage(filterName, count)}
26
+ </Option>
27
+ );
28
+ })}
29
+ </Select>
30
+ );
31
+ };
32
+
33
+ FilterSelect.propTypes = {
34
+ message: PropTypes.string.isRequired,
35
+ value: PropTypes.array.isRequired,
36
+ onChange: PropTypes.func.isRequired,
37
+ possibleFilters: PropTypes.object.isRequired,
38
+ onClear: PropTypes.func.isRequired,
39
+ customizeContent: PropTypes.func.isRequired,
40
+ };
41
+
42
+ export default FilterSelect;
@@ -0,0 +1,96 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Box } from '@strapi/design-system/Box';
4
+ import { Popover } from '@strapi/design-system/Popover';
5
+ import { Stack } from '@strapi/design-system/Stack';
6
+ import { FocusTrap } from '@strapi/design-system/FocusTrap';
7
+ import { useIntl } from 'react-intl';
8
+ import FilterSelect from './FilterSelect';
9
+
10
+ const FiltersPopover = ({
11
+ source,
12
+ onToggle,
13
+ query,
14
+ npmPackageType,
15
+ possibleCategories,
16
+ possibleCollections,
17
+ handleSelectChange,
18
+ handleSelectClear,
19
+ }) => {
20
+ const { formatMessage } = useIntl();
21
+
22
+ return (
23
+ <Popover source={source} padding={3} spacing={4} onBlur={() => {}}>
24
+ <FocusTrap onEscape={onToggle}>
25
+ <Stack spacing={1}>
26
+ <Box>
27
+ <FilterSelect
28
+ message={formatMessage({
29
+ id: 'admin.pages.MarketPlacePage.filters.collections',
30
+ defaultMessage: 'Collections',
31
+ })}
32
+ value={query?.collections || []}
33
+ onChange={(newCollections) => {
34
+ const update = { collections: newCollections };
35
+ handleSelectChange(update);
36
+ }}
37
+ onClear={() => handleSelectClear('collections')}
38
+ possibleFilters={possibleCollections}
39
+ customizeContent={(values) =>
40
+ formatMessage(
41
+ {
42
+ id: 'admin.pages.MarketPlacePage.filters.collectionsSelected',
43
+ defaultMessage:
44
+ '{count, plural, =0 {No collections} one {# collection} other {# collections}} selected',
45
+ },
46
+ { count: values.length }
47
+ )
48
+ }
49
+ />
50
+ </Box>
51
+ {npmPackageType === 'plugin' && (
52
+ <Box>
53
+ <FilterSelect
54
+ message={formatMessage({
55
+ id: 'admin.pages.MarketPlacePage.filters.categories',
56
+ defaultMessage: 'Categories',
57
+ })}
58
+ value={query?.categories || []}
59
+ onChange={(newCategories) => {
60
+ const update = { categories: newCategories };
61
+ handleSelectChange(update);
62
+ }}
63
+ onClear={() => handleSelectClear('categories')}
64
+ possibleFilters={possibleCategories}
65
+ customizeContent={(values) =>
66
+ formatMessage(
67
+ {
68
+ id: 'admin.pages.MarketPlacePage.filters.categoriesSelected',
69
+ defaultMessage:
70
+ '{count, plural, =0 {No categories} one {# category} other {# categories}} selected',
71
+ },
72
+ { count: values.length }
73
+ )
74
+ }
75
+ name="categories"
76
+ />
77
+ </Box>
78
+ )}
79
+ </Stack>
80
+ </FocusTrap>
81
+ </Popover>
82
+ );
83
+ };
84
+
85
+ FiltersPopover.propTypes = {
86
+ onToggle: PropTypes.func.isRequired,
87
+ source: PropTypes.shape({ current: PropTypes.instanceOf(Element) }).isRequired,
88
+ query: PropTypes.object.isRequired,
89
+ npmPackageType: PropTypes.oneOf(['plugin', 'provider']).isRequired,
90
+ possibleCollections: PropTypes.object.isRequired,
91
+ possibleCategories: PropTypes.object.isRequired,
92
+ handleSelectChange: PropTypes.func.isRequired,
93
+ handleSelectClear: PropTypes.func.isRequired,
94
+ };
95
+
96
+ export default FiltersPopover;
@@ -0,0 +1,107 @@
1
+ import React, { useState, useRef } from 'react';
2
+ import styled from 'styled-components';
3
+ import PropTypes from 'prop-types';
4
+ import { useIntl } from 'react-intl';
5
+ import { Box } from '@strapi/design-system/Box';
6
+ import { Button } from '@strapi/design-system/Button';
7
+ import { Tag } from '@strapi/design-system/Tag';
8
+ import Cross from '@strapi/icons/Cross';
9
+ import Filter from '@strapi/icons/Filter';
10
+ import FiltersPopover from './FiltersPopover';
11
+
12
+ const FilterTag = ({ name, handleRemove }) => {
13
+ return (
14
+ <Box padding={1}>
15
+ <Tag icon={<Cross />} onClick={handleRemove}>
16
+ {name}
17
+ </Tag>
18
+ </Box>
19
+ );
20
+ };
21
+
22
+ const ButtonToggle = styled(Button)`
23
+ height: ${({ theme }) => theme.sizes.input.S};
24
+ `;
25
+
26
+ const NpmPackagesFilters = ({
27
+ possibleCollections,
28
+ possibleCategories,
29
+ npmPackageType,
30
+ query,
31
+ handleSelectClear,
32
+ handleSelectChange,
33
+ }) => {
34
+ const [isVisible, setIsVisible] = useState(false);
35
+ const buttonRef = useRef();
36
+ const { formatMessage } = useIntl();
37
+
38
+ const handleToggle = () => setIsVisible((prev) => !prev);
39
+
40
+ const handleTagRemove = (tagToRemove, filterType) => {
41
+ const update = {
42
+ [filterType]: query[filterType].filter((previousTag) => previousTag !== tagToRemove),
43
+ };
44
+
45
+ handleSelectChange(update);
46
+ };
47
+
48
+ return (
49
+ <>
50
+ <Box paddingTop={1} paddingBottom={1}>
51
+ <ButtonToggle
52
+ variant="tertiary"
53
+ ref={buttonRef}
54
+ data-testid="filters-button"
55
+ startIcon={<Filter />}
56
+ onClick={handleToggle}
57
+ size="S"
58
+ >
59
+ {formatMessage({ id: 'app.utils.filters', defaultMessage: 'Filters' })}
60
+ </ButtonToggle>
61
+ {isVisible && (
62
+ <FiltersPopover
63
+ onToggle={handleToggle}
64
+ source={buttonRef}
65
+ query={query}
66
+ handleSelectClear={handleSelectClear}
67
+ handleSelectChange={handleSelectChange}
68
+ possibleCollections={possibleCollections}
69
+ possibleCategories={possibleCategories}
70
+ npmPackageType={npmPackageType}
71
+ />
72
+ )}
73
+ </Box>
74
+ {query.collections?.map((collection) => (
75
+ <FilterTag
76
+ name={collection}
77
+ key={collection}
78
+ handleRemove={() => handleTagRemove(collection, 'collections')}
79
+ />
80
+ ))}
81
+ {npmPackageType === 'plugin' &&
82
+ query.categories?.map((category) => (
83
+ <FilterTag
84
+ name={category}
85
+ key={category}
86
+ handleRemove={() => handleTagRemove(category, 'categories')}
87
+ />
88
+ ))}
89
+ </>
90
+ );
91
+ };
92
+
93
+ FilterTag.propTypes = {
94
+ name: PropTypes.string.isRequired,
95
+ handleRemove: PropTypes.func.isRequired,
96
+ };
97
+
98
+ NpmPackagesFilters.propTypes = {
99
+ npmPackageType: PropTypes.oneOf(['plugin', 'provider']).isRequired,
100
+ possibleCollections: PropTypes.object.isRequired,
101
+ possibleCategories: PropTypes.object.isRequired,
102
+ query: PropTypes.object.isRequired,
103
+ handleSelectChange: PropTypes.func.isRequired,
104
+ handleSelectClear: PropTypes.func.isRequired,
105
+ };
106
+
107
+ export default NpmPackagesFilters;
@@ -9,6 +9,7 @@ const NpmPackagesGrid = ({
9
9
  useYarn,
10
10
  isInDevelopmentMode,
11
11
  npmPackageType,
12
+ strapiAppVersion,
12
13
  }) => {
13
14
  // Check if an individual package is in the dependencies
14
15
  const isAlreadyInstalled = useCallback(
@@ -26,6 +27,7 @@ const NpmPackagesGrid = ({
26
27
  useYarn={useYarn}
27
28
  isInDevelopmentMode={isInDevelopmentMode}
28
29
  npmPackageType={npmPackageType}
30
+ strapiAppVersion={strapiAppVersion}
29
31
  />
30
32
  </GridItem>
31
33
  ))}
@@ -35,6 +37,7 @@ const NpmPackagesGrid = ({
35
37
 
36
38
  NpmPackagesGrid.defaultProps = {
37
39
  installedPackageNames: [],
40
+ strapiAppVersion: null,
38
41
  };
39
42
 
40
43
  NpmPackagesGrid.propTypes = {
@@ -43,6 +46,7 @@ NpmPackagesGrid.propTypes = {
43
46
  useYarn: PropTypes.bool.isRequired,
44
47
  isInDevelopmentMode: PropTypes.bool.isRequired,
45
48
  npmPackageType: PropTypes.string.isRequired,
49
+ strapiAppVersion: PropTypes.string,
46
50
  };
47
51
 
48
52
  export default NpmPackagesGrid;