@strapi/admin 4.5.0-beta.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 (233) 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/index.js +6 -2
  11. package/admin/src/content-manager/components/EditViewDataManagerProvider/index.js +103 -60
  12. package/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js +169 -162
  13. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/cleanData.js +70 -16
  14. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/findLeafByPathAndReplace.js +52 -0
  15. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/index.js +2 -0
  16. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/recursivelyFindPathsBasedOnCondition.js +72 -0
  17. package/admin/src/content-manager/components/FieldComponent/index.js +9 -2
  18. package/admin/src/content-manager/components/PreviewWysiwyg/index.js +1 -1
  19. package/admin/src/content-manager/components/RelationInput/RelationInput.js +80 -76
  20. package/admin/src/content-manager/components/RelationInputDataManager/RelationInputDataManager.js +95 -63
  21. package/admin/src/content-manager/components/RelationInputDataManager/utils/diffRelations.js +24 -0
  22. package/admin/src/content-manager/components/RelationInputDataManager/utils/index.js +2 -1
  23. package/admin/src/content-manager/components/RelationInputDataManager/utils/normalizeRelations.js +8 -29
  24. package/admin/src/content-manager/components/RelationInputDataManager/utils/normalizeSearchResults.js +8 -4
  25. package/admin/src/content-manager/components/RelationInputDataManager/utils/select.js +1 -0
  26. package/admin/src/content-manager/components/RepeatableComponent/index.js +4 -3
  27. package/admin/src/content-manager/hooks/__test__/usePrev.test.js +26 -0
  28. package/admin/src/content-manager/hooks/index.js +1 -0
  29. package/admin/src/content-manager/hooks/useFetchContentTypeLayout/utils/formatLayouts.js +19 -48
  30. package/admin/src/content-manager/hooks/usePrev.js +14 -0
  31. package/admin/src/content-manager/hooks/useRelation/useRelation.js +100 -7
  32. package/admin/src/content-manager/pages/App/reducer.js +3 -0
  33. package/admin/src/content-manager/pages/ListSettingsView/components/DraggableCard.js +3 -3
  34. package/admin/src/content-manager/pages/ListSettingsView/components/Settings.js +2 -2
  35. package/admin/src/content-manager/pages/ListSettingsView/components/SortDisplayedFields.js +1 -1
  36. package/admin/src/core/apis/CustomFields.js +0 -1
  37. package/admin/src/core/store/configureStore.js +17 -2
  38. package/admin/src/favicon.png +0 -0
  39. package/admin/src/hooks/useFetchMarketplacePlugins/index.js +2 -2
  40. package/admin/src/hooks/useFetchMarketplacePlugins/utils/api.js +4 -2
  41. package/admin/src/hooks/useFetchMarketplaceProviders/index.js +3 -3
  42. package/admin/src/hooks/useFetchMarketplaceProviders/utils/api.js +5 -3
  43. package/admin/src/index.js +1 -0
  44. package/admin/src/pages/App/index.js +1 -1
  45. package/admin/src/pages/HomePage/assets/corner-ornament.svg +48 -0
  46. package/admin/src/pages/HomePage/index.js +3 -2
  47. package/admin/src/pages/MarketplacePage/components/NpmPackageCard/CardButton.js +110 -0
  48. package/admin/src/pages/MarketplacePage/components/NpmPackageCard/InstallPluginButton.js +32 -21
  49. package/admin/src/pages/MarketplacePage/components/NpmPackageCard/PackageStats.js +79 -0
  50. package/admin/src/pages/MarketplacePage/components/NpmPackageCard/index.js +28 -11
  51. package/admin/src/pages/MarketplacePage/components/NpmPackagesFilters/FilterSelect.js +41 -0
  52. package/admin/src/pages/MarketplacePage/components/NpmPackagesFilters/FiltersPopover.js +96 -0
  53. package/admin/src/pages/MarketplacePage/components/NpmPackagesFilters/index.js +106 -0
  54. package/admin/src/pages/MarketplacePage/components/NpmPackagesGrid/index.js +4 -0
  55. package/admin/src/pages/MarketplacePage/components/SortSelect/index.js +70 -0
  56. package/admin/src/pages/MarketplacePage/index.js +68 -8
  57. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormApiTokenContainer/index.js +5 -4
  58. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormBody/index.js +4 -3
  59. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormHead/index.js +6 -2
  60. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/Regenerate/index.js +1 -1
  61. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js +5 -4
  62. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/schema.js +1 -1
  63. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/index.js +7 -38
  64. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/utils/options.js +31 -0
  65. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/index.js +32 -43
  66. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/Collapse/index.js +1 -1
  67. package/admin/src/pages/SettingsPage/pages/Roles/ListPage/components/RoleRow/index.js +3 -1
  68. package/admin/src/pages/SettingsPage/pages/Roles/ListPage/index.js +2 -1
  69. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/EventInput/index.js +2 -2
  70. package/admin/src/translations/ca.json +1 -1
  71. package/admin/src/translations/de.json +1 -1
  72. package/admin/src/translations/dk.json +1 -1
  73. package/admin/src/translations/en.json +21 -7
  74. package/admin/src/translations/es.json +1 -1
  75. package/admin/src/translations/fr.json +1 -1
  76. package/admin/src/translations/gu.json +1 -1
  77. package/admin/src/translations/he.json +1 -1
  78. package/admin/src/translations/hi.json +1 -1
  79. package/admin/src/translations/hu.json +1 -1
  80. package/admin/src/translations/id.json +1 -1
  81. package/admin/src/translations/it.json +1 -1
  82. package/admin/src/translations/ja.json +1 -1
  83. package/admin/src/translations/ko.json +1 -1
  84. package/admin/src/translations/ml.json +1 -1
  85. package/admin/src/translations/nl.json +1 -1
  86. package/admin/src/translations/no.json +1 -1
  87. package/admin/src/translations/pl.json +1 -1
  88. package/admin/src/translations/pt-BR.json +1 -1
  89. package/admin/src/translations/ru.json +1 -1
  90. package/admin/src/translations/sa.json +1 -1
  91. package/admin/src/translations/sk.json +1 -1
  92. package/admin/src/translations/sv.json +1 -1
  93. package/admin/src/translations/th.json +1 -1
  94. package/admin/src/translations/zh-Hans.json +1 -1
  95. package/admin/src/translations/zh.json +1 -1
  96. package/build/1856.172d5fa0.chunk.js +174 -0
  97. package/build/2077.058590f4.chunk.js +206 -0
  98. package/build/2912.2c42c07b.chunk.js +259 -0
  99. package/build/4318.5e670740.chunk.js +30 -0
  100. package/build/4715.22747b59.chunk.js +387 -0
  101. package/build/{4800.d09f1225.chunk.js → 4800.a6935af6.chunk.js} +1 -1
  102. package/build/4982.1b75ddb1.chunk.js +325 -0
  103. package/build/617f9c948fa79e6d73bd.png +0 -0
  104. package/build/6d21938306785f176538.png +0 -0
  105. package/build/70674f63fc3904c20de0.svg +7 -0
  106. package/build/7692.a36fb2c2.chunk.js +470 -0
  107. package/build/7841.c50e9509.chunk.js +259 -0
  108. package/build/7866.ba215f99.chunk.js +505 -0
  109. package/build/7e9af4fb7e723fcebf1f.svg +48 -0
  110. package/build/8380.e53e7207.chunk.js +299 -0
  111. package/build/8549.832ed79d.chunk.js +159 -0
  112. package/build/8738.0fe8a61e.chunk.js +463 -0
  113. package/build/{9066.26faf397.chunk.js → 9066.eaf76ff3.chunk.js} +4 -4
  114. package/build/{9166.8fcb3019.chunk.js → 9166.90876521.chunk.js} +14 -13
  115. package/build/{9420.0fe11290.chunk.js → 9420.5292d1d2.chunk.js} +38 -37
  116. package/build/9649.468667d9.chunk.js +199 -0
  117. package/build/9d5d788027e86620c234.svg +5 -0
  118. package/build/Admin-authenticatedApp.c4f68103.chunk.js +80 -0
  119. package/build/{Admin_homePage.4b2be829.chunk.js → Admin_homePage.26d32e30.chunk.js} +5 -4
  120. package/build/Admin_marketplace.32375885.chunk.js +22 -0
  121. package/build/Admin_settingsPage.bf2234e1.chunk.js +178 -0
  122. package/build/admin-app.9049056c.chunk.js +112 -0
  123. package/build/{admin-edit-roles-page.4dd6bcb9.chunk.js → admin-edit-roles-page.69d9fcb2.chunk.js} +1 -1
  124. package/build/ca-json.07ae0f2c.chunk.js +1 -0
  125. package/build/content-manager.ff998bed.chunk.js +1204 -0
  126. package/build/content-type-builder-translation-sv-json.6deff030.chunk.js +1 -0
  127. package/build/{content-type-builder.a6e29716.chunk.js → content-type-builder.16af63a6.chunk.js} +13 -13
  128. package/build/de-json.6b3e1894.chunk.js +1 -0
  129. package/build/dk-json.144c6a8e.chunk.js +1 -0
  130. package/build/{email-settings-page.bfe6227f.chunk.js → email-settings-page.c3469093.chunk.js} +5 -5
  131. package/build/en-json.4a269f6b.chunk.js +1 -0
  132. package/build/es-json.6d123a82.chunk.js +1 -0
  133. package/build/fr-json.28ab54cb.chunk.js +1 -0
  134. package/build/gu-json.9a50ea64.chunk.js +1 -0
  135. package/build/he-json.72f18790.chunk.js +1 -0
  136. package/build/hi-json.0301b7ba.chunk.js +1 -0
  137. package/build/hu-json.c4b641bb.chunk.js +1 -0
  138. package/build/{i18n-settings-page.18166125.chunk.js → i18n-settings-page.46d894ff.chunk.js} +4 -4
  139. package/build/id-json.86035797.chunk.js +1 -0
  140. package/build/index.html +1 -1
  141. package/build/it-json.bbdc8993.chunk.js +1 -0
  142. package/build/ja-json.1c9eeeec.chunk.js +1 -0
  143. package/build/ko-json.e1f66398.chunk.js +1 -0
  144. package/build/main.91328e7a.js +9381 -0
  145. package/build/ml-json.963c889f.chunk.js +1 -0
  146. package/build/nl-json.2b8cc3a0.chunk.js +1 -0
  147. package/build/no-json.a58c28bd.chunk.js +1 -0
  148. package/build/pl-json.249626b3.chunk.js +1 -0
  149. package/build/pt-BR-json.7852f808.chunk.js +1 -0
  150. package/build/ru-json.d7cfc2ff.chunk.js +1 -0
  151. package/build/runtime~main.c9c319c0.js +2 -0
  152. package/build/sa-json.44e95991.chunk.js +1 -0
  153. package/build/sk-json.7ba4b330.chunk.js +1 -0
  154. package/build/sv-json.8e5a7911.chunk.js +1 -0
  155. package/build/th-json.a67309b1.chunk.js +1 -0
  156. package/build/{upload-settings.3d613216.chunk.js → upload-settings.53b690f3.chunk.js} +4 -4
  157. package/build/{users-advanced-settings-page.f4051d92.chunk.js → users-advanced-settings-page.3f4ee86e.chunk.js} +4 -4
  158. package/build/{webhook-edit-page.9e46fc3f.chunk.js → webhook-edit-page.dc9442ce.chunk.js} +1 -1
  159. package/build/webhook-list-page.02191138.chunk.js +134 -0
  160. package/build/zh-Hans-json.21617c24.chunk.js +1 -0
  161. package/build/zh-json.608aaf24.chunk.js +1 -0
  162. package/ee/admin/pages/SettingsPage/pages/Roles/ListPage/index.js +3 -2
  163. package/env.js +1 -0
  164. package/package.json +12 -11
  165. package/scripts/build.js +11 -0
  166. package/utils/create-plugins-exclude-path.js +40 -0
  167. package/webpack.alias.js +0 -13
  168. package/webpack.config.js +4 -1
  169. package/admin/src/assets/images/banner_strapi-rocket.png +0 -0
  170. package/admin/src/assets/images/big-logo-home.png +0 -0
  171. package/admin/src/assets/images/homepage-logo.png +0 -0
  172. package/admin/src/assets/images/icon_made-by-strapi.svg +0 -5
  173. package/admin/src/assets/images/logo_strapi_auth.png +0 -0
  174. package/admin/src/assets/images/logo_strapi_auth_v4.png +0 -0
  175. package/admin/src/assets/images/logo_strapi_menu.png +0 -0
  176. package/admin/src/assets/images/oops.png +0 -0
  177. package/admin/src/content-manager/components/State/index.js +0 -37
  178. package/admin/src/favicon.ico +0 -0
  179. package/build/15026a3d58aeb2828134.png +0 -0
  180. package/build/1856.d8f13391.chunk.js +0 -173
  181. package/build/1939.e3c87653.chunk.js +0 -325
  182. package/build/2077.31a2d91e.chunk.js +0 -205
  183. package/build/2912.ab68a736.chunk.js +0 -258
  184. package/build/4318.7d167b58.chunk.js +0 -30
  185. package/build/4715.44b1ef9b.chunk.js +0 -386
  186. package/build/4982.c2a311b7.chunk.js +0 -324
  187. package/build/7841.4b67af3f.chunk.js +0 -258
  188. package/build/7866.5fbeb7e5.chunk.js +0 -504
  189. package/build/8380.9b53a31d.chunk.js +0 -284
  190. package/build/8549.cf10b5d1.chunk.js +0 -158
  191. package/build/8738.a30a2160.chunk.js +0 -461
  192. package/build/90f49a385afb000fb1d4.svg +0 -5
  193. package/build/962.8651ba3f.chunk.js +0 -184
  194. package/build/Admin-authenticatedApp.883449a5.chunk.js +0 -80
  195. package/build/Admin_marketplace.82c0570b.chunk.js +0 -11
  196. package/build/Admin_settingsPage.98e2a62b.chunk.js +0 -178
  197. package/build/a6b842e0b6d2b61135d1.svg +0 -5
  198. package/build/admin-app.a61d5c2e.chunk.js +0 -112
  199. package/build/b997a22a2e0b87ef1fa2.ico +0 -0
  200. package/build/bd81ba6c07827282255d.png +0 -0
  201. package/build/c3de6118ef47086ad05c.png +0 -0
  202. package/build/ca-json.82df6eab.chunk.js +0 -1
  203. package/build/content-manager.933dc286.chunk.js +0 -1201
  204. package/build/de-json.0ad554eb.chunk.js +0 -1
  205. package/build/dk-json.e195ea1a.chunk.js +0 -1
  206. package/build/en-json.1889403c.chunk.js +0 -1
  207. package/build/es-json.09f80f6e.chunk.js +0 -1
  208. package/build/fb376b132d18bf4522ca.png +0 -0
  209. package/build/fde9b1ad0670d29a2516.png +0 -0
  210. package/build/fr-json.606d056b.chunk.js +0 -1
  211. package/build/gu-json.9881264f.chunk.js +0 -1
  212. package/build/he-json.3b825d80.chunk.js +0 -1
  213. package/build/hi-json.83dcf48f.chunk.js +0 -1
  214. package/build/hu-json.6f328bce.chunk.js +0 -1
  215. package/build/id-json.1f3c4303.chunk.js +0 -1
  216. package/build/it-json.494ac432.chunk.js +0 -1
  217. package/build/ja-json.6f262117.chunk.js +0 -1
  218. package/build/ko-json.36dc3b9a.chunk.js +0 -1
  219. package/build/main.63e7ea0a.js +0 -9338
  220. package/build/ml-json.9566bf9a.chunk.js +0 -1
  221. package/build/nl-json.94c3a289.chunk.js +0 -1
  222. package/build/no-json.40386397.chunk.js +0 -1
  223. package/build/pl-json.ccc6ef23.chunk.js +0 -1
  224. package/build/pt-BR-json.744f024d.chunk.js +0 -1
  225. package/build/ru-json.d22ea13c.chunk.js +0 -1
  226. package/build/runtime~main.3a5e1b07.js +0 -2
  227. package/build/sa-json.8fb1c04d.chunk.js +0 -1
  228. package/build/sk-json.6c7335d4.chunk.js +0 -1
  229. package/build/sv-json.2e589a7d.chunk.js +0 -1
  230. package/build/th-json.72e8de3d.chunk.js +0 -1
  231. package/build/webhook-list-page.a712ae40.chunk.js +0 -134
  232. package/build/zh-Hans-json.a4d7dc69.chunk.js +0 -1
  233. 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,6 +25,7 @@ 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
  }
@@ -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,41 @@
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
+ aria-label={message}
13
+ placeholder={message}
14
+ size="M"
15
+ onChange={onChange}
16
+ onClear={onClear}
17
+ value={value}
18
+ customizeContent={customizeContent}
19
+ multi
20
+ >
21
+ {Object.entries(possibleFilters).map(([filterName, count]) => {
22
+ return (
23
+ <Option key={filterName} value={filterName}>
24
+ {computeFilterMessage(filterName, count)}
25
+ </Option>
26
+ );
27
+ })}
28
+ </Select>
29
+ );
30
+ };
31
+
32
+ FilterSelect.propTypes = {
33
+ message: PropTypes.string.isRequired,
34
+ value: PropTypes.array.isRequired,
35
+ onChange: PropTypes.func.isRequired,
36
+ possibleFilters: PropTypes.object.isRequired,
37
+ onClear: PropTypes.func.isRequired,
38
+ customizeContent: PropTypes.func.isRequired,
39
+ };
40
+
41
+ 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,106 @@
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
+ startIcon={<Filter />}
55
+ onClick={handleToggle}
56
+ size="S"
57
+ >
58
+ {formatMessage({ id: 'app.utils.filters', defaultMessage: 'Filters' })}
59
+ </ButtonToggle>
60
+ {isVisible && (
61
+ <FiltersPopover
62
+ onToggle={handleToggle}
63
+ source={buttonRef}
64
+ query={query}
65
+ handleSelectClear={handleSelectClear}
66
+ handleSelectChange={handleSelectChange}
67
+ possibleCollections={possibleCollections}
68
+ possibleCategories={possibleCategories}
69
+ npmPackageType={npmPackageType}
70
+ />
71
+ )}
72
+ </Box>
73
+ {query.collections?.map((collection) => (
74
+ <FilterTag
75
+ name={collection}
76
+ key={collection}
77
+ handleRemove={() => handleTagRemove(collection, 'collections')}
78
+ />
79
+ ))}
80
+ {npmPackageType === 'plugin' &&
81
+ query.categories?.map((category) => (
82
+ <FilterTag
83
+ name={category}
84
+ key={category}
85
+ handleRemove={() => handleTagRemove(category, 'categories')}
86
+ />
87
+ ))}
88
+ </>
89
+ );
90
+ };
91
+
92
+ FilterTag.propTypes = {
93
+ name: PropTypes.string.isRequired,
94
+ handleRemove: PropTypes.func.isRequired,
95
+ };
96
+
97
+ NpmPackagesFilters.propTypes = {
98
+ npmPackageType: PropTypes.oneOf(['plugin', 'provider']).isRequired,
99
+ possibleCollections: PropTypes.object.isRequired,
100
+ possibleCategories: PropTypes.object.isRequired,
101
+ query: PropTypes.object.isRequired,
102
+ handleSelectChange: PropTypes.func.isRequired,
103
+ handleSelectClear: PropTypes.func.isRequired,
104
+ };
105
+
106
+ 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;